Rate limits
Rate limits depend on your plan tier. Limits are enforced per API key and apply across the entire endpoint pool — not per endpoint.
Limits by plan
| Plan | Per-second | Per-minute | Per-day |
|---|---|---|---|
| Trial | 2 | 60 | 10,000 |
| Hobby | 5 | 200 | 50,000 |
| Production | 50 | 3,000 | 1,000,000 |
| Enterprise | Custom — quoted to your traffic shape | ||
WebSocket connections and message volume have separate limits — see the WebSocket overview.
Rate-limit headers
Every response includes:
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 184
X-RateLimit-Reset: 1700000000X-RateLimit-Reset is a Unix timestamp in seconds. Inspect it on every response and back off pre-emptively when Remaining approaches zero.
When you exceed a limit
You'll get a 429 response:
HTTP/1.1 429 Too Many Requests
Retry-After: 5
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded. Retry after 5 seconds.",
"retry_after_seconds": 5,
"request_id": "req_8f3a2b1c"
}
}Recommended retry pattern
Exponential backoff with jitter, respecting retry_after_seconds:
async function withRetry(fn, { maxAttempts = 5 } = {}) {
let attempt = 0;
while (true) {
try { return await fn(); }
catch (err) {
attempt++;
if (err.status !== 429 && err.status < 500) throw err;
if (attempt >= maxAttempts) throw err;
const serverHint = err.body?.error?.retry_after_seconds ?? 0;
const exp = Math.min(2 ** attempt, 60);
const jitter = Math.random() * 0.5 * exp;
const sleep = Math.max(serverHint, exp + jitter) * 1000;
await new Promise(r => setTimeout(r, sleep));
}
}
}Don't retry on
400, 401, 403, or 404 — these are client errors that won't fix themselves. Only retry on 429 and 5xx.