Waiting, errors and retries
A send is answered at once with 202 and status: "queued". The message then goes out the way a person would send it, so it may wait. A rule either holds it (it stays queued and goes by itself) or fails it (status: "failed", with the reason in error). This page says which is which, how long a wait can last, and what your code should try again.
Where to see why
- A failed message says why in its
error, in plain words: inGET /v1/messages/{id}, in themessage.statuswebhook, and in your panel under the message in its chat and in the Message log (under Developers). Itserror_codesays the same as a fixed value for your code: see Error codes. - A waiting message stays
queueduntil it goes, with noerror. Itserror_codenames what it is waiting for, and isnullagain once it has gone.GET /v1/instances/{id}shows where the instance stands today underprotection: the day's limits and how much of them is used, itstimezone, and undernew_chatswhether new chats are paused, why and until when.
What holds a message
A held message needs nothing from you. It goes when its turn comes.
| Why it waits | For how long |
|---|---|
| Its turn. One instance sends one message at a time, with a gap that depends on who it is for (see the pace) | seconds; for someone new, up to a minute, and 5 to 10 minutes after every tenth new person |
| Night. Nobody who never wrote is messaged from 22:00 to 08:00 in the instance's time zone (or the quiet hours you set) | until the morning |
| One person, many messages. At most 4 an hour go to someone who has not answered, 10 while they are chatting with you | until the hour has passed, or they write |
| An earlier message to the same chat is still waiting. Messages to one person go in the order you sent them | until that one has gone |
| One group, many messages: at most 20 an hour to one group | until the hour has passed |
| The instance is reconnecting to WhatsApp | a minute at a time; one that stays disconnected fails the message |
| WhatsApp is limiting the instance, or has just refused one of its messages. Only replies go meanwhile: everything else waits | until it ends (at least an hour after a refusal), when that is within 6 hours; a longer one fails the message with the reason |
A file given by url whose server did not answer, or answered with a server error |
tried 3 times, 2 minutes apart, then it fails |
What fails a message
A failed message will not go by itself. Read error: it says what to do.
- The instance cannot send: it is not connected, it is stopped, the account is suspended, or a different phone was linked after the message was queued.
- The person: they wrote "STOP", or their number is not on WhatsApp.
- The day's limits: the instance's new people for the day are used up, or its warm-up's daily messages that are not replies, or its group messages (200 a day) or channel posts (30 a day). The reason says "Try again tomorrow".
- Someone new who has not answered: more than 2 follow-ups in a day, or a new first message before 7 days have passed. The reason gives the date it may go.
- What a first message holds: a location, a contact card, a sticker or a poll; an
http://link, or only a link; more than 600 characters; or a text or file that already opened 5 chats that day. - New chats are paused: by WhatsApp for longer than 6 hours, by us, or because almost nobody answers the instance's first messages. Replies still go.
- The file: it could not be fetched, is too large, or a sticker is not a WebP image.
- WhatsApp refused it, or did not confirm it in time. In the second case it may have been delivered: look at the chat before sending it again.
- An OTP code that cannot go within 2 minutes fails at once: a late code is useless.
- You cancelled it before it went.
The rules themselves are in How sending protects your number.
A message's error codes
error is written for people, and its words may change. error_code is for your code: one fixed value per reason, on the message in the API and in the message.status event. On a failed message it is why it failed. On a queued one it is what the message is waiting for, and no event is sent for a wait. A message that failed before codes were added has none. New values may be added, so treat one you do not know like error alone.
| Code | What it means | The message |
|---|---|---|
instance_not_connected |
The instance is not connected to WhatsApp | fails |
instance_stopped |
The instance is stopped. Start it to send | fails |
instance_reconnecting |
The instance is reconnecting to WhatsApp | waits |
number_on_another_instance |
The WhatsApp number scanned belongs to another instance, so it was not linked here | fails |
account_suspended |
The account is suspended | fails |
phone_changed |
A different phone was linked after the message was queued or scheduled | fails |
opted_out |
The person wrote "STOP" or similar | fails |
opt_out_unconfirmed |
WhatsApp could not say whether this person is one who opted out | waits |
not_on_whatsapp |
The phone number is not on WhatsApp | fails |
no_number |
The message was addressed by a private chat id, the person's number is no longer known, and they have not written lately | fails |
quiet_hours |
It is night where the instance is, and nobody new is messaged at night | waits |
pacing |
The gap before the instance's next message | waits |
earlier_message_waiting |
An earlier message to the same chat goes first | waits |
per_minute_limit |
The instance's ceiling of messages a minute | waits |
per_person_limit |
Several messages to this person in the last hour went unanswered | waits |
per_group_limit |
Many messages went to this group in the last hour | waits |
daily_new_people_limit |
The instance's new people for the day are used up | fails |
daily_lookup_limit |
The instance checked as many new numbers on WhatsApp as it may today | fails |
daily_volume_limit |
The day's messages that are not replies are used up: warm-up, or your own cap | fails |
daily_group_limit |
The day's group messages are used up | fails |
daily_channel_limit |
The day's channel posts are used up | fails |
unanswered_follow_ups |
The person has not answered, and the day's follow-ups to them are used up | fails |
not_answered_yet |
The person has not answered the last message; error gives the date they may be written to again |
fails |
first_message_type |
A first message cannot be a location, a contact card, a sticker or a poll | fails |
first_message_too_long |
A first message is longer than allowed | fails |
insecure_link |
A message to someone new carries an http:// link |
fails |
link_only |
A first message is only a link | fails |
same_first_message |
The same text, or file and caption, already opened the day's allowed chats | fails |
new_chats_paused |
We paused new chats from the instance. Replies still go | fails |
few_replies |
Almost nobody answers the instance's first messages, so new chats are paused | fails |
whatsapp_restricted |
WhatsApp is limiting the instance, or has just refused one of its messages | waits |
whatsapp_quota |
The instance is near or at WhatsApp's quota of new chats | waits |
whatsapp_refused |
WhatsApp refused the message. Sending it again will not help | fails |
whatsapp_unreachable |
WhatsApp did not answer | waits |
may_have_been_delivered |
WhatsApp did not confirm it in time. Check the chat before sending it again | fails |
file_unavailable |
The file could not be fetched from its url, or is too large |
waits |
file_missing |
The message's file is no longer stored with us | fails |
invalid_sticker |
A sticker must be a WebP image of at most 1 MB | fails |
file_too_large |
On a message you received: its file was over 100 MB, so we did not keep it | stays received, with no media |
otp_too_late |
An OTP's message could not go within its short window | fails |
otp_ended |
The OTP ended before its code went | fails |
broadcast_stopped |
Its broadcast held it back: paused, cancelled, stopped for safety, or the message could not be queued. The person's turn comes again | fails |
warmup_stopped |
Automatic warm-up stopped before the line went | fails |
cancelled |
You cancelled it before it went | fails |
not_queued |
We could not queue it. Send it again | fails |
A message that "waits" fails with the same code when the wait cannot end in time: see The longest wait. A request the API refuses outright (409, 422, 429) makes no message, so it has no error_code: read its message.
The longest wait
A message waits at most 24 hours, counted from when you sent it, or from its send_at if you scheduled it. A hold that would last to within 10 minutes of that is not started: the message fails then, and its error names what held it, usually ending "It waited as long as it could, so it was not sent. Send it again if it is still wanted." One still queued when the 24 hours end fails with "Could not reach WhatsApp. Please try again.", or with a note that it may have been delivered.
The pace
Each instance sends one message at a time. After any message, the next one waits a gap, picked at random, by who it is for:
| The next message is for | Gap before it |
|---|---|
| Someone who wrote in the last 24 hours (a reply) | 2 to 8 seconds |
| Someone who wrote before that, a group or a channel | 8 to 20 seconds |
| Someone who never wrote | 30 to 60 seconds after the last first message, and a break of 5 to 10 minutes after every 10 |
A message to a person or a group also shows "typing…" for 1 to 4 seconds first. Above all of this there is a ceiling of 20 messages a minute per instance. So 100 replies take ten to fifteen minutes. 100 first messages take days: a newly linked instance may message 10 new people a day, rising to 50 over its first two weeks.
Different instances send at the same time; one does not slow another.
Do not send faster than an instance sends. The API takes 600 requests a minute, and an instance sends about 20 messages a minute, so a burst only makes a queue. Once an instance has more than 500 messages waiting or scheduled, POST /v1/messages to it answers 429 with the code backlog_full and a Retry-After: send the rest as these go out. Messages scheduled with send_at count like the waiting ones. Replies to people who wrote in the last 24 hours are always taken. For one message to many people, use a broadcast, which paces itself.
What your code should retry
A request that was refused (no message was made):
| Status | Retry? |
|---|---|
409 |
Only when there is a Retry-After header: wait that long, then send the same request. Without one, read message: for example, the instance is not connected, or is stopped. |
429 |
Yes, after Retry-After. It is the request limit (600 a minute per account), an instance's full backlog, or a plan or safety limit that message names. |
502, 503 |
Yes. WhatsApp did not answer, or we could not take the request just now. Wait for Retry-After when present; otherwise a few seconds, then longer each time. |
401, 403, 404, 422 |
No. Fix the key, the id or the request first. |
Send the same Idempotency-Key on every retry of one message, so two answers to one request can never become two messages. POST /v1/otps and POST /v1/broadcasts take the header too.
A message that was accepted (202) needs no retry while it is queued: waiting is normal.
A message that failed: never send it again in a loop. Repeating a refused message is the pattern WhatsApp bans, and the same reason will refuse it again. Read error and act on it: wait for tomorrow, let the person write first, fix the file. When you do send it again, use a new Idempotency-Key: the old key returns the failed message.
The API's error codes
A refused request carries a code beside its message. We may improve a message's words; a code never changes. So let your program act on the code, and show or log the message.
| Code | Meaning |
|---|---|
unauthenticated |
No API key was sent, or it was revoked. |
key_not_allowed |
The key was made for less: it is read-only or send-only, or for one instance. |
ip_not_allowed |
The key is locked to IP addresses, and the request came from another. |
account_suspended |
The account is paused or suspended; message says why. |
plan_limit |
The plan's instances, messages, conversations or API requests are used up. |
feature_not_included |
Your plan, or its free trial, does not include this feature; message names it. |
instance_not_found |
No instance with this id on your account. |
message_not_found |
No message with this id on your account. |
chat_not_found |
The instance has no chat with this person, or does not know them. |
group_not_found |
No group with this id. |
channel_not_found |
No channel with this id. |
broadcast_not_found |
No broadcast with this id on your account. |
otp_not_found |
No OTP with this id on your account. |
status_not_found |
No status with this id on the instance. |
not_found |
No such address, or nothing else with this id. |
not_connected |
The instance is not connected to WhatsApp: stopped, disconnected or not linked yet. |
link_refused |
The phone scanned belongs to another instance. Unlink to start again. |
whatsapp_restricted |
WhatsApp is restricting the number, and this would make it worse. Wait until it lifts. |
new_chats_paused |
New chats are paused on the instance, so it cannot reach new people now. |
whatsapp_refused |
WhatsApp refused the request itself; message says what it needs. |
in_progress |
Something else is under way on the instance or the account. Try again in a few seconds. |
conflict |
Not possible in the state this is in, such as cancelling a message that has gone. |
validation_failed |
Something in the request is wrong; errors names each field. |
wrong_code |
The OTP code is not right; attempts_left says how many tries remain. |
no_attempts_left |
The OTP has no attempts left. Start a new one. |
rate_limited |
Too many requests from the account in a minute. |
limit_reached |
One endpoint's own limit for the instance, key or phone, by the minute, hour or day; message says which. |
backlog_full |
The instance already has too many messages waiting or scheduled. Send more as they go out. |
file_budget_used |
The account's inline files for the day are used. Send files by url, or wait for Retry-After. |
busy |
The instance is answering other requests that read from WhatsApp. Try again in a few seconds. |
whatsapp_unavailable |
WhatsApp did not answer. Try again. |
unavailable |
We could not take the request just now. Try again. |
person_unknown |
WhatsApp could not tell us the number behind a private chat_id just now, so nothing was sent. Try again. |
server_error |
Something failed on our side. Try again, and tell us if it stays. |
bad_request |
The request could not be read, such as a method the address does not take. |