Default public API rate limit
| Scope | Limit | Window |
|---|---|---|
| Per IP (default) | 600 requests | 60 s |
/api/* routes that don’t have a stricter override. Implemented by api/_rate-limit.js / api/_ip-rate-limit.js.
MCP server
| Scope | Limit | Window |
|---|---|---|
| Per API key (MCP tools) | 60 requests | 60 s |
OAuth endpoints
| Endpoint | Limit | Window | Scope |
|---|---|---|---|
POST /api/oauth/register | 5 | 60 s | Per IP |
GET /api/oauth/authorize | 10 | 60 s | Per IP |
POST /api/oauth/token | 10 | 60 s | Per credential / client / IP fallback |
api/oauth/register.js, api/oauth/authorize.js, and api/oauth/token.ts.
For /api/oauth/token, the limiter key is client_secret hash for client_credentials, then client_id when present, and only falls back to caller IP when neither credential identifier is available.
Exceeding any of these during the OAuth flow will cause the MCP client to fail the connection handshake — wait 60 s and retry.
Write endpoints
| Endpoint | Limit | Window | Scope |
|---|---|---|---|
POST /api/scenario/v1/run-scenario | 10 | 60 s | Per IP |
POST /api/scenario/v1/run-scenario (queue depth) | 100 in-flight | — | Global |
POST /api/leads/v1/register-interest | 5 | 60 min | Per IP + Turnstile (desktop sources require signed HMAC bypass) |
POST /api/leads/v1/submit-contact | 3 | 60 min | Per IP + Turnstile |
/api/brief/share-url, /api/notification-channels, /api/create-checkout, /api/customer-portal, etc.) fall back to the default per-IP limit above.
Bootstrap / health / version
These have no custom limit beyond the default. Cache headers vary by endpoint:GET /api/bootstrap— default all-keys response usesCache-Control: public, s-maxage=600, stale-while-revalidate=120, stale-if-error=900plus fast-tierCDN-Cache-Control; explicit?tier=fast/?tier=slowrequests use browsermax-age=60/max-age=300and CDNs-maxage=600/s-maxage=7200.GET /api/health—private, no-store, max-age=0plusCDN-Cache-Control: no-store.GET /api/version—public, s-maxage=300, stale-while-revalidate=60, stale-if-error=3600.
Response when limited
HTTP 429 with:Retry guidance
- Respect
Retry-After. Don’t pound on a 429. - For batch work, pace yourself: at 600 req/min/IP the default gives you ~10 req/s headroom.
- For MCP, 60/min is generous for conversational use but tight for scripted batch fetches — prefer the REST API for batch.
- Spurious 429s often mean you’re sharing an egress IP (corporate proxy, CI runner). Contact support for a per-key limit bump if needed.
Hard caps (not soft limits)
- Webhook callback URLs must be HTTPS (except localhost).
api/downloadfile sizes capped at ~50 MB per request.POST /api/scenario/v1/run-scenarioglobally pauses new jobs when the pending queue exceeds 100 — returns 429.api/v2/shipping/webhooksTTL is 30 days — re-register to extend.
