Массовая отправка через MAX: как не сломать канал первой же рассылкой
Практический разбор массовой отправки через MAX: очереди, безопасный темп, приоритеты и что считать нормальной инженерной защитой от 429.
Почему рассылка быстро становится сложной
На бумаге массовая отправка через MAX выглядит просто: берёте список получателей и крутите цикл поPOST /messages. В реальности такой подход ломается почти сразу, потому что канал защищает себя от перегрузки, а ваша бизнес-логика редко умеет держать ровный темп сама по себе.
Первая проблема обычно не “слишком мало возможностей MAX”, а отсутствие своей инфраструктуры: очереди, приоритетов, ретраев, дедупликации и нормальной наблюдаемости.
Рабочая архитектура
Для MAX-рассылки не нужен космический стек, но нужен скучный и предсказуемый pipeline:
Рендер схемы...
- Producer формирует сообщение и кладёт его в очередь, а не шлёт сразу в канал.
- Workers держат понятный темп отправки и не превышают безопасный RPS.
- DLQ хранит сообщения, которые не удалось доставить даже после повторов.
- Метрики отвечают на вопрос не “API хороший или плохой”, а “что именно сейчас мешает отправке”.
Приоритеты и очередь
Главная ошибка молодых продуктов это одна очередь на всё подряд. Тогда код подтверждения и маркетинговая рассылка дерутся за один и тот же канал.
| Тип сообщения | Приоритет | Что делать |
|---|---|---|
| OTP и безопасность | P0 | Пускать первыми и не смешивать с маркетингом |
| Транзакционные статусы | P1 | Держать в отдельной очереди или как минимум выше промо |
| Продуктовые напоминания | P2 | Можно терпеть умеренную задержку |
| Промо и массовые кампании | P3 | Давить последними и только после контроля темпа |
const queueMessage = {
chatId: "1234567890",
message: "Ваш заказ отправлен",
priority: "P1",
clientId: "order_1001_sent",
};Даже если у вас всего один worker, такое разделение уже спасает от самых неприятных продуктовых эффектов.
Retry и DLQ
В рассылке нельзя опираться на “один запрос = одна доставка”. У вас будут 429, 5xx, сетевые сбои и отдельные невалидные адресаты. Поэтому контур без retry и DLQ это не архитектура, а временная удача.
async function processJob(job) {
try {
return await maxClient.send(job.payload);
} catch (error) {
if ([429, 500, 502, 503, 504].includes(error.status)) {
return queue.retry(job, computeBackoff(job.attempt));
}
return dlq.push({
...job,
reason: error.status ?? "unknown_error",
});
}
}DLQ нужен не только чтобы “не потерять” сообщения. Он нужен, чтобы команда видела, где у рассылки системная проблема: токены, формат, канал, конкретный тип пользователей или бизнес-логика.
Когда лучше не писать всё самому
Если вы запускаете одну внутреннюю кампанию, свой sender может быть оправдан. Но как только появляется несколько сценариев, приоритеты, CRM, операторы и соседние каналы, стоимость собственной инфраструктуры начинает расти быстрее, чем кажется в начале.
- Нужно быстро соединить MAX с Telegram, VK или резервным SMS.
- Нужны шаблоны, история отправки и статусы без отдельного internal tooling.
- Команда не хочет поддерживать очереди и лимиты как отдельный мини-продукт.
В таком случае проще использовать готовую платформу вроде Relaya и оставить за собой только продуктовую логику. Если же вы строите всё сами, следующая статья по цепочке это разбор лимитов и работа с 429.
Массовая отправка ломается не потому, что “канал плохой”, а потому, что сообщение становится уже не просто сообщением, а задачей доставки. А задачи доставки любят очереди, приоритеты и дисциплину.
Создайте бесплатный MAX-профиль
Если хочется не просто читать, а сразу проверить сценарий руками: подключите MAX, отправьте себе тестовое сообщение и уже потом решайте, нужны ли другие каналы.