Ошибка 429 в Telegram — причины и стратегии обхода
Почему Telegram возвращает 429 Too Many Requests и как правильно реализовать backoff, очереди и rate limiting.
Что такое ошибка 429
HTTP-статус 429 Too Many Requests — стандартный ответ сервера, означающий, что клиент отправил слишком много запросов за определённый период. В контексте Telegram Bot API это одна из самых частых ошибок при масштабировании ботов.
Когда бот превышает допустимую частоту запросов, Telegram возвращает JSON с описанием ошибки:
{
"ok": false,
"error_code": 429,
"description": "Too Many Requests: retry after 5",
"parameters": {
"retry_after": 5
}
}Ключевое поле — parameters.retry_after. Оно указывает, через сколько секунд можно повторить запрос. Игнорирование этого поля — прямой путь к временной блокировке бота. Подробнее о всех лимитах Telegram читайте в статье Telegram API лимиты в 2026 году.
Типичные причины появления
Ошибка 429 возникает в разных сценариях. Вот наиболее частые:
| Сценарий | Причина | Решение |
|---|---|---|
| Массовая рассылка | Отправка >30 сообщений/сек | Rate limiter + очередь |
| Флуд в одну группу | >20 сообщений/мин в один чат | Агрегация сообщений |
| Частый polling | getUpdates без таймаута | Long polling с timeout=30 |
| Параллельные запросы | Несколько воркеров без координации | Централизованный rate limiter |
| Редактирование сообщений | Частое обновление progress bar | Батчинг обновлений (раз в 3–5 сек) |
| Множество ботов на одном IP | IP-level rate limiting | Распределение по IP-адресам |
| Webhook + sendMessage | Ответ в webhook + параллельная отправка | Выбрать один способ отправки |
Частая ошибка: разработчики не учитывают, что лимит в 30 msg/sec — суммарный для всех операций бота. Если вы одновременно отправляете сообщения, редактируете и удаляете — все эти операции учитываются вместе.
Заголовок Retry-After
Telegram включает информацию о необходимой паузе в тело ответа (полеparameters.retry_after) и иногда в HTTP-заголовок Retry-After. Всегда используйте значение из тела ответа — оно более надёжно.
Типичные значения Retry-After:
- 1–5 секунд — лёгкое превышение, обычно при рассылке
- 10–30 секунд — серьёзное превышение или повторное нарушение
- 60–300 секунд — бот попал под усиленный мониторинг
- Более 300 секунд — риск временной блокировки
Правило простое: всегда уважайте Retry-After. Если вы продолжаете отправлять запросы во время паузы, Telegram увеличит время блокировки экспоненциально.
Экспоненциальный backoff
Экспоненциальный backoff — базовая стратегия обработки 429. Идея проста: при каждой повторной ошибке увеличиваем время ожидания в 2 раза.
// Экспоненциальный backoff для Telegram Bot API
async function sendWithBackoff(token, chatId, text, maxRetries = 5) {
let baseDelay = 1000; // начальная задержка 1 сек
for (let attempt = 0; attempt < maxRetries; attempt++) {
const res = await fetch(
`https://api.telegram.org/bot${token}/sendMessage`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ chat_id: chatId, text }),
}
);
if (res.ok) {
return await res.json();
}
if (res.status === 429) {
const body = await res.json();
const retryAfter = body.parameters?.retry_after || 1;
// Берём максимум из retry_after и экспоненциальной задержки
const delay = Math.max(retryAfter * 1000, baseDelay);
// Добавляем jitter (±20%) для предотвращения «стада»
const jitter = delay * (0.8 + Math.random() * 0.4);
console.warn(
`[429] Попытка ${attempt + 1}/${maxRetries}, retry_after=${retryAfter}s, ждём ${Math.round(jitter)}ms`
);
await new Promise((r) => setTimeout(r, jitter));
baseDelay *= 2; // удваиваем базовую задержку
continue;
}
// Другие ошибки — не ретраим
throw new Error(`Telegram error: ${res.status}`);
}
throw new Error(`Не удалось отправить за ${maxRetries} попыток`);
}Зачем нужен jitter
Без jitter (случайного разброса) все воркеры, получившие 429, попробуют повторить запрос одновременно — и снова получат 429. Это называется «thundering herd problem». Добавление ±20% случайного разброса решает проблему.
Очередь с повторной отправкой
Для production-систем экспоненциальный backoff недостаточен. Нужна полноценная очередь с управлением повторными попытками. Подробную архитектуру описываем в статье массовая отправка в Telegram.
// Простая очередь отправки с retry для Telegram
class TelegramSendQueue {
constructor(token, rateLimit = 25) {
this.token = token;
this.rateLimit = rateLimit;
this.queue = [];
this.dlq = []; // Dead Letter Queue
this.processing = false;
this.stats = { sent: 0, retried: 0, failed: 0 };
}
add(chatId, text, maxRetries = 3) {
this.queue.push({ chatId, text, retries: 0, maxRetries });
if (!this.processing) this.process();
}
async process() {
this.processing = true;
const interval = 1000 / this.rateLimit;
while (this.queue.length > 0) {
const task = this.queue.shift();
try {
await this.send(task.chatId, task.text);
this.stats.sent++;
} catch (err) {
if (err.retryAfter && task.retries < task.maxRetries) {
task.retries++;
this.stats.retried++;
// Возвращаем в очередь с задержкой
setTimeout(() => this.queue.push(task), err.retryAfter * 1000);
} else {
this.stats.failed++;
this.dlq.push({ ...task, error: err.message, failedAt: new Date() });
}
}
await new Promise((r) => setTimeout(r, interval));
}
this.processing = false;
}
async send(chatId, text) {
const res = await fetch(
`https://api.telegram.org/bot${this.token}/sendMessage`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ chat_id: chatId, text }),
}
);
if (res.status === 429) {
const body = await res.json();
const err = new Error("Rate limited");
err.retryAfter = body.parameters?.retry_after || 5;
throw err;
}
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
getStats() {
return {
...this.stats,
pending: this.queue.length,
deadLetters: this.dlq.length,
};
}
}
// Использование
const queue = new TelegramSendQueue("BOT_TOKEN");
userIds.forEach((id) => queue.add(id, "Ваш заказ отправлен!"));
// Проверка статистики
setInterval(() => console.log(queue.getStats()), 5000);Алгоритм Token Bucket
Token Bucket — элегантный способ контролировать скорость отправки. Идея: есть «ведро» с токенами, которое пополняется с фиксированной скоростью. Каждая отправка «тратит» один токен. Если токенов нет — ждём.
// Token Bucket rate limiter
class TokenBucket {
constructor(capacity, refillRate) {
this.capacity = capacity; // макс. токенов
this.tokens = capacity; // текущие токены
this.refillRate = refillRate; // токенов в секунду
this.lastRefill = Date.now();
}
async acquire() {
this.refill();
if (this.tokens >= 1) {
this.tokens -= 1;
return true;
}
// Ждём пока появится токен
const waitTime = (1 / this.refillRate) * 1000;
await new Promise((r) => setTimeout(r, waitTime));
this.refill();
this.tokens -= 1;
return true;
}
refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(
this.capacity,
this.tokens + elapsed * this.refillRate
);
this.lastRefill = now;
}
}
// Использование: 25 токенов, пополнение 25/сек
const limiter = new TokenBucket(25, 25);
async function sendLimited(chatId, text) {
await limiter.acquire(); // ждёт, если лимит исчерпан
return sendTelegramMessage(chatId, text);
}Совет: Token Bucket лучше подходит для плавного трафика. Для burst-трафика (пик при массовой рассылке) используйте комбинацию Token Bucket + очередь. В Релая именно такая архитектура используется по умолчанию.
Мониторинг ошибок 429
Без мониторинга вы не узнаете о проблемах, пока пользователи не начнут жаловаться. Вот ключевые показатели, которые нужно отслеживать:
| Показатель | Что показывает | Порог алерта |
|---|---|---|
| 429 errors / minute | Частота rate limiting | > 5/мин |
| Avg retry_after | Серьёзность превышения | > 10 секунд |
| Queue depth | Размер очереди (backpressure) | > 1000 сообщений |
| DLQ size | Недоставленные сообщения | > 0 |
| Success rate | Процент успешных доставок | < 99% |
| P95 delivery time | Время доставки с учётом retry | > 30 секунд |
// Статистика доставки — пример dashboard API
const stats = await fetch('/api/stats/delivery');
// {
// "requests_total": { "success": 25340, "rate_limited": 45 },
// "retry_after_p95_sec": 3,
// "queue_depth": 127,
// "dlq_size": 2,
// "delivery_duration_p95_sec": 1.2
// }Настройте алерты в дашборде или аналогичной системе. Если процент 429 превышает 1% от общего количества запросов — пора пересмотреть архитектуру отправки.
Как Релая устраняет проблему 429
Релая полностью берёт на себя управление rate limiting для Telegram и других каналов. Вот как это работает:
- Адаптивный rate limiter: платформа автоматически подстраивает скорость отправки под текущие лимиты Telegram, учитывая возраст бота и историю нарушений
- Распределённая очередь: сообщения хранятся в надёжной очереди с гарантией доставки и повторными попытками
- Автоматический backoff: при получении 429 платформа приостанавливает отправку на нужное время без потери сообщений
- DLQ и алерты: если сообщение не удалось доставить после всех попыток, оно попадает в DLQ с уведомлением
- Мультиканальное резервирование: при серьёзных ограничениях Telegram можно автоматически переключиться на MAX — где лимиты более предсказуемы
Результат: при использовании Релая вероятность столкнуться с 429 существенно снижается. Платформа проактивно управляет скоростью отправки, а при необходимости использует MAX как запасной канал — растущий мессенджер с лояльной российской аудиторией.
Если вы столкнулись с ошибкой 429 и хотите стабилизировать отправку, попробуйте Релая. Также можно прочитать наши статьи о лимитах Telegram API и массовой отправке без блокировок.
Создайте бесплатный MAX-профиль
Если хочется не просто читать, а сразу проверить сценарий руками: подключите MAX, отправьте себе тестовое сообщение и уже потом решайте, нужны ли другие каналы.