> For the complete documentation index, see [llms.txt](https://herd-security.gitbook.io/herd-security-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://herd-security.gitbook.io/herd-security-docs/api-documentation/rate-limits.md).

# Rate Limits

The Herd API enforces a per-key rate limit of 1000 requests per minute and reports usage through X-RateLimit response headers.

The Herd API enforces a rate limit of **1,000 requests per minute per API key**, counted in a fixed 60-second window. All `/api/v1` endpoints share the same budget for a given key.

## Rate limit headers

Every successful response includes headers describing your current budget:

| Header                  | Meaning                                                       |
| ----------------------- | ------------------------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests allowed in the current window (`1000`)       |
| `X-RateLimit-Remaining` | Requests remaining in the current window                      |
| `X-RateLimit-Reset`     | When the current window resets, as a Unix timestamp (seconds) |

## When you exceed the limit

Requests over the limit are rejected with **HTTP 429 Too Many Requests**. The response carries the same `X-RateLimit-*` headers (with `X-RateLimit-Remaining: 0`), plus a `Retry-After` header with the number of seconds until the window resets:

```json
{
  "status": "error",
  "error": {
    "code": 429,
    "message": "Rate limit exceeded, please slow down."
  }
}
```

The cleanest way to handle 429s is to wait for the `Retry-After` interval before retrying:

```javascript
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);

    if (response.status !== 429) {
      return response;
    }

    const retryAfter = Number(response.headers.get("Retry-After") ?? 2 ** i);
    await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
  }

  throw new Error("Max retries exceeded");
}
```

{% hint style="info" %}
Since each API key gets its own budget, give high-volume integrations their own dedicated key rather than sharing one key across several tools.
{% endhint %}
