Формат
Успех — 200 и JSON. Ошибка — JSON с текстом и кодом:
{"message": "Текст длиннее 4000 символов", "code": "text_too_long"}
Если запрос можно повторить позже, в ответе есть retryAfter (секунды) и заголовок Retry-After:
{"message": "Создавать группы можно не чаще раза в 5 минут", "code": "too_frequent", "retryAfter": 214}
Опирайтесь на code: текст message написан для человека и может меняться.
Коды
| HTTP | code |
Что случилось | Что делать |
|---|---|---|---|
400 |
validation |
нет обязательного поля, неверный тип, неверный JSON, адрес чата или idMessage не этого канала |
исправить запрос |
400 |
not_authorized |
канал не подключён к мессенджеру | проверить getStateInstance, привязать номер в кабинете |
400 |
no_account |
на номере нет аккаунта в этом мессенджере | — |
400 |
bad_phone |
номер не из 11–12 цифр | исправить номер |
400 |
bad_url |
ссылка не http(s) или ведёт во внутреннюю сеть |
дать публичную ссылку |
400 |
url_unavailable |
файл по ссылке не скачался | проверить ссылку |
400 |
file_empty, upload_failed |
файл пустой или площадка его не приняла | — |
413 |
— | тело запроса больше 100 МБ — отклонено до обработки | уменьшить файл |
400 |
text_too_long |
текст длиннее предела канала | разбить текст |
400 |
webhook_set |
receiveNotification, когда у канала задан вебхук |
читать вебхук или снять его |
400 |
код площадки (chat.not.found, privacy_restricted…) |
площадка отказала в действии | см. message |
401 |
— | неверный idInstance или токен; ключ перевыпущен или отозван |
взять ключ в кабинете |
402 |
payment_required |
канал на паузе за неоплату, закрыта отправка | продлить канал в кабинете |
403 |
reserved |
попытка изменить системную подписку Gratio | — |
404 |
— | неизвестный метод | проверить имя метода |
409 |
idempotency_conflict |
clientMessageId уже использован для другого содержимого |
новый ключ для нового сообщения |
409 |
busy |
на канале уже идёт разговор через API | дождаться конца звонка |
409 |
limit |
у канала уже 5 подписок | удалить ненужную |
429 |
too_frequent и другие |
темп канала: залп сверх правил площадки | повторить после Retry-After |
429 |
— | больше 30 запросов в секунду с одного адреса | снизить частоту |
469 |
rate_limited |
мессенджер ограничил поиск номеров или вступления | повторить после Retry-After |
500 |
internal |
сбой Gratio | повторить позже; с clientMessageId повтор безопасен |
500 |
too_large |
тело JSON больше 100 КБ | уменьшить тело, файлы — через uploadFile |
501 |
not_supported |
канал этого не умеет | getCapabilities, см. Возможности каналов |
503 |
unavailable |
канал сейчас не обслуживается, сообщение площадке не отправлялось | повторить после Retry-After |
503 |
outcome_unknown |
связь оборвалась посреди отправки, дошло ли сообщение — неизвестно | повторить с тем же clientMessageId |
Отказ с паузой всегда приходит как 429 или 469 с Retry-After. Отказ, который повтор не исправит, — как 400 с причиной в code.
Свои коды есть у отдельных методов и каналов — они описаны в статьях WhatsApp, Поиск и вступление: правила и лимиты, Звонки: как это работает и sendEmail.
Групповые методы
Методы групп, как у Green API, на отказ площадки отвечают 200 с флагом false и причиной:
{"addParticipant": false, "reason": "privacy_restricted: пользователь запретил приглашения"}
Проверяйте флаг в ответе, а не только код HTTP.
Когда повторять
| Ответ | Повторять? |
|---|---|
429, 469, 503 unavailable |
да, после Retry-After |
500 internal, сетевой сбой, тайм-аут |
да, с тем же clientMessageId для отправки |
503 outcome_unknown |
да, только с тем же clientMessageId; не отправлять то же в другой канал, пока нет итога |
400, 401, 402, 404, 409, 501 |
нет — сначала исправить причину |
Подробно о повторах — в статье Надёжная отправка без дублей.
Обработка в коде
Одна функция-обёртка: повторяет то, что можно повторить, и отдаёт причину остального.
Python
import os
import time
import uuid
import requests
BASE = f"{os.environ['GRATIO_URL']}/waInstance{os.environ['GRATIO_ID']}"
TOKEN = os.environ["GRATIO_TOKEN"]
RETRY = {429, 469, 500, 502, 503, 504}
class GratioError(Exception):
def __init__(self, status: int, code: str, message: str):
super().__init__(f"{status} {code}: {message}")
self.status, self.code = status, code
def call(method: str, payload: dict, attempts: int = 5) -> dict:
for attempt in range(attempts):
try:
r = requests.post(f"{BASE}/{method}/{TOKEN}", json=payload, timeout=90)
except requests.RequestException:
time.sleep(2 ** attempt) # сеть: повтор с паузой
continue
if r.status_code == 200:
return r.json()
body = r.json() if r.content else {}
if r.status_code in RETRY and attempt < attempts - 1:
time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
continue
raise GratioError(r.status_code, body.get("code", ""), body.get("message", ""))
raise GratioError(0, "network", "нет связи с API")
# у отправки — ключ от дублей, тогда повтор безопасен
call("sendMessage", {"chatId": "79991234567@c.us", "message": "Здравствуйте!",
"clientMessageId": str(uuid.uuid4())})
Node.js
const { GRATIO_URL, GRATIO_ID, GRATIO_TOKEN } = process.env;
const base = `${GRATIO_URL}/waInstance${GRATIO_ID}`;
const RETRY = new Set([429, 469, 500, 502, 503, 504]);
const sleep = (ms) => new Promise((ok) => setTimeout(ok, ms));
export async function call(method, payload, attempts = 5) {
for (let attempt = 0; attempt < attempts; attempt++) {
let r;
try {
r = await fetch(`${base}/${method}/${GRATIO_TOKEN}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(90_000),
});
} catch {
await sleep(1000 * 2 ** attempt); // сеть: повтор с паузой
continue;
}
if (r.ok) return r.json();
const body = await r.json().catch(() => ({}));
if (RETRY.has(r.status) && attempt < attempts - 1) {
await sleep(1000 * Number(r.headers.get("retry-after") ?? 2 ** attempt));
continue;
}
throw Object.assign(new Error(`${r.status} ${body.code}: ${body.message}`), body);
}
throw new Error("нет связи с API");
}
await call("sendMessage", {
chatId: "79991234567@c.us",
message: "Здравствуйте!",
clientMessageId: crypto.randomUUID(),
});
Что дальше
- Темп и лимиты — откуда берутся
429и469. - Надёжная отправка без дублей — почему повтор отправки безопасен с
clientMessageId.
