Rate limits and quotas
The platform applies rate limiting narrowly and deliberately. There is no blanket per-caller quota today; the limiter guards the surfaces where abuse is cheapest. This page is the authoritative account of the real limiter behavior — it deliberately does not publish specific numbers, because they are operational and subject to change.
Handle 429 defensively on every route, not just the ones documented here.
Never assume a route’s current throttling behavior is permanent, and never
treat a 429 as a permanent failure.
What is rate-limited
Credential verification
The fdk_ verification path is rate-limited on the attempt, before the
database lookup. An fdk_ key is otherwise online-guessable with no limiter,
so the throttle sits in front of the key lookup rather than behind it. This
covers /v1/my/* authentication and the initial-credential issuance /
session-verification flow.
The limiter is keyed per caller (the verification bucket). When it is spent:
- The response is
429. - A
retry-afterheader (in seconds) is set to the limiter’s window. - The body’s code is
rate_limited, with a message stating the per-minute limit.
Some management and operator routes
Selected management and operator routes carry their own limiters and return the
same 429 + retry-after + rate_limited shape when spent. Treat any
management route as potentially throttled.
Checkout velocity
Checkout has a velocity gate distinct from the credential limiter. When too
many attempts occur for an order/scope, it returns 429 with code
velocity_exceeded (a deliberately non-oracle-sensitive 429, so the
storefront can back off and retry). See
Error codes.
The 429 response
| Element | Value |
|---|---|
| HTTP status | 429 |
retry-after header | Seconds to wait before retrying. Always honor it. |
| Body code | rate_limited (verification/management) or velocity_exceeded (checkout). |
HTTP/1.1 429 Too Many Requests
retry-after: 60
content-type: application/json
{ "error": { "code": "rate_limited", "message": "too many credential verification attempts; ..." } }Fail-closed when the limiter store is down
The limiter is backed by a store. If that store is unavailable, the platform
does not let an unmetered request through — it fails closed with a 503
rather than admitting the request:
| Surface | Code | Status |
|---|---|---|
| Credential/session verification (limiter store down) | db_error / select_failed (message: rate limiter unavailable) | 503 |
| Credential/session check itself could not complete | auth_unavailable | 503 |
The verification limiter does not emit rate_limiter_unavailable — that
code belongs only to the provenance module. When its own backing store fails,
it surfaces the underlying database error code (db_error or select_failed)
with a 503 and the message rate limiter unavailable. Branch on the 503
status, not on the specific code.
A 503 from these surfaces means the request was refused, not admitted. Retry
with backoff. This is the same fail-closed posture the rest of the platform
takes on an unavailable dependency (see Error codes).
What is not rate-limited
- The public catalog reads (
GET /v1/tenants/{tenant}/catalog/*— products, product-by-slug, search, collections) are unthrottled today. There is no per-caller limit on them. This is the one deliberately public read surface, and it carries no verification hop to throttle in front of.
Because the public catalog is unthrottled today does not make that permanent.
Build clients that back off on 429 everywhere, including on read paths, so a
future limiter never breaks them.
Quotas
There are no published per-tenant request or resource quotas today. Pagination
is offset-based (limit 1 to 100, offset 0 or greater); list endpoints do
not impose a rate cost beyond the limiters described above. See
Pagination and rate limits for the paging shape.
Client guidance
- Read the
retry-afterheader and wait at least that long before retrying. - Back off exponentially on repeated
429s. - Retry
503(db_error/select_failedfrom a downed limiter store,auth_unavailable) with backoff — it is a transient fail-closed refusal, never a signal to proceed unmetered. - Do not hard-code any limit or window; they are operational and unpublished.
Related
- Error codes —
rate_limited,velocity_exceeded,db_error/select_failed,auth_unavailable. - Pagination and rate limits — the paging shape.
- Tenant-facing API — which routes are public versus credentialed.