detail and a standard set of rate-limit headers. Workflow runs and other managed-usage surfaces add a run-rate limiter that returns the flat DenialEnvelope, and organization-scoped routes add a third org api-class limiter. This page documents every limiter, every 429 wire shape, the headers each carries, and how to back off.
This page covers the HTTP API only. The realtime Socket.io collaboration server enforces a separate transport-level flood limit that is not an HTTP 429 — see its own section below and /realtime/socket-events.
The rate-limit layers at a glance
All limiters use a sliding 60-second window. The per-key and per-user limiters are checked together — a request is admitted only if both have remaining capacity, and the layer that ran out determines the
detail string.
Per-key and per-user limits
Every request authenticated with anmx_live_… API key passes through a two-level request limiter before it reaches a route handler. Both levels share the same sliding 60-second window.
- Per-key — each API key has its own
rate_limit_per_minute. The default is 60 requests per minute. The rate-limit store counter key isratelimit:apikey:{api_key_id}. - Per-user — across all of a user’s API keys, the aggregate limit is 300 requests per minute. The rate-limit store counter key is
ratelimit:user:{user_id}.
detail is "API key rate limit exceeded"; when the per-user aggregate is hit first, it is "User rate limit exceeded (across all API keys)". Both responses are HTTP 429 with the same header set.
integer
default:"60"
The per-key request ceiling, stored on each API key. New keys are created with
60; an administrator can raise or lower it per key. The per-user aggregate of 300 req/min applies on top and is not configurable per request.429 response — per-key / per-user (Shape A)
429 Too Many Requests — Shape A
integer
The ceiling for the limiter that produced the response (the per-key limit, e.g.
60).integer
Requests left in the current 60-second window.
0 when the limit is hit.integer
Unix epoch time in seconds when the window resets and capacity is restored.
integer
Seconds to wait before retrying. Always present on a 429; defaults to
60 when a more precise value is not available.Org api-class limit
Org-scoped routes (those that require organization membership via X-Organization-ID) add an organization api-class limiter on top of the per-key and per-user limiters. Its ceiling comes from your plan’s api rate-limit entitlement, so it is plan-dependent rather than a fixed default. See /billing/usage-gating and /billing/plans for the per-plan values.
This limiter stacks with the others — an org-scoped API-key request is counted against the org api-class counter, the per-user counter (300/min), and the per-key counter. It fails open on any error, so a rate-limit store outage never blocks org traffic. Administrative routes and scheduled runs do not pass through it.
429 response — org api-class (Shape B)
The org limiter returns the structured envelope wrapped under detail (a dict-valued detail), with code: "rate_limited":
429 Too Many Requests — Shape B
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After headers documented above.
Run-rate limits (run / managed-usage surfaces)
Workflow runs and the other managed-usage surfaces — the AI Composer, the Assistant, and managed knowledge retrieval and ingestion — pass through an additional run-rate limiter as part of the billing admission gate. The schema defines two plan-scoped classes, but only one is live today:sync_exec— synchronous (blocking) run executions per minute. This is the only run-rate class that is actually consumed: every run and managed-usage surface counts againstsync_exec, so it is the only one that can produce a live 429.async_exec— defined in the entitlement schema for asynchronous run executions and reserved for a future surface. It is not an active limit today; treat the synchronoussync_execclass as the live run-rate limit.
sync_exec ceiling comes from your plan’s rate-limit entitlements, so the exact number is plan-dependent. A null entitlement means unlimited (the limiter is skipped), and a 0 entitlement means blocked. See /billing/usage-gating for the per-plan run-rate values and /billing/plans for the plan matrix.
When the run-rate limiter denies a request, it first releases any credit reservation the gate had taken (so a blocked run is charged nothing), then raises a 429 carrying the flat DenialEnvelope. This is the form you must handle on these surfaces alongside the header-based 429.
The run-rate limiter is part of the live billing admission gate. It applies on the run / Composer / Assistant / managed-knowledge surfaces and is absent on plain CRUD and org-settings routes — those return only the
{"detail": …} shapes. The same gate also produces 402 (credit/wallet) and 403 (quota) denials; those are covered on /api-reference/errors and /billing/usage-gating.429 response — run-rate (Shape D, DenialEnvelope)
The run-rate denial is a flat object with no detail wrapper and code: "rate_limit_exceeded":
429 Too Many Requests — Shape D
rate-layer DenialEnvelope also carries the rate-limit headers, each omitted when its underlying value is unknown rather than sent as an empty string. Retry-After is always present and defaults to 60.
integer
Seconds to wait before retrying. Defaults to
60.integer
The class ceiling. Omitted when unknown.
integer
Runs left in the window. Omitted when unknown.
integer
Unix epoch (seconds) when the window resets. Omitted when unknown.
Reading the headers
Every 429 response — whichever limiter fired — carriesRetry-After, and where the value is known, X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. The example below triggers a workflow run, then inspects the headers on a 429. Replace mx_live_… with your API key and the IDs with your own.
The two SDKs surface 429s differently. The JavaScript SDK maps every 429 to
RateLimitError regardless of body shape. The Python SDK maps only the string-detail header 429 (Shape A) to RateLimitError. Both the FastAPI-wrapped dict-detail envelope (Shape B) and the flat rate-layer DenialEnvelope (Shape D) raise the base BillingError with layer == "rate" — because the Python SDK’s layer-to-subclass map has no "rate" key, so any rate-layer envelope (wrapped under detail or top-level) falls through to base BillingError. Catch both RateLimitError and BillingError in Python to handle every 429. See /sdks/errors-retries.Backing off and retrying
Both SDKs treat429, 500, 502, and 503 as retryable and honor Retry-After when deciding how long to wait between attempts; 400, 401, 403, 404, 409, and 422 are not retried. The Python client retries GET and HEAD requests only, while the JavaScript client retries all methods. SSE stream connection errors are surfaced once and are not auto-reconnected.
When you back off by hand, prefer Retry-After over a fixed sleep, and add jitter so concurrent clients do not retry in lockstep:
Socket.io transport rate limit (not a 429)
The realtime collaboration server enforces its own per-user flood limit on socket messages. This is a transport-level guard and is deliberately distinct from the HTTP rate limits above — it is not an HTTP 429 and it does not carry aDenialEnvelope. When a connected user sends gated events too quickly, the server emits an error event:
Socket.io error event
retryAfterMs hint is included only on a genuine limit hit; a store error blocks the operation without it (the limiter fails closed here). The limiter is keyed per user across all gated events. For the full Socket.io event reference, see /realtime/socket-events.
Related pages
Errors & status codes
The three HTTP error-envelope shapes and which surface emits each, including the full status taxonomy.
Usage gating & limits
The billing admission gate and the per-plan run-rate ceilings that drive Shape D 429s.
Authentication
Authenticate every request with
Authorization: Bearer mx_live_… and X-Organization-ID.SDK errors & retries
SDK error classes, the retry policy, and how each SDK surfaces a 429.