Start Here

Rate Limits

Limits keep one integration from starving another and cap how fast a leaked key can spend your money. Request limits answer 429 RATE_LIMIT_ERROR; sending quotas answer 429 QUOTA_EXCEEDED.

How limits work

Every limit is a token bucket. The whole budget is available at once as a burst, then refills evenly — a 100-per-minute budget gets one token back every 0.6 seconds. You never wait for a window to reset.

The limits

Broadcast send100 per minute per workspace

One budget shared by every API key and by sends from the dashboard. A second key does not buy a second budget.

OTP send100 per minute per workspace

Counted separately from verify, so users mistyping codes never block new sends.

OTP send, one destination3, then 1 every 20 s per workspace and destination

One user hammering resend cannot spend your whole budget. Two spellings of one number count as one.

OTP verify100 per minute per workspace

Separately, each code is locked by its fifth wrong guess.

Retrieve a broadcast100 per minute per workspace

Leave a delay between polls — the budget refills one request every 0.6 seconds.

Everything2,000 burst, 50 per second per IP address

A backstop against a single abusive host, applied before any other limit.

Sending quotas

Broadcasts are also capped by how many recipients they carry, not just how many requests you make. Every workspace currently has the same quotas.

Recipients per broadcast350,000

Counted after the list is cleaned. A larger list is refused with 400 VALIDATION_ERROR — split it into several broadcasts.

Recipients per 24 hours1,000,000

A rolling window over every broadcast your workspace accepted, from the API and the dashboard alike.

Recipients still sending350,000

Recipients across your in-flight broadcasts that no provider has taken yet, scheduled ones included. Room frees up as sending drains.

Every recipient that is accepted counts toward these quotas, whatever happens to its message afterwards. Addresses removed when the list is cleaned — invalid, duplicate, unsupported or unsubscribed — never count. A QUOTA_EXCEEDED response carries no Retry-After: the message says how much room is left.

Response headers

Every rate-limited route names the budget it charged and what is left of it. When you get a 429, wait for Retry-After seconds before trying again.

RateLimit-Policy: otp:send
RateLimit-Limit: 100
RateLimit-Remaining: 87

// only on a 429 caused by one of these budgets
Retry-After: 12
The per-destination limit and the per-code verify cap are enforced inside the send and verify logic, so their 429s carry no Retry-After. For a destination, one new send is allowed every 20 seconds; a locked code never recovers — send a new one.

During an outage

If our rate-limit store is unreachable, reads and verifies keep working, but OTP and broadcast sends are refused with 429 until it recovers. We would rather reject a send than deliver messages we cannot count.