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_limited with a window starting ingress. comes from these.
  • One token may have at most 16 requests in flight at once. Over that, 429 rate_limited with window: "concurrency.token" and a 1 second wait. When the whole API is carrying as much as it will, you get the same 429 with window: "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_busy with bound set to channel, account or pool.
  • Media sends have their own bound: 429 gateway_busy with bound: "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` valueLimitResets
Messages sentsandbox_messages_daily180 a daymidnight
New chatssandbox_chats_monthly10 a monththe first of the month
Number checkssandbox_number_checks_daily20 a daymidnight
Group addssandbox_group_adds_daily5 a daymidnight
API callssandbox_api_calls_monthly1,500 a monththe 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`CauseWait
rate_limiteda flood guard refused a burstRetry-After header, then retry
gateway_busytoo many requests in flightRetry-After header (1 second, 10 for media), then retry; nothing is queued
sandbox_allowance_exceededa sandbox allowance ran outRetry-After header, or until resets_at
daily_send_cap_exceededa daily send ceiling is exhausted, or a zero cap stops sendsfor a positive ceiling, until resets_at; for cap: 0, contact support
daily_cap_group_add, daily_cap_contact_write, daily_cap_contact_checka warm-up protection allowance is usedretry_after in the body, or until resets_at
history_request_too_soonhistory requested too recentlyRetry-After header (also retry_after in the body)
whatsapp_rate_limitedWhatsApp throttled the numberno 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:

json
{ "code": "rate_limited", "error": "...", "window": "ingress.source", "retry_after": 2 }
json
{ "code": "gateway_busy", "error": "...", "bound": "account", "retry_after": 1 }
json
{
  "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
}
json
{
  "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.

Limits · WAgate docs