Skip to content

Rate limits and errors

Read the quota headers on every API response, handle 429s correctly, and understand the error format and the codes you are most likely to meet.

The public API limits how many requests you can make, and tells you how much you have left on every response. When something goes wrong, every error has the same shape, so your code can switch on one field.

Request quotas

Requests are counted in fixed windows per minute and per day, both per API key and per organisation. The organisation's limit is the ceiling: all your keys together can never exceed it, whatever a single key's own limit says.

The limits depend on your organisation's settings. Read them from the headers rather than hard-coding them. Most requests count as one; a media upload (POST /media) counts as five.

Headers on every response

HeaderMeaning
RateLimit-LimitThe quota of the window closest to running out
RateLimit-RemainingRequests left in that window
RateLimit-ResetSeconds until that window resets (a number of seconds, not a timestamp)
RateLimit-PolicyEvery window that applies, for example "key-minute";q=120;w=60, "key-day";q=100000;w=86400, …
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-ResetThe same numbers, under the names many client libraries look for

In RateLimit-Policy, q is the quota and w is the window length in seconds.

Two different 429s

codeWhat it meansWhat to do
quota_exceededYour quota for a window is used up. details.scope says whether it was the key's (key) or the organisation's (tenant).Wait for the number of seconds in Retry-After, which is when the window resets.
rate_limitedYou are sending bursts faster than the platform accepts. Retry-After is about 1 second. This answer has no RateLimit-* headers.Pause briefly and retry. Spread your requests out.

A media URL import can also give 429 rate_limited with details.reason: media_import_capacity when your organisation already has 2 imports running.

Tip: Watch RateLimit-Remaining and slow down before it reaches zero. You can see your usage over time in API usage.

Plan allowances (402)

Separately from these short windows, your plan may cap API requests or messages sent per billing period. When such an allowance is used up, requests are refused with 402 usage_limit_reached. details says which meter (api_requests or messages_sent), how much is used, the allowance and the period_end. Retrying does not help. A plan change does. See Usage.

The error format

Every error response has this body, never nested under an error key:

json
{
  "code": "validation_failed",
  "message": "A human-readable explanation.",
  "request_id": "01J9…",
  "details": {
    "fields": [{ "field": "text.body", "message": "…" }]
  }
}
  • code: switch on this. It is a fixed list and won't change meaning.
  • message: for people. It may change between releases, so don't parse it.
  • request_id: the same as the X-Request-Id response header. Quote it when you contact support.
  • details: extra context for some codes, such as the fields that failed validation or the permission that was missing.

Status codes

StatusCommon code valuesMeaning
200 / 201 / 202Success. 202 on a send means accepted for delivery, not yet delivered.
400validation_failed, media_too_large, unsupported_message_typeSomething in the request is wrong. details.fields names each problem.
401unauthenticatedNo valid API key. See Authenticate API requests.
402plan_feature_unavailable, plan_limit_reached, subscription_restricted, usage_limit_reached, wallet_balance_lowYour plan, subscription or WhatsApp credit balance stops this. No key or permission can lift it.
403permission_deniedThe key lacks the permission this endpoint needs. details.permission names it.
404not_foundNo such item in your organisation. Items of other organisations give the same answer.
409window_expired, template_required, template_not_approved, contact_opted_out, duplicate_request, media_unavailable, conflictThe request is valid but the current state refuses it.
413request_too_largeThe request body is too large.
429quota_exceeded, rate_limitedToo many requests. See above.
503unavailableThe service could not answer right now. Retry.
codeMeaningUseful details
plan_feature_unavailableYour plan does not include this feature.feature, plan, upgrade_plan
plan_limit_reachedA count limit of your plan is reached.entitlement, limit, used
subscription_restrictedYour subscription is restricted or cancelled, so template sends are paused. Replies inside the 24-hour window still work.status, action
usage_limit_reachedA plan allowance is used up for the billing period.meter, used, allowance, period_end
wallet_balance_lowYour WhatsApp credit balance cannot cover a template message of this category. Replies inside the 24-hour window are never refused for this.balance_minor, required_minor, wallet_category, top_up_path

See Plans and trial and WhatsApp credits.

Good habits

  • Retry safely. On a timeout or a 503, retry with backoff. For POST /messages, reuse the same Idempotency-Key so the customer never gets a message twice.
  • Don't retry 4xx answers unchanged, except 429 after waiting Retry-After.
  • Log request_id with every error.

If a message was accepted but never arrived, see Messages not delivered.

Still need help?

Write to us at [email protected] and tell us what you were trying to do. Already a customer? Sign in to IconEngage — the same articles are under Help in the app.