Skip to main content
Lettr enforces rate limits to protect infrastructure stability and ensure consistent performance for all users. If you are receiving 429 errors or hitting monthly quotas, this guide covers how to identify the issue and adjust your sending approach.

Rate Limit Overview

Lettr applies limits at the API request level, per authentication credential:
LimitThresholdWindow
Standard API rate limit300 requests5 minutes
High-volume API rate limit2,000 requests5 minutes
Authentication failures5 failures5 minutes (triggers 15-minute block)
Recipients per request50Per request (combined to, cc, bcc)
High-volume rate limits are available on Pro and Enterprise plans. Contact sales@lettr.com to discuss your volume needs.

Rate Limit Response

When you exceed the rate limit, the API returns an HTTP 429 status code with the following response body:
{
  "status": "error",
  "error_code": "rate_limit_exceeded",
  "message": "Too many requests. Please retry after 60 seconds.",
  "retry_after": 60
}
The retry_after field indicates how many seconds you should wait before making another request.
If you continue sending requests after receiving a 429 response, the cooldown period may be extended. Always respect the retry_after value before retrying.

Handling Rate Limits — Implementing Backoff

When your application receives a 429 response, it should pause and retry with exponential backoff. The following example reads the retry_after value from the response body and backs off accordingly:
async function sendWithBackoff(url, payload, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": "Bearer YOUR_API_KEY",
      },
      body: JSON.stringify(payload),
    });

    if (response.status === 429) {
      const body = await response.json();
      const retryAfter = body.retry_after || 60;
      const backoff = retryAfter * 1000 * Math.pow(2, attempt);

      console.log(`Rate limited. Retrying in ${backoff / 1000}s (attempt ${attempt + 1})`);
      await new Promise((resolve) => setTimeout(resolve, backoff));
      continue;
    }

    return await response.json();
  }

  throw new Error("Max retries exceeded");
}
# Send a request and check for 429 status
curl -s -w "\nHTTP Status: %{http_code}\n" \
  -X POST https://app.lettr.com/api/emails \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "you@example.com",
    "to": ["recipient@example.com"],
    "subject": "Hello",
    "html": "<p>Hi there</p>"
  }'
If the response status is 429, parse the JSON body and wait for the number of seconds specified in retry_after before retrying.

Efficient Sending

Each API request supports up to 50 recipients across the to, cc, and bcc fields combined. To minimize the number of API calls and stay within rate limits, always fill each request to capacity before sending the next.
function chunkRecipients(recipients, size = 50) {
  const chunks = [];
  for (let i = 0; i < recipients.length; i += size) {
    chunks.push(recipients.slice(i, i + size));
  }
  return chunks;
}

async function sendToAll(recipients, emailPayload) {
  const chunks = chunkRecipients(recipients);

  for (const chunk of chunks) {
    await sendWithBackoff("https://app.lettr.com/api/emails", {
      ...emailPayload,
      to: chunk,
    });

    // Pause between requests to avoid hitting rate limits
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
}
Sending to 10,000 recipients at 50 per request requires only 200 API calls. With a 1-second delay between each, that completes in under 4 minutes — well within the standard rate limit of 300 requests per 5 minutes.

Monthly Email Quotas

Each plan includes a monthly email quota. Every recipient on every request counts toward this quota.
PlanMonthly Quota
Free3,000 emails
Pro 50K50,000 emails
Pro 100K100,000 emails
Pro 200K200,000 emails
Pro 500K500,000 emails
Pro 1M1,000,000 emails
EnterpriseCustom
Emails beyond your tier limit are charged at $0.80 per 1,000 emails. Check your current plan details in the Lettr dashboard under Settings > Billing.

Monitoring Usage

You can track your current API usage and monthly email quota in the Lettr dashboard:
  1. Go to Dashboard > Usage to view your current month’s email volume.
  2. Review your remaining quota and historical usage trends.
  3. Set up alerts if your usage is approaching the plan limit.
Check your usage regularly, especially when launching new campaigns or onboarding large recipient lists. This helps you avoid unexpected quota exhaustion mid-send.

Queuing Strategies

For large sends, queue outbound requests and pace them with a controlled delay to stay within rate limits:
async function processQueue(queue, emailPayload, delayMs = 1000) {
  const chunks = chunkRecipients(queue, 50);
  let sent = 0;

  for (const chunk of chunks) {
    try {
      await sendWithBackoff("https://app.lettr.com/api/emails", {
        ...emailPayload,
        to: chunk,
      });
      sent += chunk.length;
      console.log(`Sent ${sent} / ${queue.length}`);
    } catch (error) {
      console.error(`Failed at ${sent} sent. Error: ${error.message}`);
      // Re-queue remaining recipients
      const remaining = queue.slice(sent);
      console.log(`Re-queuing ${remaining.length} recipients for retry.`);
      return processQueue(remaining, emailPayload, delayMs);
    }

    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }

  console.log(`All ${queue.length} recipients processed.`);
}
This approach uses a simple in-memory queue with setTimeout pacing. For production workloads, consider persisting the queue to a database so incomplete sends can be resumed after a restart.

Best Practices

Always handle 429 responses by reading the retry_after value and backing off exponentially. Never retry immediately — this worsens throttling and can extend the cooldown period.
Include up to 50 recipients per API request. Sending one recipient per request wastes your rate limit allowance and dramatically slows throughput.
Regularly check your email volume and quota in the Lettr dashboard. Catching usage spikes early prevents disruptions to scheduled campaigns.
Rather than polling the API repeatedly to check email status, configure webhooks to receive delivery events in real time. This eliminates unnecessary API calls and keeps you well under rate limits.
For sends to thousands of recipients, queue requests and pace them with a consistent delay. This avoids bursts that trigger rate limiting and gives you control over throughput.

Increasing Your Limits

If you are consistently hitting rate limits or approaching your monthly quota, you have two options:
  1. Upgrade your plan. Higher plans include larger monthly quotas and access to high-volume rate limits. Visit Settings > Billing in the dashboard to compare plans.
  2. Contact sales for Enterprise. If you need custom rate limits, dedicated infrastructure, or quotas beyond 500,000 emails per month, reach out to sales@lettr.com.
If you are experiencing rate limit issues that seem incorrect — for example, receiving 429 errors well below the documented thresholds — contact support@lettr.com with your API key prefix and timestamps of the affected requests.