API ReferenceRate Limits

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

WindowLimitApplies to
60 seconds60 requests per IPEvery route, every auth mode, including unauthenticated ones.
1 hour1000 requests per IPSame. 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 tierLimit
FREE60 requests / minute
PRO500 requests / minute
GROWTH2000 requests / minute
ENTERPRISE10000 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 a GROWTH key 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 carries details.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}/summarize at 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

HeaderDescription
X-RateLimit-LimitRequests allowed in the current window
X-RateLimit-RemainingRequests left in the current window
X-RateLimit-ResetSeconds until the window resets — not a Unix timestamp. Add it to "now" yourself
Retry-AfterSeconds 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).

  1. Honor Retry-After — Don't retry sooner than the header tells you.
  2. Use exponential backoff for transient errors (429, 503, network failures). Start at 1s, double up to 60s.
  3. Batch where possible — e.g. POST /v1/products/bulk creates up to 1000 products in one queued call instead of N individual POST /v1/products requests.
  4. Cache idempotent reads — Storefront catalog data changes slowly; cache product lists for 30-60 seconds on the edge.
  5. 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.