Rate Limits
API requests are rate-limited per organisation per minute. The limit depends on the organisation’s active plan.
Limits by plan
| Plan | Requests per minute |
|---|---|
| Free | 60 |
| Starter | 300 |
| Professional | 1,000 |
| Enterprise | 5,000 |
All tokens belonging to the same organisation share one rate-limit bucket — a service account and a PAT from the same org count against the same limit.
Rate-limit headers
Every authenticated response includes these headers:
| Header | Example | Description |
|---|---|---|
X-RateLimit-Limit | 300 | Requests allowed per minute |
X-RateLimit-Remaining | 247 | Requests remaining in current window |
X-RateLimit-Reset | 1745427600 | Unix timestamp when the window resets |
Handling 429
When the limit is exceeded, the server returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 37
Content-Type: application/problem+json{
"type": "https://problems.eunifyer.com/rate_limit_exceeded",
"title": "Too Many Requests",
"status": 429,
"detail": "Rate limit exceeded",
"code": "rate_limit_exceeded",
"limit": 300,
"reset_in_seconds": 37
}Wait Retry-After seconds before retrying.
Retry with exponential backoff
Do not hammer the API after a 429. Use exponential backoff:
import time, httpx
def call_with_retry(client: httpx.Client, method: str, url: str, **kwargs) -> httpx.Response:
for attempt in range(5):
r = client.request(method, url, **kwargs)
if r.status_code == 429:
wait = int(r.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
continue
return r
raise RuntimeError("Exceeded retry budget")async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 5): Promise<Response> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const resp = await fetch(url, options);
if (resp.status === 429) {
const retryAfter = parseInt(resp.headers.get("Retry-After") ?? String(2 ** attempt));
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
continue;
}
return resp;
}
throw new Error("Exceeded retry budget");
}Best practices
- Monitor
X-RateLimit-Remaining— throttle your request rate proactively rather than waiting for 429s. - Use cursor pagination efficiently — fetching large datasets with
limit=100uses fewer API calls thanlimit=20. - Batch creates — use
Idempotency-Keyon create operations so you can safely retry without double-creating on transient errors. - Use delta sync for Drive — the
/drive/sync/deltaendpoint is designed for sync clients and is more efficient than polling individual file endpoints. - Cache tokens — re-issuing a Keycloak token on every request is wasteful and will hit rate limits faster. Cache the access token and re-grant when it expires.