Rate limits
The request budgets for each key and each address, the headers that report them and what the API returns when a limit is reached.
The API counts requests in one-minute windows and answers 429 when a budget is used up.
Budgets#
| Budget | Default | Counts |
|---|---|---|
| For each key | 600 a minute for a test key, 6,000 once it is live | Every request made with the key |
| Calls on a person, for each key | 60 a minute | POST /subjects/find, /export, /withdraw, /erase, /link, POST /requests/link, the notice campaign links and both processor-task calls, on top of the key's budget |
| Consent checks, for each key | 600 identifiers a minute | Each identifier in POST /consent/check, the subject and every alsoKnownAs entry, and each identifier in a write with collectedAt from a key that holds consent:check, on top of the key's budget |
| Audit log reads, for each key | 60 a minute | GET /audit-log, on top of the key's budget |
| Hosted notice page, for each address | 300 requests and 20 unknown links in 10 minutes | Requests to /n/{token} from one IP address |
| Browser and app writes, for each key and address | 60 a minute | Requests other than GET made with a publishable key from one IP address |
| For each address | 3,000 a minute | Every request to /sdk from one IP address, whatever the key |
| Hosted rights request form | 5 an hour for each address | Requests submitted through the form |
- Whoever runs your deployment can change these defaults.
- You can set a key's own budget, from 60 to 600,000 a minute, under Rate limit when you create the key.
- Set a key's own number of identifiers to check, from 60 to 6,000 a minute, under Checks per minute on the key. Whoever runs the deployment can raise the 6,000 with
SDK_CHECK_MAX_PER_MINUTE. - Each consent check also counts once against the key's budget and the budget for each address.
- A key that kept the default of 600 moves to 6,000 when it goes live. A budget you set yourself stays as it is.
- Calls made with a secret key are not held to the write budget for each address, since one server acts for many people.
A project's monthly record limit is separate from these budgets; see Usage and limits.
Response headers#
| Header | Sent on | Value |
|---|---|---|
RateLimit-Limit |
Every keyed response | The key's budget for the window |
RateLimit-Remaining |
Every keyed response | Requests left in the window |
RateLimit-Reset |
Every keyed response | Seconds until the window starts again |
Retry-After |
A 429 response |
Seconds to wait before the next request |
RateLimit-Limit: 600
RateLimit-Remaining: 587
RateLimit-Reset: 42When a limit is reached#
The answer is 429 with a Retry-After header. Wait that many seconds, then continue. A consent check, or a write with collectedAt from a key that holds consent:check, over the key's identifier budget answers 429 with the code CHECK_BUDGET_EXCEEDED.
{
"statusCode": 429,
"message": ["Too many requests"],
"error": "TOO_MANY_REQUESTS",
"timestamp": "2026-09-24T10:12:03.000Z",
"path": "/sdk/v1/subjects/find",
"requestId": "0d8a3c1e-5a52-4f0b-9c0e-2f1b7a9d4e63"
}| Caller | What it does on a 429 |
|---|---|
| Your server | Wait for Retry-After, then send the request again |
| Node.js SDK | Throws ConsentApiError with retryAfter. It does not retry. |
| Python SDK | Raises RateLimitError with retry_after. It does not retry. |
| Browser runtime | Applies the visitor's choice locally at once and sends the write again on a later page load |
For a large import, use Import CSV in Records, or pace your writes below the key's budget from the start: under ten a second for a test key.
The other error bodies are in Errors.