# Rate limits and headers

> Request rate limits per API key, the rate-limit response headers, retry-after on 429 and 503 and the other headers the Fileverity API sets.

Source: https://fileverity.com/en/docs/rate-limits

Each API key can make 120 requests per minute. The limit is counted in one-minute windows and covers every endpoint, including requests that return an error. When a window is used up, further requests get `429` with the code `rate_limited` until the window resets.

Need higher limits or volume pricing? [Contact sales](https://fileverity.com/en/contact).

## Rate-limit headers

Every response to an authenticated request carries the state of your window:

| Header | Meaning |
| --- | --- |
| `x-ratelimit-limit-requests` | Requests allowed per window. |
| `x-ratelimit-remaining-requests` | Requests left in the current window. |
| `x-ratelimit-reset-requests` | Seconds until the window resets. |
| `retry-after` | Only on `429` with the code `rate_limited` and on `503` with the code `service_unavailable`. Seconds to wait before the next request. `pending_files_limit` (also `429`) does not carry it. |

Requests rejected for a missing or invalid key (`401`) do not carry these headers.

**Honor retry-after: JavaScript**

```javascript
async function call(url, init) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const response = await fetch(url, init);
    if (response.status !== 429) return response;
    const wait = Number(response.headers.get("retry-after")) || 1;
    await new Promise((resolve) => setTimeout(resolve, wait * 1000));
  }
  throw new Error("Rate limited");
}
```

**Honor retry-after: Python**

```python
import time

def call(method, url, **kwargs):
    for attempt in range(5):
        response = requests.request(method, url, **kwargs)
        if response.status_code != 429:
            return response
        time.sleep(int(response.headers.get("retry-after", 1)))
    raise RuntimeError("Rate limited")
```

Spread bursts over time, prefer [webhooks](https://fileverity.com/en/docs/webhooks.md) to tight polling loops, and use `Prefer: wait` on [analyses](https://fileverity.com/en/docs/analyses.md) instead of polling. When capacity is busy, `POST /analyses` returns `503 service_unavailable` with `retry-after`; see [When the service is busy](https://fileverity.com/en/docs/analyses.md#busy).

## Other response headers

| Header | Where | Meaning |
| --- | --- | --- |
| `x-request-id` | Every response | The request ID, also in error bodies. |
| `cache-control: no-store` | Every response | API responses are never cached. |
| `preference-applied` | `POST /analyses` | `wait=N` when your `Prefer` header was honored. |
| `idempotent-replayed` | POST endpoints | `true` when the response is a saved replay. See [Idempotency](https://fileverity.com/en/docs/idempotency.md). |
| `x-evidence-included` | `GET /analyses/{id}/report.pdf` | `false` when document evidence has already expired and the PDF omits it. |

## Headers you send

- `Authorization: Bearer <key>`: required. See [Authentication and keys](https://fileverity.com/en/docs/authentication.md).
- `Content-Type: application/json` for JSON bodies; `multipart/form-data` for `POST /files` uploads.
- `Idempotency-Key`: optional, on POST requests.
- `Prefer: wait=N`: optional, on `POST /analyses`.

Example: `curl -i https://api.fileverity.com/v1/models -H "Authorization: Bearer $FILEVERITY_API_KEY"` shows all response headers.

