Limits and errors
Error format
Section titled “Error format”Every error has the same format:
{ "error": "invalid_token", "message": "Texto explicativo em português" }erroris a stable code, insnake_case. Use this field in your code.messageis text for humans and may change. It is in Portuguese.
Status codes
Section titled “Status codes”| Status | Meaning |
|---|---|
400 |
Invalid request data (validation_error, invalid_json, invalid_phone and the media codes) |
401 |
Instance token or account security token missing or invalid |
402 |
Instance suspended or with a pending payment |
403 |
IP not allowed or account blocked |
404 |
Resource not found, such as a webhook delivery or a route that does not exist |
409 |
Operation incompatible with the instance’s current state |
413 |
Request body too large |
415 |
Content type not accepted: send application/json |
422 |
Contact opted out |
429 |
Too many requests |
500 |
Internal error (internal_error) |
The codes for each route are in the error tables of the authentication, connection, sending messages and sending media guides.
On validation_error, the message lists the fields with problems, such as Dados inválidos: to, text.
Rate limit
Section titled “Rate limit”There is a limit of requests per minute per IP (currently 300) and per instance (currently 600). When you exceed it, the API responds 429 with the rate_limited code and the Retry-After header, which carries the number of seconds to wait before trying again.
Consecutive authentication failures from the same IP also lead to 429: after many 401 responses in a minute, calls from that IP are refused until the window passes.
const resposta = await fetch(url, opcoes)if (resposta.status === 429) { const segundos = Number(resposta.headers.get('retry-after') ?? 1) await new Promise((r) => setTimeout(r, segundos * 1000)) // try again}Message sending also goes through a queue with a controlled pace, to protect the number. That is why a 202 does not mean immediate delivery.
| Item | Limit |
|---|---|
| Text of a message | 10,000 characters |
| Media caption | 1,024 characters |
Media as base64 (decoded) |
16 MB |
| Body of a media request | 25 MB |
| Body of other requests | 1 MB |
Phone numbers in POST /contacts/check |
50 per call |
Number health
Section titled “Number health”GET /v1/instances/{instanceId}/health shows the instance’s send and receive counters, so you notice early if the number is sending too much. The response has today and last7Days (each with sent and received), byType and warning, which is "high_send_ratio" when the sent volume is out of proportion to the received volume, or null.
Best practices
Section titled “Best practices”- Make calls from your server and keep the token in an environment variable.
- On a
429, wait for theRetry-Aftertime instead of retrying in a loop. - Do not repeat a send just because you did not see the webhook: check
GET /queueand the delivery history first. - Honor opt-out. People who ask to leave and keep receiving messages tend to report the number.