Rate limits

Limits by endpoint type, response headers, and why they are off by default in this environment.

Rate limiting is controlled by a single flag, RATE_LIMIT_ENABLED, and it is off in local and kind development by default. Every request succeeds regardless of volume in that environment. Where it matters, note which environment a limit was observed in.

Limits by endpoint type

Requests are classified by the route they hit, not given one blanket limit:

TypeBase limitScaled by planKeyed by
auth10 / minNoCaller IP
api100 / minYesOrganization id
user100 / min (20 / min if unauthenticated)NoUser subject, or IP if unauthenticated
webhook60 / minNoSource IP
public60 / minNoCaller IP

Only api scales with plan tier, deliberately: scaling an authentication endpoint's tolerance by plan would make credential stuffing easier the more a customer pays, which is backwards. The multipliers, verified against rate_limit_policy.py:

PlanMultiplierEffective api limit
Free1x100 / min
Starter2x200 / min
Pro5x500 / min
Business10x1000 / min
Enterprise50x5000 / min

These numbers differ from an older plan-limits table that predates the endpoint-type scheme above; the table here is what the running middleware does. An api-classified limit also applies to the whole organization, not per member: every teammate's calls share one budget.

Headers

Every response, successful or not, carries:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1705577400

A 429 additionally carries Retry-After (seconds), and its body is not the usual error envelope. See Errors for the exact shape.

Enabling it locally

Set RATE_LIMIT_ENABLED=true in backend/.env and restart the backend to observe real 429 responses while developing against it.

On this page