Rate limits
API-key traffic is rate-limited per key with a sliding window. Dashboard sessions are not rate-limited.
The limit
Each key has a requests-per-minute cap — 60/min by default, configurable per key when you create it. The window is a continuous 60 seconds (not a fixed calendar minute), so the cap holds across every 60-second span.
RateLimit-* headers
Every response to an API-key request carries the current limiter state:
| Header | Meaning |
|---|---|
RateLimit-Limit | Requests allowed per minute for this key. |
RateLimit-Remaining | Requests remaining in the current window. |
RateLimit-Reset | Seconds until the window frees capacity. |
When you exceed it (429)
Over the limit, the API returns 429 Too Many Requests with a Retry-After header (seconds) — mirrored in the body — and the same RateLimit-* headers. No downstream work runs for a throttled request.
{ "error": "Rate limit exceeded", "retryAfter": 30, "limit": 60 }Recommended client backoff
On a 429, wait the number of seconds in Retry-After before retrying, and prefer exponential backoff with jitter for repeated failures. Proactively, you can slow down as RateLimit-Remaining approaches zero rather than waiting for a rejection.
async function withRetry(run: () => Promise<Response>): Promise<Response> {
for (let attempt = 0; ; attempt++) {
const res = await run();
if (res.status !== 429) return res;
const retryAfter = Number(res.headers.get("Retry-After") ?? 1);
const jitter = Math.random() * 0.3;
await new Promise((r) => setTimeout(r, (retryAfter + jitter) * 1000 * 2 ** attempt));
}
}See errors for the full status-code table.