For AI agents: a documentation index is available at /llms.txt. A markdown version of this page is available at /guide/api/rate-limits.md.
Rate limits
120 requests per minute and 5,000 per day per key, with headers telling you where you stand.
The Developer API enforces two limits at once. Exceeding either returns
429 Too Many Requests.
| Window | Limit |
|---|---|
| Per minute | 120 requests |
| Per day | 5,000 requests |
Both limits are counted per API key. Requests without an X-API-KEY header
are counted per source IP instead — which matters when a misconfigured client
starts failing authentication, because those attempts still consume quota.
#Response headers
Every response carries your current standing, so you can throttle before you get blocked rather than after:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 43
When you are blocked, a third header tells you how long to wait:
Retry-After: 60
The value is in seconds. Honour it rather than guessing — retrying early just burns the daily allowance.
#Staying under the limits
Batch with filters, not loops
One
tasks/listcall with afiltersobject beats fetching every project and looping. Most list endpoints filter server-side.Cache what does not change
The user directory and project list change rarely. Cache them for minutes, not seconds — this alone usually removes most of the traffic.
Back off on 429
Sleep for
Retry-After, then retry with exponential backoff and jitter so a fleet of workers does not resynchronise into a thundering herd.Use a separate key per integration
Limits are per key, so a nightly bulk sync on its own key cannot starve your interactive integrations.
Generate fresh signing values on every retry
A retry must carry a new X-TIMESTAMP, a new X-NONCE and a recomputed
X-SIGNATURE. Replaying the original headers is rejected as a reused nonce —
and if the wait exceeded five minutes, as an expired timestamp too.
#A worked backoff
async function callWithRetry(path, payload, attempt = 0) {
const response = await call(path, payload); // re-signs on every invocation
if (response.status !== 429 || attempt >= 4) return response;
const retryAfter = Number(response.headers.get('Retry-After') ?? 60);
// Exponential growth plus jitter, so parallel workers don't resynchronise.
const backoff = retryAfter * 1000 * 2 ** attempt + Math.random() * 1000;
await new Promise((resolve) => setTimeout(resolve, backoff));
return callWithRetry(path, payload, attempt + 1);
}
#MCP traffic counts too
The MCP server authenticates with the same key, so tool calls from Claude, Cursor or Copilot draw on the same 120/minute and 5,000/day budget as your REST integrations. If an assistant is doing bulk work, give it its own key.
#Need more headroom?
The limits are configurable per deployment. If a legitimate integration needs more, contact your Orangescrum account manager with the expected request volume and pattern.