Rate limiting

Per-key request budgets, the X-RateLimit headers, Retry-After, and how to back off.

Updated 2026-06-10

Every API key has a per-minute request budget. Exceeding it returns 429 rate_limited with a Retry-After header telling you when the window resets.

Defaults

SettingDefaultRange
Instance-wide default60 req/minper-deploy
Per-key overridenoneany positive integer (the dashboard form suggests up to 10,000)

The per-key override (api_keys.rate_limit_per_minute) wins over the instance default. Set it on a key when minting if you know the integration needs more headroom — heavy-volume reporting jobs, bulk record imports, etc.

Response headers

Every authenticated response that passes API-key validation — success or error — carries the current bucket state:

HeaderMeaning
X-RateLimit-LimitThe effective limit for this key
X-RateLimit-RemainingRequests left in the current minute window
X-RateLimit-ResetUnix epoch seconds when the window resets

On a 429 you also get:

HeaderMeaning
Retry-AfterSeconds to wait before retrying (always ≥ 1)

How to behave

  • Watch X-RateLimit-Remaining. When it drops to 0, queue further requests until X-RateLimit-Reset.
  • On 429, sleep Retry-After seconds and retry. Don't busy-loop.
  • For sustained high volume, request a per-key override rather than spraying retries — sustained 429s show up in our observability and may trigger investigation.

Example: handle 429 with backoff

async function call(request) {
  for (let attempt = 1; attempt <= 3; attempt++) {
    const response = await fetch(request)
 
    // Anything other than 429 — success or a real error — is final.
    if (response.status !== 429) return response
 
    // 429 means "slow down". Retry-After says how many seconds to wait.
    const seconds = Number(response.headers.get('Retry-After') ?? 1)
    await new Promise((resolve) => setTimeout(resolve, seconds * 1000))
  }
  throw new Error('still rate-limited after 3 attempts')
}

Bursts vs. sustained

The bucket is per-minute. You can burst the full limit at the start of a window — 60 requests in the first second is fine. The next 59 seconds return 429s until the window rolls. If you need true bursting (1000 in 10 seconds, then quiet for an hour), ask for an override and pace your own sender.