Limits and quotas
Every rate limit, size limit and batch size the CMS enforces is in one table here, with its default, the variable that changes it and what a caller sees when it is reached.
Rate limits are counted in fixed windows in Redis, so every replica of the CMS shares one count. Each budget names what it is counted against: an address, a key, a person, a webhook or an environment. A limit marked Fixed is part of the CMS and has no variable. The configuration reference says what goes wrong when a variable is set too high or too low.
Every limit
| Limit | Default | Set by | When it is reached |
|---|---|---|---|
| Sign-in, registration and provider sign-in attempts, per address, each route its own budget | 10 a minute | AUTH_RATE_LIMIT, AUTH_RATE_LIMIT_WINDOW_MS |
429 rate_limited, with Retry-After |
| Delivery reads, per delivery key | 600 a minute | DELIVERY_RATE_LIMIT, DELIVERY_RATE_LIMIT_WINDOW_MS |
429 rate_limited, with Retry-After |
| Delivery requests, per address, counted before the key is checked | 1,200 a minute | DELIVERY_PREAUTH_RATE_LIMIT, DELIVERY_PREAUTH_RATE_LIMIT_WINDOW_MS |
429 rate_limited, with Retry-After |
| Key and token checks hashing at once, per process | 4 | KEY_VERIFY_CONCURRENCY |
Nothing is refused: further requests wait in line |
| Uploads, per person or key | 60 a minute | ASSET_UPLOAD_RATE_LIMIT, ASSET_UPLOAD_RATE_LIMIT_WINDOW_MS |
429 rate_limited, with Retry-After |
| Compares, promotions and clones together, per person or key | 100 a minute | PROMOTION_RATE_LIMIT, PROMOTION_RATE_LIMIT_WINDOW_MS |
429 rate_limited, with Retry-After |
| Webhook deliveries, per webhook | 60 a minute | WEBHOOK_RATE_LIMIT |
Nothing is refused: the rest wait for the next minute |
| One webhook delivery attempt | 10 seconds | WEBHOOK_TIMEOUT_MS |
The attempt counts as failed and is retried |
| Preview tokens minted, per person or key | 30 a minute | Fixed | 429 rate_limited |
| Test events, per webhook | 10 a minute | Fixed | 429 rate_limited |
| Search index rebuilds, per environment | 5 an hour | Fixed | 429 rate_limited, with Retry-After: 3600 |
| Request body | 10 MB | JSON_BODY_LIMIT |
413 payload_too_large |
| A restore's staged metadata, measured after gzip is decoded | 256 MB | IMPORT_MAX_BYTES |
413 payload_too_large |
| One file, uploaded or restored | 25 MB | ASSET_MAX_BYTES |
413 payload_too_large: Files may be at most 25 MB. |
Files held by one project; 0 is unlimited |
5 GB | ASSET_PROJECT_QUOTA_BYTES |
409 quota_exceeded, naming what is used and the quota |
| Images being resized at once, per process | 4 | ASSET_TRANSFORM_CONCURRENCY |
503 unavailable, with Retry-After: 2 |
Widths an image may be asked for with ?w= |
320, 640, 960, 1280, 1920, 2560 | ASSET_IMAGE_WIDTHS |
422 validation_failed for any other width |
| Webhooks, per project | 20 | Fixed | 422 validation_failed: A project may have at most 20 webhooks. |
| Failed delivery attempts in a row before a webhook pauses | 20 | Fixed | The webhook is paused until an owner turns it back on |
| Documents in one publish request | 200 | Fixed | 422 validation_failed on documents: Publish at most 200 documents at a time. |
| Documents and deletions in one push | 500 | Fixed | 422 validation_failed on documents: Send at most 500 documents and deletions in one call. |
| Documents in one diff | 5,000 | Fixed | 422 validation_failed on documents: Send at most 5000 documents at a time. |
| Rows in one promotion | 500 | Fixed | 422 validation_failed on entries: Send at most 500 entries in one call. |
| Rows in one page of a compare | 500, and 200 when limit is left out |
Fixed | 422 validation_failed on limit |
| Documents on either side of a compare, or in a clone's source | 20,000 | Fixed | 422 validation_failed on scope or cloneFrom, saying to narrow the scope or create the environment empty |
| Folders in a push, diff or compare scope | 50 | Fixed | 422 validation_failed on scope: Name at most 50 folders. |
| Environments named when a project is created | 10 | Fixed | 422 validation_failed on environments: Use at most 10 environments. |
| Search results a caller can reach | 200, at most 50 a page | Fixed | 422 validation_failed on offset: Search reaches the first 200 results., or on limit: Ask for between 1 and 50 results. |
| A search question | 200 characters | Fixed | 422 validation_failed on q: Use at most 200 characters. |
Reference levels delivery inlines, ?refs= |
3, at most 5 | Fixed | 422 validation_failed on refs: Ask for between 0 and 5 levels. |
| References and files inlined into one delivery answer | 200 references, 500 files | Fixed | Nothing is refused: the rest stay as written, and _meta says refsTruncated or assetsTruncated |
Folder levels a listing reaches, ?depth= |
10 | Fixed | 422 validation_failed on depth: Ask for between 1 and 10 levels. |
| A document key | 512 characters, 16 folders deep | Fixed | 422 validation_failed on key: A key must be at most 512 characters. or … at most 16 folders deep. |
| A component's schema | 64 KB | Fixed | 422 validation_failed: A schema is at most 64 KB. |
| A project's settings | 64 KB | Fixed | 422 validation_failed on settings: Use at most 65536 bytes of settings. |
| A promotion's message | 500 characters | Fixed | 422 validation_failed on message: Use at most 500 characters. |
| Locales named for one folder of a scope | 64 | Fixed | 422 validation_failed on scope.N.locales: Name at most 64 locales. |
Tags in a delivery ?tags= filter |
16 | Fixed | 422 validation_failed on tags: Filter by at most 16 tags. |
| A website preview URL or a webhook URL | 2,048 characters | Fixed | 422 validation_failed on the URL field: Use at most 2048 characters. |
Documents in one page of the management API's listing, ?limit= |
500 | Fixed | 422 validation_failed on limit: Ask for between 1 and 500 documents. |
| Versions in one page of a document's history | 100 | Fixed | 422 validation_failed on limit: Ask for between 1 and 100 versions. |
| Files in one page of the asset listing | 100 | Fixed | 422 validation_failed on limit: Ask for between 1 and 100 assets. |
| Deliveries in one page of a webhook's log | 100 | Fixed | 422 validation_failed on limit: Ask for between 1 and 100 deliveries. |
| Records in one page of the promotion trail | 50 | Fixed | 422 validation_failed on limit: Ask for between 1 and 50 records. |
| How long a staged restore waits to be committed | 1 hour | Fixed | It is swept: then 404 not_found: No such import. |
When a limit is hit
Every rate limit answers the same way, whichever budget ran out:
{ "error": "rate_limited", "message": "Too many attempts. Try again in a minute." }
The message is the same for every budget, even the hourly one; the Retry-After header carries the window's length in seconds, and is the number to wait. The CLI and the SDK retry a refused read, a GET, a few times, waiting what Retry-After says for at most a few seconds each time; a write, or a longer wait, is the caller's to retry. The dashboard shows its own sentence for the budgets content managers meet, such as "Too many promotions at once; try again in a minute."
If Redis cannot be reached, a rate-limited route does not let the request through uncounted: it answers 503 rate_limit_unavailable, "Please try again shortly." Fix Redis rather than the limit.
Per-address budgets are only as accurate as TRUST_PROXY. Behind a proxy with TRUST_PROXY left at 0, every caller shares the proxy's address and one budget; Behind a proxy and TLS shows how to set it.
A size limit the CMS enforces is no use if a proxy in front of it cuts the request first. Give the proxy at least JSON_BODY_LIMIT on every path, ASSET_MAX_BYTES on uploads, and IMPORT_MAX_BYTES on …/import.
Related
- Configuration reference: every variable in this table, and what breaks when it is wrong.
- Behind a proxy and TLS:
TRUST_PROXYand body limits at the edge. - Delivery: what the delivery budgets protect.
- Promotion: the promotion budget and batch sizes in use.
- Backups: the import limit in use.