Rate Limits
The limits actually enforced today, the response headers, and recommended client behavior under throttling.
Brainerce enforces two layers of rate limiting: a global per-IP throttle that applies to every route, and, on top of it, a per-credential quota for admin API keys and marketplace-app installations. A request must pass both.
Global — per IP, every route
| Window | Limit | Applies to |
|---|---|---|
| 60 seconds | 60 requests per IP | Every route, every auth mode, including unauthenticated ones. |
| 1 hour | 1000 requests per IP | Same. Runs alongside the 60-second window, not instead of it. |
Both tiers are keyed on the client IP, not on your credential. Two integrations running from the same host share these buckets.
Individual routes set their own, tighter limits. Anything that costs money to abuse —
customer registration, login, forgot-password, resend-verification, coupon application,
review submission — carries a per-route override well below 60/min, some as low as
3 requests per minute per IP. There is no single number that covers the storefront
surface; treat 60/min as a ceiling, not an allowance, and read the 429 body's
details.limit for the limit that actually tripped.
Per API key — admin mode
On top of the global tiers, a brainerce_* key gets its own bucket keyed on the key id,
so two integrators on the same IP no longer collide:
| Key tier | Limit |
|---|---|
FREE | 60 requests / minute |
PRO | 500 requests / minute |
GROWTH | 2000 requests / minute |
ENTERPRISE | 10000 requests / minute |
Three things to know about this bucket:
- It does not lift the global per-IP tiers. The
/v1/*controller has no throttle exemption, so aGROWTHkey calling from a single IP still meets the 60/min per-IP throttle first. The per-key quota bounds a key; it does not exempt it. To actually use a 2000/min tier you have to spread the traffic across source IPs — a single worker behind one NAT will see 429s long before its tier. - There is a per-account aggregate ceiling. All keys on one account share a bucket of
min(tier_limit × 5, 20000)requests/minute — so minting more keys does not multiply your throughput. A 429 from this bucket carriesdetails.scope: "account". - A few
/v1/*routes are capped far lower, regardless of tier: customer login and register at 5/min, forgot-password and reset-password at 3/min, applying a coupon to a cart or checkout at 10/min, applying a gift card to a checkout and checking a gift-card balance at 5/min each (tighter than the coupon route on purpose: a coupon code is meant to be shared, a gift-card code is bearer value and therefore a guessing surface),POST /v1/storefront-bot/conversations/{id}/summarizeat 10/min, and media ingest-by-URL at 20 per 5 minutes. These are abuse and cost-amplification guards, not quotas you can raise.
Support can pin an individual key to a custom limit; if that has been done for you, the override replaces the tier value.
Per installation — marketplace apps
App-installation tokens (app_inst_*) are on their own tiers: 1000/min for
first-party Brainerce apps, 500/min for ECOMMERCE_PLATFORM connectors, 100/min
for everyone else. These do not apply to brainerce_* API keys — if you hold an API
key, the table above is yours.
Inbound webhooks
Inbound webhook routes are not unbounded. The marketplace-app webhook receivers
(/api/apps/webhooks/* and /api/apps/webhooks-signed/*) are capped at 30,000
requests / minute per installation — a DoS safety valve rather than a plan quota,
keyed on the installation id so one busy install cannot starve another behind the same
egress IP. The global per-IP tiers above still apply to any inbound route that does not
explicitly override them.
Failed-authentication throttling
Separately from the quotas above, an IP that repeatedly sends invalid or expired credentials (a wrong brainerce_* key, a forged token) is temporarily blocked: after 50 failed-auth attempts within 5 minutes that IP receives 429 Too Many Requests until the window clears. Requests that authenticate successfully never count toward this limit.
If you hit this, fix the credential — don't retry-storm. A valid key with the correct scopes is never affected.
Response headers
| Header | Description |
|---|---|
X-RateLimit-Limit | Requests allowed in the current window |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | Seconds until the window resets — not a Unix timestamp. Add it to "now" yourself |
Retry-After | Seconds to wait before retrying (only on 429 responses) |
X-Brainerce-*-prefixed rate-limit headers do not exist; the three above are the whole set.
Because two windows run at once, the hourly tier emits its own suffixed copies —
X-RateLimit-Limit-long, X-RateLimit-Remaining-long, X-RateLimit-Reset-long. The
unsuffixed headers describe the 60-second window.
When throttled
A throttled request returns 429 Too Many Requests with the standard error envelope:
{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Rate limit exceeded — retry in 12 seconds.",
"details": { "retryAfterSeconds": 12, "limit": 60, "ttlMs": 60000 },
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/products"
}Switch on code === 'RATE_LIMITED' — it is the same code whichever bucket tripped, and
details tells you which one (scope: "account" for the per-account ceiling, tier for
the per-key bucket).
Recommended client behavior
- Honor
Retry-After— Don't retry sooner than the header tells you. - Use exponential backoff for transient errors (
429,503, network failures). Start at 1s, double up to 60s. - Batch where possible — e.g.
POST /v1/products/bulkcreates up to 1000 products in one queued call instead of N individualPOST /v1/productsrequests. - Cache idempotent reads — Storefront catalog data changes slowly; cache product lists for 30-60 seconds on the edge.
- Use webhooks instead of polling — Webhooks push order/inventory/customer changes to you in real time.
No burst allowance
There is none. Each bucket is a fixed-window counter that rejects at exactly the limit —
request number 61 in a 60-request window is a 429, with no grace, no sliding average,
and no borrowing against the next window. Size your client for the limit itself.
If your integration needs a higher limit, contact [email protected] with your expected traffic pattern.