Rate limits
1800 requests per 30 minutes and 5 bad requests per 60 seconds, per company.
The limits
| What | Limit | Window |
|---|---|---|
| Requests | 1800 | 30 minutes |
| Bad requests | 5 | 60 seconds |
A "bad request" is any response with status code 400 through500. Bad requests count in both meters: as a normal request and as a bad request. So fix auth and parameter shape before you start a loop.
What it counts per
The budget belongs to your company, not to an IP address and not to a single machine. Calling from three servers does not give you three budgets: they draw on the same one.
The numbers above are the standard budget. They are not a hard ceiling on what the platform can give you: we raise them per customer when an integration genuinely needs more, and several customers already run higher. If you are planning a bulk export or a nightly sync that will not fit,talk to us before you build around the limit.
Throttle response
Above the limit you get HTTP 429 Too Many Requests back, with a Retry-After header telling you how many seconds you're still blocked.
HTTP/1.1 429 Too Many Requests
Retry-After: 180
Content-Type: application/json
{ "error": "rate_limited" }How the throttle window grows
Once you hit the request limit, you're blocked for 3 minutes for each extra request you still try. That window grows until 30 minutes have passed since you hit the limit. In practice: stop trying, wait theRetry-After value, then resume at a calmer pace.
| Status | Description |
|---|---|
429 | Too Many Requests |
401 | Unauthorized (counts as a bad request) |
Respect Retry-After
async function call(url: string, headers: HeadersInit): Promise<Response> {
const res = await fetch(url, { headers });
if (res.status === 429) {
const seconds = parseInt(res.headers.get("retry-after") ?? "60", 10);
await new Promise(r => setTimeout(r, seconds * 1000));
return call(url, headers);
}
return res;
}import time, requests
def call(url, headers):
res = requests.get(url, headers=headers, timeout=10)
if res.status_code == 429:
seconds = int(res.headers.get("Retry-After", "60"))
time.sleep(seconds)
return call(url, headers)
return resStay under the limit
- Cache what you already have. Resource groups, workplaces, and day programs don't change often. Refresh on a schedule, not per user action.
- Ask broadly instead of narrowly. One call with a date range is cheaper than six calls per individual day.
- Avoid hot loops. A last-position poll every 5 seconds eats your budget in 2.5 hours. Vehicles update every 1 to 3 minutes typically, polling faster gains you nothing.
- Fix bad requests first. Five consecutive
401s lock you out for a minute. Test credentials in dev before you ship to prod. - Call server-side, not from the browser. One central proxy with caching shares the work, and your secret stays out of client bundles.
More
- Errors for the status codes and what each one means.
- Authentication to avoid
401storms.