Rate limits
Requests are counted per key, per minute. The budget comes from the workspace's plan.
| Plan | Requests / minute | API keys |
|---|---|---|
| Starter | 60 | 2 |
| Growth | 300 | 10 |
| Enterprise | 1000 | Unlimited |
| Custom | 1000 unless agreed otherwise | Unlimited |
Per key, not per workspace — so a misbehaving integration exhausts its own budget rather than starving the others. Splitting your integrations across separate keys is worth doing for that reason alone.
Reading the headers
Every response carries the current state, so you never have to guess:
| Header | Meaning |
|---|---|
RateLimit-Limit | Requests allowed in the window. |
RateLimit-Remaining | Requests left in it. |
RateLimit-Reset | Seconds until the window resets. |
Past the limit you get 429 with code: "rate_limit_exceeded" and a
Retry-After header in seconds. Honour it. Retrying immediately just burns
the next window too.
async function call(path, init = {}) {
const res = await fetch(`${BASE}${path}`, init);
if (res.status === 429) {
const wait = Number(res.headers.get('retry-after') ?? 60);
await sleep(wait * 1000);
return call(path, init);
}
// Slow down before you are refused, not after.
if (Number(res.headers.get('ratelimit-remaining') ?? 99) < 5) {
await sleep(1000);
}
return res;
}
Staying inside the budget
Page with limit=100. The maximum page size is the same one request as the
default of 25, and it does four times the work.
Sync incrementally. updated_since turns a nightly full refetch into a
handful of calls. See Pagination.
Subscribe instead of polling. A webhook tells you the moment something changes and costs you no requests at all. Polling every minute to catch a change that happens twice a day spends your entire budget learning nothing.
Do not parallelise a sync to go faster. Ten concurrent workers on a Starter key exhaust sixty requests in a few seconds and then all wait together. One sequential loop that respects the headers finishes sooner.
If the budget is genuinely too small
A per-key override can be set for a specific integration without changing the whole plan. If a legitimate workload does not fit — a first import of many years of attendance, say — talk to us rather than engineering around it; a one-off backfill is a different problem from a steady-state limit.