Throttling and rate-limit headers
Which routes have a limit of their own, which rate limit is attached to a key, what the four headers mean, and how a client reacts to 429.
4 minute readAPIUpdated: 10/25/2026
On this page
Before you start
- For
- Anyone who builds a sign-in flow or has to interpret a
429. - Prerequisite
- none
Two things are throttled, for two reasons: The abuse-prone routes (sign-in, sign-in link, registration, password reset, and the round trips of provider sign-in) each carry a limit of their own, and every call with an API key also counts against a rate limit that belongs to the key. This page lists the headers, both limits, and the right reaction to 429.
The headers
Every response to which a limit applies carries the state of the window:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
requests allowed in the window in total |
X-RateLimit-Remaining |
of those, how many are still open |
X-RateLimit-Reset |
the point in time at which the window starts anew, in Unix seconds |
Retry-After |
seconds until the next permitted attempt, only when the limit is exceeded (429) |
All four are readable for browser clients on other origins, as are X-Trace-Id, X-Correlation-Id, and Server-Timing.
The response when a limit is exceeded
{ "code": "RateLimit.Exceeded", "message": "Too many requests", "traceId": "trc_…" }
The status is 429, with Retry-After. A second code of the same area, RateLimit.GlobalCircuitBreaker (also 429), indicates a platform-wide overload safeguard. The reaction is the same.
The limits of the sign-in and registration routes
The limits apply per window and per counting key: “IP” counts per sender address, and “IP+email” counts per sender address and email address in the body.
| Route | Limit |
|---|---|
POST /v1/auth/identify |
10 per minute (IP+email) and 40 per 10 minutes (IP) |
POST /v1/auth/login |
5 per minute (IP+email) |
POST /v1/auth/login/choose-organization |
10 per minute (IP) |
POST /v1/auth/mfa/verify, POST /v1/auth/mfa/recovery-code |
5 per minute (IP) |
POST /v1/auth/login/magic-link |
5 per minute (IP+email) |
POST /v1/auth/login/magic-link/consume |
5 per minute (IP) |
POST /v1/auth/password-reset/request |
5 per minute (IP+email) |
POST /v1/auth/password-reset/consume |
5 per minute (IP) |
POST /v1/auth/onboarding/consume |
5 per minute (IP) |
GET /v1/auth/login/sso/{providerId} |
20 per minute (IP) |
GET|POST /v1/auth/login/sso/callback |
30 per minute (IP) |
GET /v1/auth/login/social |
60 per minute (IP) |
GET /v1/auth/login/social/{provider} |
20 per minute (IP) |
GET|POST /v1/auth/login/social/callback |
30 per minute (IP) |
POST /v1/auth/login/exchange |
10 per minute (IP) |
POST /v1/auth/sso-recovery/consume |
5 per minute (IP) |
GET /v1/auth/signup |
60 per minute (IP) |
POST /v1/auth/signup/start |
3 per 10 minutes (IP+email) and 10 per 24 hours (IP) |
POST /v1/auth/signup/verify, POST /v1/auth/signup/social |
10 per minute (IP) |
POST /v1/auth/signup/complete, POST /v1/auth/signup/join |
5 per minute (IP) |
POST /v1/tenants/me/sso/{providerId}/domains/{domain}/verify |
10 per minute (IP) |
Further public and session-bound routes have windows of their own, for example invitation acceptance (GET|POST /v1/invitations/public/{token} and …/accept, each per IP), the analytics export (POST /v1/analytics/metrics/{metric}/export, per organization), and the VAT ID check (POST /v1/me/billing-profile/vat-id/verify, per organization). The description of the operation in the reference names the value where it names one, and otherwise the response headers show it.
The rate limit per key
A call with an ak_… key counts against a rate limit that belongs to the key, on every route, including the read routes without a limit of their own. The reason is the nature of an automated client: A person clicks, a script queries in a loop, and the cheap read routes are what knock a stack over fastest.
| What | Value |
|---|---|
| Default | a value of the environment in the form <count>/<window>, such as 600/1m. The window is given in s, m, or h |
| Per key | can be overridden at creation (field rateLimit in POST /v1/me/api-keys, where null means “the default of the environment”), and a rotation carries the value over |
| Counted | per key, not per sender address and not per organization |
| Headers | the same four as above, on every response |
| When exceeded | 429 RateLimit.Exceeded with Retry-After |
Calls from a signed-in session, devices, renderers, and operator keys are not affected: They have limits of their own. If several rules apply to a call, for example the limit of the sign-in route and the rate limit of the key, the headers show the tightest one, and the rule that was exceeded first determines the error code.
What a client does with this: Track X-RateLimit-Remaining and slow your own loop before zero is reached. An import that creates ten thousand media items finishes faster with a small pause per call than one that runs into the wall and waits.
The right reaction
| Rule | Why |
|---|---|
Wait for Retry-After, then retry exactly once |
Every attempt before that counts toward the window and extends the lockout. |
Watch X-RateLimit-Remaining and slow down before zero |
A client that knows the window never sees a 429. |
| Do not hammer | Retries every second fill the window instead of emptying it. |
When a call gets 429, the command line exits with code 8 (rate-limited), see JSON output and exit codes.