Skip to main content
Brew rate-limits every authenticated public v1 endpoint. Most limits apply per credential and route over a rolling 60-second window. Email audits also use an organization-wide request limit and concurrency limits. Rate-limited responses expose standard Retry-After semantics. The no-auth GET /v1/health and GET /v1/help discovery endpoints have no rate limit.

Headers

Every rate-limited response carries the rate-limit headers, and a 429 RATE_LIMITED or retryable 503 response also carries Retry-After. See Response headers for the complete header catalog and wire-level examples. This page owns the policies and recovery behavior. For a rolling-window credential refusal, Retry-After is the time until the oldest recorded request leaves the window. The refused request does not take a slot, so repeating it does not extend that window. The error’s details identify the admission scope, limit, remaining budget, reset time and consumed: false. Wait the indicated time and retry the same request, keeping its idempotency key when the operation supports one.

MCP Tools

MCP tools draw on the same per-credential budgets as the routes they call. Tools on one budget share it: for example, send_email, pause_email_send, resume_email_send, and cancel_email_send share 10 a minute. Call get_brew_capabilities for the current budget of every tool. After RATE_LIMITED, wait the seconds it names and retry only the refused calls.

Per-Route Policies

Each route declares a named policy in lib/api/rate_limit/policy.ts. The policy name appears in observability dashboards. HTTP rate-limit failures use the stable RATE_LIMITED error code, with the policy context available in the message or logs.

Brands, Integrations, and API Keys

These routes aren’t in the policy table yet. An API key reads their limit from the X-RateLimit-Limit header, the same as every other route. On these routes, brand keys and organization keys get the same limits. The limit for POST /v1/brands, which takes an organization key, isn’t listed yet.

Email Audit Admission

Email audits fan out to several external checks. They use these additional limits across the Public API, MCP, and agent calls: Capacity rejections return 429 RATE_LIMITED with Retry-After. They do not run or charge the audit.

Open Image Uploads

A brand can hold 20 local-file image uploads open at once. An upload is open from POST /v1/content/image-uploads until POST /v1/content/add-image adds it or it expires. Opening a 21st is 429 RATE_LIMITED, with Retry-After set to when the oldest expires.

Session Traffic: 300/min Ceiling

UI-backed session traffic (dashboard, Brew chat orchestrator) gets a flat 300/min ceiling across every policy above except email.audit, which stays at 6 per minute. API-key traffic uses the strict per-route limits in the table.

Generosity and Burst Behavior

  • The window is a rolling 60 seconds, not a fixed wall-clock minute. A spike at the top of a minute does NOT get a free second budget.
  • The store uses Redis sorted-sets (when available); when Redis is unavailable, every rate-limited endpoint fails open with X-RateLimit-Remaining: <limit> and X-RateLimit-Reset: now+60. You will not see spurious 429s during a Redis blip.
  • Test mode bypass: BREW_DISABLE_API_RATE_LIMIT=1 on the dev server skips the gate entirely (test-only env var; never set in production).

429 Recovery Cookbook

The canonical retry loop:
Key invariants:
  • Always honor Retry-After. Take the max of Retry-After and your own back-off so you don’t out-aggressive yourself.
  • Reuse the same Idempotency-Key on every retry. See Idempotency for replay semantics.
  • Preemptively pause when X-RateLimit-Remaining hits 0. The header ships on rate-limited responses, so a well-behaved client can throttle itself before a 429 ever fires.
The standard 429 RATE_LIMITED and retryable 503 envelopes, including error.retryAfter, are documented in Errors. The header and body carry the same wait duration.

Server-Side Observability

Every rate-limited response records an api.rate_limit.hit event with the policy, admission scope, limit, retry interval, request id, and actor scope. The event does not include request content. If persistent 429 responses do not match your expected request rate, contact support with the x-request-id from one response.
  • Idempotency: pair with Idempotency-Key so retries are safe.
  • Errors: full error envelope + every error.code.
  • Response headers: every header Brew sets.
  • Async jobs & polling: for POST /v1/sends and POST /v1/automations/triggers/{triggerEventId}/fire, you don’t need to call the API again to wait for delivery. Poll the run.

Need Help?

Our team is ready to support you at every step of your journey with Brew. Choose the option that works best for you:

Search Documentation

Type in the “Ask any question” search bar at the top left to instantly find relevant documentation pages.

Ask ChatGPT or Claude

Click here to open the Copy page menu at the top of this page, then send the page to ChatGPT, Claude, or your editor.