Limits
What limits a channel has, and what each 429 means.
Paid channels
Paid channels have no gateway send cap by default and no sandbox message allowance. An operator override can impose a positive daily ceiling or a zero stop. Public ingress and in-flight limits still apply. Pacing is up to you. WhatsApp can still restrict a number that behaves badly, so watch Health before you push volume.
Public customer request limits are below. Ingress and concurrency values are code defaults that the deployment can configure; use the returned window, bound and Retry-After rather than treating these numbers as fixed API guarantees:
- A request body can be at most 1 MiB.
- One client address can make at most 3,000 requests a minute across all channels and tokens, with a burst of 600. The public edge also has a shared ceiling of 12,000 requests a minute with a burst of 2,400. A 429
rate_limitedwith awindowstartingingress.comes from these. - One token may have at most 16 requests in flight at once. Over that, 429
rate_limitedwithwindow: "concurrency.token"and a 1 second wait. When the whole API is carrying as much as it will, you get the same 429 withwindow: "concurrency". - The gateway holds at most 8 in-flight requests per channel, and also bounds all of one account's channels together and a shared pool. Over a bound, 429
gateway_busywithboundset tochannel,accountorpool. - Media sends have their own bound: 429
gateway_busywithbound: "media"and a 10 second wait.
Design for back-pressure: keep a small queue on your side and send a few at a time. Branch on the 429 code: a busy gateway needs a short wait, an exhausted daily allowance can mean waiting until midnight, and a zero send cap needs support to change it.
The sandbox channel
Every account comes with one free sandbox channel for testing. Its allowances are counted in Bangkok time (UTC+7).
| Allowance | `window` value | Limit | Resets |
|---|---|---|---|
| Messages sent | sandbox_messages_daily | 180 a day | midnight |
| New chats | sandbox_chats_monthly | 10 a month | the first of the month |
| Number checks | sandbox_number_checks_daily | 20 a day | midnight |
| Group adds | sandbox_group_adds_daily | 5 a day | midnight |
| API calls | sandbox_api_calls_monthly | 1,500 a month | the first of the month |
What counts:
- Every send counts as a message: texts, contact cards, polls, media, reactions, removed reactions and deletes.
- A chat you already messaged this month does not count against new chats again.
- Group adds count each person added. Creating a group, creating a community and linking a group into a community count as group adds too.
- Every request made with the sandbox channel's token counts as one API call, reads included, whether or not it succeeds.
- Requests WAgate refuses before they run (a 429 from a busy gateway, or from any allowance), requests the console makes for you, messages you receive and webhooks WAgate delivers do not count.
- A request that would go over an allowance is refused whole, before anything is sent.
Reading a 429
Branch on code, never on the status alone.
| `code` | Cause | Wait |
|---|---|---|
rate_limited | a flood guard refused a burst | Retry-After header, then retry |
gateway_busy | too many requests in flight | Retry-After header (1 second, 10 for media), then retry; nothing is queued |
sandbox_allowance_exceeded | a sandbox allowance ran out | Retry-After header, or until resets_at |
daily_send_cap_exceeded | a daily send ceiling is exhausted, or a zero cap stops sends | for a positive ceiling, until resets_at; for cap: 0, contact support |
daily_cap_group_add, daily_cap_contact_write, daily_cap_contact_check | a warm-up protection allowance is used | retry_after in the body, or until resets_at |
history_request_too_soon | history requested too recently | Retry-After header (also retry_after in the body) |
whatsapp_rate_limited | WhatsApp throttled the number | no header; back off, retry later |
Test sends to your webhook receivers (POST webhooks/test) have no cooldown of their own. They count against these limits like any other request.
Example bodies:
{ "code": "rate_limited", "error": "...", "window": "ingress.source", "retry_after": 2 }{ "code": "gateway_busy", "error": "...", "bound": "account", "retry_after": 1 }{
"code": "sandbox_allowance_exceeded",
"error": "...",
"window": "sandbox_messages_daily",
"cap": 180, "used": 180, "reserved": 0,
"resets_at": "2026-09-30T00:00:00+07:00",
"retry_after": 3600
}{
"code": "daily_cap_group_add",
"error": "...",
"class": "group_add", "window": "daily_group_add",
"cap": 5, "sent": 5, "tier": "...",
"resets_at": "2026-09-30T00:00:00+07:00", "retry_after": 3600
}daily_send_cap_exceeded carries window: "daily_send", cap, sent, resets_at and retry_after. An exhausted positive ceiling resets at Bangkok midnight (resets_at). A cap of 0 stops sends and remains zero after midnight: email [email protected] to have it changed. The same code can also mean send counting became unreliable for the rest of the day; inspect the error before retrying. Check the channel's daily_send_cap and daily_send_cap_source if the ceiling is unexpected.
Warm-up protection
An optional switch in the console. With it on, group adds, group and community creation, contact checks and contact saves have their own daily allowances, and a call over the allowance answers 429 daily_cap_<class>. With it off, a paid channel has none of these allowances.
Pace yourself
WAgate imposes no send timing floor on paid channels; the request limits and any send cap described above still apply. A loop that sends as fast as it can is the kind of traffic WhatsApp restricts. Space sends out with random gaps rather than a fixed interval. See Recipes for a paced sender.