Rate limits
1800 requests per 30 minutes and 5 bad requests per 60 seconds, per identity.
The limits
| What | Limit | Window |
|---|---|---|
| Requests | 1800 | 30 minutes |
| Bad requests | 5 | 60 seconds |
A "bad request" is any response with status code 400 through
500. 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 counter runs per identity, not per IP address and not
per company. An identity is the pair user|company from your
Authorization header. Two different users inside the same company each
have their own budget. One service user calling from multiple servers
shares one budget.
For OAuth flows each user counts separately, keyed on the sub
in the access token.
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 the
Retry-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 res Stay 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 & retries for the full backoff pattern and the retry wrapper.
- Authentication to avoid
401storms.