Skip to content

Guide

Limits at a glance

ScopeRate limitBurst
Stage-wide (every authenticated route)100 req/sec200
GET /v1/p/{userId} (open-tracking pixel)50 req/sec100

The pixel route is capped to half the stage budget on its own so a flood of recipient image-fetches can’t starve the dashboard’s authenticated routes. This is by design (ADR 026).

429 response

When you exceed the limit, API Gateway short-circuits with 429 TOO_MANY_REQUESTS:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 30
X-Correlation-Id: 00000000-0000-4000-8000-000000000005

{
  "error": {
    "code": "TOO_MANY_REQUESTS",
    "message": "Rate limit exceeded — retry after 30 seconds"
  }
}

The Retry-After header is in seconds. API Gateway sets a default of 30s if the limit was hit by burst exhaustion.

Always honor Retry-After. Don’t hard-code your own backoff if the server told you how long to wait.

async function callWithBackoff(url: string, init: RequestInit, attempt = 0): Promise<Response> {
  const res = await fetch(url, init);
  if (res.status !== 429 || attempt >= 5) return res;

  const retryAfter = Number(res.headers.get('retry-after') ?? Math.min(30, 2 ** attempt));
  // Add jitter to avoid thundering herd on the next bucket boundary.
  const jitter = Math.random() * 500;
  await new Promise((r) => setTimeout(r, retryAfter * 1000 + jitter));
  return callWithBackoff(url, init, attempt + 1);
}

Patterns to apply:

  • Exponential backoff with jitter — even when Retry-After is present, jitter the wakeup so concurrent clients don’t all retry at the same millisecond.
  • Cap retries. 5 attempts is plenty; if you’re still 429ing after 5, you’ve got a structural problem (queue-size mismatch, runaway loop).
  • Surface to the user. A 429 after 5 retries means your app is overloaded — back off the calling pattern, not just the HTTP retry.

Per-tenant limits (roadmap)

Today’s limits are stage-global — every customer shares the same 100 RPS budget. Two future levers:

  1. API key usage plans — already declared on the API Gateway side (esigkit-default-{stage} usage plan with 100k req/month quota); we haven’t wired per-customer keys to per-tenant quotas yet.
  2. Per-org throttling — bigger lift, requires per-tenant quota on API Gateway. Tracked in the master plan.

If your use case will hit these caps, contact support — we’ll lift the cap on a per-customer basis until the per-tenant flow ships.