details object with extra machine-readable context — for example retry_after_seconds on a rate limit. Where that happens, it is noted below.
Every response, success or failure, carries a request_id. Log it. It is the one thing that lets support find your exact request.
Handle these three first
Almost every integration only really needs to get three cases right:409 instance_not_connected
Stop and alert a human. Retrying cannot help — somebody has to re-scan the QR. What to do
429 rate_limit_exceeded
Wait, then retry. The
Retry-After header tells you how many seconds.422 invalid_recipient
Skip this recipient. The number is not usable on WhatsApp. Do not retry it.
Full error reference
Authentication — 401
If you drive the instance endpoints from a signed-in portal session instead of a key, you may also see
missing_session and invalid_session. Those never appear on the apikey path described here.Billing and access — 402, 403
The request itself — 400, 404, 413, 422
Connection — 409
Rate limit — 429
Our side — 500, 502, 503
This page covers every error reachable through the public API described on this site. The portal has some additional administrative error codes that no API key can trigger; they are deliberately not listed here.
Telling the two number limits apart
instance_limit_reached covers two different situations, on purpose — to you both mean “you cannot add another number”. But the remedies differ, so details.limit_source tells you which one you hit:
What each plan allows is on Plans and limits.
Branch on the field, never on the wording
details.limit_source also appears on admin_free_slot_limit_reached, where it reads free_slots. That code only ever reaches a platform operator’s own account, so a customer integration will only see plan or account — but branch with a default case rather than an if/else, and a third value can never surprise you.Rate limits
Sending is limited per minute, per API key.- One allowance is shared by every message type and the number check. It is one budget, not one per endpoint.
- The instance endpoints (
/v1/instances/...) have their own separate allowance of 120 requests per minute — comfortable for polling a QR every couple of seconds — so managing numbers never eats into your sending budget.
The headers
Any request that reaches a rate limiter carries all fourX-RateLimit-* headers — on success as well as on a 429. Retry-After is the exception and appears only on a 429.
Read
X-RateLimit-Limit rather than hardcoding a number — your send allowance comes from your plan and can change without any change to this API. Plans and limits sets out what each plan allows and where to read your own figures.
Requests rejected before the limiter — a missing or invalid key, for instance — carry no X-RateLimit-* headers at all.
About X-RateLimit-Reset
The window is a rolling minute, not a fixed one that empties on the hour. There is no single instant when your count drops to zero, so X-RateLimit-Reset reports the useful thing instead: seconds until the oldest request ages out, which is when X-RateLimit-Remaining next goes up by one.
- If
X-RateLimit-Remainingis above0, you can send now — you do not need to wait for the reset. - If it is
0, waitingX-RateLimit-Resetseconds gets you at least one more request. - On a
429,X-RateLimit-ResetandRetry-Afterare the same number.
A 429 body
Pacing a list
Do not fire your whole list at once and mop up the 429s afterwards. Space the sends out from the start — it is gentler on your allowance and much gentler on your WhatsApp number’s standing.Node.js — respect the headers

