Rate limits
Every API key is rate-limited across four sliding windows in parallel: per minute, per hour, per day, and per month. Each window has its own counter, all four are checked on every request, and every response carries the live state so you can throttle proactively.
Limits scale with your plan. Custom-tier customers can request bespoke caps; talk to your CSM.
Limits by plan
Your active plan picks the bucket. If your subscription drops below Professional, existing keys are deactivated until you upgrade — they aren't deleted, so a re-upgrade re-enables them instantly with their full audit history intact.
| Plan | Per minute | Per hour | Per day | Per month |
|---|---|---|---|---|
| Professional | 60 | 1,000 | 10,000 | 100,000 |
| Global | 120 | 2,000 | 20,000 | 250,000 |
| Enterprise | 300 | 5,000 | 50,000 | 1,000,000 |
| AOR | 30 | 500 | 5,000 | 50,000 |
Counter scope
Counters are per API key, not per organization. An org with three keys has three independent buckets — that's deliberate, so a misbehaving integration on one key can't starve the others. Issue one key per integration to keep noisy neighbours isolated.
All four windows tick simultaneously. Hitting any of them returns 429; the response tells you which one tripped.
Response headers
Every successful response carries the four limits and remaining counts. Rejected responses also carry a Retry-After in seconds.
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit-Minute: 120
X-RateLimit-Remaining-Minute: 117
X-RateLimit-Limit-Hour: 2000
X-RateLimit-Remaining-Hour: 1812
X-RateLimit-Limit-Day: 20000
X-RateLimit-Remaining-Day: 19312
X-RateLimit-Limit-Month: 250000
X-RateLimit-Remaining-Month: 240881HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
X-RateLimit-Limit-Minute: 120
X-RateLimit-Remaining-Minute: 0
{
"error": "rate_limit_exceeded",
"window": "minute",
"limit": 120,
"retryAfter": 60
}Sliding window semantics
We use sliding windows, not fixed-time windows. That means the “per minute” budget refreshes continuously as requests roll out of the trailing 60 seconds, instead of resetting at the top of every minute. The practical effect: you can't bunch 60 requests at :59 and another 60 at :00; you'd trip the limit.
X-RateLimit-Remaining-* on every response and back off when the smallest remaining value gets near zero. The cap that matters is the smallest non-zero one.Backoff strategy
On 429, sleep for Retry-Afterseconds with a small jitter (10–30% of the value). Don't retry instantly; you'll just renew the trip and spend another second of the same budget on a failed call.
async function callWithBackoff(url, init = {}, attempt = 0) {
const res = await fetch(url, init);
if (res.status !== 429 || attempt >= 5) return res;
const wait = Number(res.headers.get("Retry-After") ?? 30);
const jitter = wait * (0.1 + Math.random() * 0.2);
await new Promise((r) => setTimeout(r, (wait + jitter) * 1000));
return callWithBackoff(url, init, attempt + 1);
}Bulk imports
For one-off backfills, schedule the job to consume no more than 50% of the per-minute budget so other systems on the same key keep working. A simple token-bucket on the client side gets you most of the way there.
const TARGET_RPS = 1; // 60/min cap split in half
let lastCall = 0;
async function throttled(fn) {
const wait = Math.max(0, lastCall + 1000 / TARGET_RPS - Date.now());
if (wait > 0) await new Promise((r) => setTimeout(r, wait));
lastCall = Date.now();
return fn();
}Live monitoring
The detail drawer for each key in your workspace shows the four windows live with progress bars. You can see what the server sees without instrumenting the client. Useful when diagnosing “why are we suddenly hitting 429?” on a Monday morning.
Custom limits
Enterprise plans can request custom per-window caps as part of the contract. Talk to your CSM. We don't raise limits on a per-key basis outside of Enterprise; the right answer for everyone else is to add another key for a different workload.
Available onEnterpriseNext
- Idempotency for safe retries that don't double-create.
- Errors for the full set of status codes and reason strings.