# Rate limits

> 120 requests per minute and 5,000 per day per key, with headers telling you where you stand.

> For the complete documentation index, see [llms.txt](https://helpdesk.orangescrum.com/llms.txt).

Source: https://helpdesk.orangescrum.com/guide/api/rate-limits

---
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/list` call with a `filters` object 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

```js
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](https://helpdesk.orangescrum.com/guide/mcp/introduction) 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.
