Developers
Rate limits and quotas
The two limits on API traffic, the headers that tell you where you stand, and how to back off.
2 minute read
There are two separate limits, and they fail differently.
Per-minute rate limit
A short window, applied per key, sized by your plan. Bursting past it returns 429 and the limit resets within the minute. This is the one that protects the service from a runaway loop.
Daily quota
A total number of requests per workspace per day, also sized by your plan. Exhausting it returns 429 until midnight UTC. This is the one that reflects what you are paying for.
Reading the headers
Every API response carries where you stand, so you never have to guess:
x-ratelimit-limit: 50000
x-ratelimit-remaining: 49873
x-ratelimit-reset: 34211
x-ratelimit-reset is seconds until the window resets. On a 429 you also get retry-after in seconds. Respect it: retrying sooner does not work and counts against you.
Backing off properly
Retry on 429 and on 5xx, with exponential backoff and jitter, and give up after a few attempts:
async function call(path, init, attempt = 0) {
const res = await fetch(path, init)
if ((res.status === 429 || res.status >= 500) && attempt < 4) {
const after = Number(res.headers.get('retry-after') ?? 0)
const wait =
after > 0 ? after * 1000 : 2 ** attempt * 500 + Math.random() * 250
await new Promise((r) => setTimeout(r, wait))
return call(path, init, attempt + 1)
}
return res
}
Do not retry 4xx responses other than 429: a 400 or 403 will fail the same way every time.
Seeing your usage
Settings → API keys shows a fourteen-day chart of requests per key, which is the quickest way to find out which integration is doing the work. If you are consistently near the daily quota, the answer is usually a higher plan or fewer polling loops; consider webhooks instead of polling.
Did this miss something? Tell us.