Rate Limits
Request windows, monthly quotas and how to back off politely.
Per-minute window
| Parameter | Type | Description |
|---|---|---|
| Sandbox plan (free) | 120 req/min | Generous for evaluation |
| Paid plans | 600 req/min | Starter → Scale; custom on Enterprise |
| Sandbox environment (bsk_test_) | 300 req/min | All plans |
Every authenticated response carries:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1762790400Monthly request quotas
Beyond the per-minute window, plans include monthly request quotas (10k → 5M). Check GET /2/usage or the Usage page. Per-minute exhaustion returns 429 with Retry-After; monthly-quota exhaustion returns 429 rate_limit_exceeded with the reset described in the message (the period rolls over on the 1st).
Exponential backoff
async function call(url, opts, attempt = 0) {
const res = await fetch(url, opts);
if (res.status === 429 && attempt < 5) {
const wait = Number(res.headers.get("Retry-After")) || 2 ** attempt;
await new Promise((r) => setTimeout(r, wait * 1000));
return call(url, opts, attempt + 1);
}
return res;
}Webhooks beat polling
Subscribe to update.sent and you'll rarely need to poll at all — see Efficient API Usage.