# Rate limits

How many requests a Simple Feedback API key may send per minute, how to read the rate-limit headers, and how to back off after a 429.

Each API key has its own budget for each route, so polling responses never uses up the budget for creating surveys.

## Limits

| Applies to                                          | Limit                   |
| --------------------------------------------------- | ----------------------- |
| Each key, per route                                 | 60 requests per minute  |
| Each key, `POST /v1/surveys/{id}/insights/generate` | 2 requests per minute   |
| Each organization or IP address, per route          | 300 requests per minute |

Generating insights has its own small budget because every call runs an AI analysis over all of a survey's
responses. Read the stored result with `GET /v1/surveys/{id}/insights` first, which is free, and generate a new one
only when its `status` is `stale` or `not_generated`.

The last row protects the service as a whole. It counts per organization once a key is known, otherwise per IP
address. An integration that stays within its per-key limit never reaches it.

Windows are fixed, not sliding: a window starts with the first request and resets a minute later.

## Headers

Every `/v1` response tells you where you stand within the per-key limit:

```http
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 60
```

| Header                  | Meaning                                |
| ----------------------- | -------------------------------------- |
| `X-RateLimit-Limit`     | Requests allowed in the current window |
| `X-RateLimit-Remaining` | Requests left in it                    |
| `X-RateLimit-Reset`     | Seconds until the window starts over   |

## When you hit the limit

The request answers `429` with a `Retry-After` header and the same number of seconds in the body:

```json
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit of 60 requests per 60s exceeded for this API key",
    "details": { "retryAfterSeconds": 34 }
  }
}
```

Wait that long, then retry. Both limits send `Retry-After`, so your client can rely on the header alone.

> **Note:** For bulk work, such as syncing every response of every survey, pace yourself from
> `X-RateLimit-Remaining` instead of running into `429`s. With 100 responses per page, one key reads 6,000 responses
> per minute from one survey.

## MCP

The MCP server has its own budgets, separate from `/v1`, and higher defaults because an assistant makes many small
calls. See [Connect an AI assistant](https://simplefeedback.de/developer/docs/mcp.md#rate-limits).
