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:
Branch on the field, never on the wording
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 four of these — on success as well as 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.
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