Documentation Menu

Limits and quotas

6 min read

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.

Last updated