Skip to main content
Rate limits are enforced at the Vercel Edge runtime using Upstash Redis counters. All limits are sliding 60-second windows unless noted.

Default public API rate limit

Applies to all /api/* routes that don’t have a stricter override. Implemented by api/_rate-limit.js / api/_ip-rate-limit.js.

MCP server

See MCP for details.

Per-plan API rate limits

Authenticated REST API keys (wm_…) are limited per account, not per IP — a key behind a shared egress IP is not throttled by other tenants’ traffic, and all of an account’s keys share one allowance.
  • Per-minute is a hard burst limit — exceeding it returns 429 immediately.
  • Daily included is your plan’s allowance; it resets at 00:00 UTC. Requests beyond it are metered, not rejected — usage-based overage billing is rolling out. Until then a safety ceiling (10× the included allowance) caps runaway usage with a 429.
  • The per-minute burst, daily allowance, and ceiling are all per account (shared across all of an account’s wm_… keys), so issuing more keys does not raise your limit. (Operator-issued Enterprise keys are the exception — each is rate-limited independently.)
  • Need a higher limit? Contact support to raise your plan’s allowance.

OAuth endpoints

Matches the implementations in 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

Other write endpoints (/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 mostly use the default public API limit. Cache headers vary by endpoint:
  • GET /api/bootstrap — browser/session responses keep the default cache policy: all-keys responses use Cache-Control: public, s-maxage=600, stale-while-revalidate=120, stale-if-error=900 plus fast-tier CDN-Cache-Control; explicit ?tier=fast / ?tier=slow requests use browser max-age=60 / max-age=300 and CDN s-maxage=600 / s-maxage=7200. Key-authenticated responses use Cache-Control: no-store and do not emit CDN cache headers. User API key validation also has a fail-closed fixed 60 s per-IP pre-validation limit of 600 attempts.
  • GET /api/healthprivate, no-store, max-age=0 plus CDN-Cache-Control: no-store.
  • GET /api/versionpublic, s-maxage=300, stale-while-revalidate=60, stale-if-error=3600.

Rate limit response headers (self-throttle before a 429)

Every /api/* response — success or error — advertises the IETF RateLimit header fields so an agent can pace itself before it trips a 429:
  • RateLimit-Policy — the applicable quota (q) over a window of w seconds for the default sliding window. Stricter per-endpoint, per-plan, and OAuth limits (see the tables above) apply on those routes.
  • RateLimit-Limit — the same quota as a bare integer, for parsers that predate the structured-field draft.
These are static advertisements, so they add no latency on the hot path. The legacy X-RateLimit-* names are also emitted for back-compat.

Response when limited

An HTTP 429 additionally carries the live per-window counters (remaining is 0; the reset and Retry-After are delta-seconds) plus the combined RateLimit member:
Note the IETF RateLimit-Reset (and the t value in the combined RateLimit member) is seconds remaining, whereas the legacy X-RateLimit-Reset is an absolute epoch in milliseconds. For a daily-ceiling 429 the Retry-After counts down to the next 00:00 UTC.

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.

Customer notifications and paid-plan caps

API and MCP plan caps are tracked against the product catalog limits that ship with entitlements: When a paid user approaches or exceeds one of these limits, WorldMonitor records a compact Convex rollup and opens a current account notice in Settings. The daily count is read from the same per-account meter that governs enforcement, so a warning reflects the same usage number the plan is metered against. Daily limits warn at 80% and switch to over-limit at 100%; burst limits only notify on sustained pressure, not a single isolated spike. If the notice is still current, a Resend-backed lifecycle sends an email at a bounded cadence. The email and dashboard notice explain the current usage, the relevant plan limit, and the available options: reduce traffic, wait for reset, upgrade when a self-serve path exists, or contact support when the next tier is not self-serve. WorldMonitor does not automatically upgrade a user, charge for overages, or move a customer into API Business because they crossed a cap. Any future hard enforcement for paid plans must first pass the internal apiPlanLimitNotices.getEnforcementReadiness gate: no stale usage source, no pending/failed email, and no blocked self-serve upgrade path.

Hard caps (not soft limits)

  • Webhook callback URLs must be HTTPS (except localhost).
  • api/download file sizes capped at ~50 MB per request.
  • POST /api/scenario/v1/run-scenario globally pauses new jobs when the pending queue exceeds 100 — returns 429.
  • api/v2/shipping/webhooks TTL is 30 days — re-register to extend.