Errors and rate limits
Error codes, HTTP statuses, rate-limit tiers, and retry guidance for the CookieBeam REST API.
Error envelope
Errors use the same envelope as successful responses. The data object carries a human-readable error message and, for most API-key and consent-subject failures, a machine-readable code:
{
"data": {
"error": "API key is missing required scope(s): banners:write",
"code": "missing_scope"
},
"version": "v1"
}
Validation errors add a details array describing each invalid field.
Error codes
Authentication and scope failures (see Authentication):
| HTTP status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The request body failed validation (consent subject endpoints) |
| 401 | invalid_key | The API key is malformed, unknown, or has the wrong environment prefix |
| 401 | revoked_key | The API key has been revoked |
| 401 | expired_key | The API key has expired |
| 401 | unauthorized | The endpoint requires an API key and none was sent |
| 403 | plan_required | The team's plan does not include the required feature |
| 403 | missing_scope | The key lacks a required scope; the message names the missing scopes |
| 403 | test_key_read_only | A test key was used for a write or publish operation |
| 404 | not_found | The referenced banner or consent subject was not found |
| 409 | version_conflict | A consent state write carried a stale baseVersion; the response includes the current snapshot |
| 413 | invalid_request | The request body exceeded 20 KB (consent subject endpoints) |
| 429 | rate_limited | A rate limit was exhausted; see below |
| 500 | internal_error | Unexpected server error |
| 503 | identity_unavailable | Consent identity hashing is temporarily unavailable; retry later |
Rate limits for API keys
API-key requests are limited per key over a fixed 15-minute window. The limit depends on the team's plan:
| Plan | Requests per 15 minutes |
|---|---|
| Free | 50 |
| Starter | 200 |
| Professional | 500 |
| Business | 2,000 |
| Enterprise | 5,000 |
A plan the server cannot map to a tier falls back to the Free limit.
Responses that pass key authentication and reach the rate limiter carry three headers:
X-RateLimit-Limit— the key's limit for the window.X-RateLimit-Remaining— requests left in the current window.X-RateLimit-Reset— when the window resets, as a Unix timestamp in milliseconds.
This includes HTTP 429 rejections. It does not include requests rejected before rate limiting runs — entitlement, scope, and test-key failures (and malformed, revoked, or expired keys) return without these headers — and some server-error paths also skip them. Treat the headers as present on rate-limiter outcomes, not as a universal invariant.
When the limit is exhausted the API returns HTTP 429:
{
"data": {
"error": "Rate limit exceeded",
"code": "rate_limited"
},
"version": "v1"
}
The response includes a Retry-After header with the number of seconds to wait before the next request.
Other rate limits
Two limits apply independently of API keys:
- Requests to the banner endpoints that arrive without an API key (dashboard sessions and other browser traffic) are limited to 100 requests per 15 minutes per client IP.
POST /api/consent/log(the unversioned banner ingestion route) is limited to 1,000 requests per minute per client IP.- Failed authentication attempts are limited to 10 per minute per client IP; beyond that, further attempts return HTTP 429 with
rate_limitedand aRetry-Afterheader.
Retry guidance
- On HTTP 429, wait at least the number of seconds in
Retry-Afterbefore retrying. Do not retry in a tight loop — repeated bursts just consume the next window. - On HTTP 5xx, retry with exponential backoff.
- Reads (
GETand the consent resolve endpoint) are safe to retry as-is. - For consent state writes, retry a 409
version_conflictby re-reading the subject, merging against the returnedcurrentsnapshot, and writing again with the newversionasbaseVersion.