The public API applies two rate limits: a pre-auth IP limit that defends against brute-force key enumeration, and a per-key limit sharded by route class. Both return `rate_limit_exceeded` (HTTP 429) when exceeded.

## How rate limits work

The IP limit runs before authentication — coarse enough to shed abuse without affecting legitimate traffic.

The per-key limit runs after authentication and is sharded by route class so your read traffic does not crowd out your session-create traffic. Route classes:

- `sessions:create` — `POST /v1/sessions`.
- `applicants:create` — `POST /v1/applicants`.
- `dsar:create` — `POST /v1/data_subject_requests` (capped low).
- `reads` — every `GET /v1/*`.
- `global` — catchall per key, applied alongside the more specific class.

Each organization has a tier (`standard`, `elevated`, `enterprise`, or `custom`). New organizations start on `standard`.

Route classes are an internal throttling dimension, independent from the scopes you grant a key: `route_class` names the traffic bucket a route falls into, not a scope you hold. A key granted only `sessions:write` still shows `route_class: "sessions:create"` in a 429 from `POST /v1/sessions`, since the class name predates a later scope simplification and was kept as-is so existing rate-limit tooling and alerts do not need to change.

## Rate-limit headers

Every authenticated response carries the per-key limit state:

```
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 873
X-RateLimit-Reset: 1717000060
X-RateLimit-Window: 60
```

- `X-RateLimit-Limit` — the request budget for the current window.
- `X-RateLimit-Remaining` — how many requests are left before `429`.
- `X-RateLimit-Reset` — UNIX epoch seconds when the window rolls over.
- `X-RateLimit-Window` — window length in seconds.

Track `X-RateLimit-Remaining` and slow down before you reach zero — it is cheaper than handling 429s reactively.

## When you hit the limit

A `429 rate_limit_exceeded` response includes details inside the error envelope:

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded for this API key",
    "request_id": "req_...",
    "details": {
      "reset_at": 1717000060,
      "limit": 100,
      "window_seconds": 60,
      "route_class": "sessions:create",
      "tier": "standard"
    }
  }
}
```

Back off until `reset_at`, then retry. Use exponential backoff with jitter if you are processing a queue — synchronizing every worker on `reset_at` just hits the next window's limit.

## Burst vs sustained rate

Limits are enforced as a fixed-window count, not a token bucket. A burst that fits inside one window passes; long-term sustained throughput is bounded by the window's limit. The IP limit and most per-key limits use a 60-second window; `dsar:create` uses 24 hours.

## Asking for a higher limit

If you consistently see `rate_limit_exceeded` and adding backoff is not enough, contact support. Custom tiers are sized to your traffic shape. See the [API error codes](/api/errors).