Rate limits
Rate limits protect both you and us. We use three overlapping layers with sensible defaults; if you hit a limit we return 429 with headers telling you when to retry.
Layered limits
| Layer | Limit | When it triggers |
|---|---|---|
| Per API key (burst) | 100 req/s | Short spikes above 100/second |
| Per API key (sustained) | 1000 req/min | Sustained load. protects against runaway loops |
| Per IP (unauthenticated) | 20 req/min | Only applies before Bearer auth succeeds |
| Global (per IP block) | 5000 req/s | Volumetric abuse safety-net |
Response headers
Every request that goes through the rate-limiter returns these headers:
| Header | Meaning |
|---|---|
| RateLimit-Limit | Requests allowed in the current window |
| RateLimit-Remaining | Requests left in this window |
| RateLimit-Reset | Unix seconds until the window resets |
| Retry-After | Only on 429. seconds to wait before retrying |
Handling 429
When rate-limited we return 429 with a Retry-After header. Wait, then retry.
HTTP 429 Too Many Requests
Retry-After: 5
{
"error": {
"type": "rate_limit",
"code": "RATE_LIMITED",
"message": "Too many requests. Please slow down."
}
}Exponential backoff
For transient errors (429, 502, 503, 504), use exponential backoff with jitter. Don't retry immediately in a tight loop. you will lengthen the outage for everyone.
async function withBackoff(fn, maxAttempts = 5) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const res = await fn();
if (res.ok || (res.status !== 429 && res.status < 500)) return res;
if (attempt === maxAttempts) return res;
// exp backoff (1s, 2s, 4s, 8s, 16s) with 20% jitter
const retryAfter = Number(res.headers.get('retry-after')) || 2 ** (attempt - 1);
const jitter = retryAfter * 0.2 * Math.random();
await new Promise((r) => setTimeout(r, (retryAfter + jitter) * 1000));
}
}Need higher limits?
Enterprise plans get bumped defaults. Contact support with your projected throughput and use-case. we can lift caps for legitimate workloads within a business day.