Legan Studio
Все статьи
~ 14 мин чтения

Рассылки в Telegram-боте без бана: лимиты и правила

Как делать рассылки из Telegram-бота и не получить бан: лимиты Bot API, правильный темп, сегментация и техника отказоустойчивых очередей.

  • Telegram
  • рассылки
  • продажи
  • автоматизация

Массовая рассылка — самая частая причина, по которой бот получает ограничения от Telegram. Платформа жёстко контролирует темп отправки и поведение пользователей: достаточно нескольких жалоб «Report Spam», и бот уезжает в shadow-ban или, в худшем случае, блокируется полностью. Разберём, как правильно строить рассылки, чтобы оставаться в пределах официальных лимитов, какие архитектурные паттерны выдерживают базы в сотни тысяч подписчиков и как не нарушить 152-ФЗ и закон «О рекламе».

Какие лимиты у Bot API

Telegram официально декларирует:

  • не больше 30 сообщений в секунду разным пользователям суммарно от одного бота;
  • не больше 1 сообщения в секунду одному пользователю в личке;
  • не больше 20 сообщений в минуту в одну группу или канал.

На практике это значит, что базу в 100 000 человек теоретически можно разослать за час — если качественно соблюдать темп. Но Telegram отслеживает не только частоту: важно соотношение «отправлено / прочитано / репорт». Если из 1000 сообщений 50 были репорнуты как спам — бот получит ограничения, даже если каждый отдельный запрос укладывался в 30 RPS.

Лимиты не публикуются полностью: часть из них — soft, часть — hard. Hard ограничения возвращают 429 Too Many Requests с заголовком retry_after. Soft — это то, что Telegram применяет молча: shadow-ban, замедление доставки, понижение видимости бота в поиске.

Tier-аккаунты и расширенные лимиты

Для крупных проектов Telegram даёт повышенные лимиты по запросу через @BotSupport. Условия:

  • бот существует не менее 3-6 месяцев;
  • средняя нагрузка стабильно упирается в стандартные лимиты;
  • в боте нет жалоб и нарушений ToS;
  • описан бизнес-кейс с цифрами.

После аппрува можно получить от 100 до 1000+ сообщений в секунду. Но это не «купить лимит» — это исключение для нагруженных продуктов уровня крупных СМИ, маркетплейсов или новостных каналов.

Что такое FloodWait и как с ним работать

FloodWait — это исключение, которое возвращает Bot API при превышении лимитов. В теле ответа есть parameters.retry_after — количество секунд, которые нужно подождать перед повторной попыткой.

Пример в Python с aiogram:

import asyncio
from aiogram import Bot
from aiogram.exceptions import TelegramRetryAfter, TelegramForbiddenError, TelegramBadRequest

async def safe_send(bot: Bot, chat_id: int, text: str, max_retries: int = 3):
    for attempt in range(max_retries):
        try:
            await bot.send_message(chat_id, text)
            return "ok"
        except TelegramRetryAfter as e:
            wait = e.retry_after + 1
            await asyncio.sleep(wait)
        except TelegramForbiddenError:
            return "blocked_by_user"
        except TelegramBadRequest as e:
            if "chat not found" in str(e).lower():
                return "chat_not_found"
            raise
    return "retry_exhausted"

Главное правило: retry_after — это не рекомендация, а обязательство. Если проигнорировать, следующий retry_after будет в 2-3 раза больше, и так до временной полной блокировки бота.

Главный риск — массовые жалобы

Если 1-2% получателей нажимают «Report Spam» в первые минуты после рассылки, бот быстро улетает в ограничения. Сообщения начинают «теряться» без явной ошибки — Bot API возвращает 200, но пользователю ничего не приходит. Это и есть тот самый shadow-ban.

Чтобы этого не происходило, важно:

  • слать только тем, кто сам начал диалог с ботом (Telegram физически не даёт писать тем, кто не нажал /start);
  • давать понятную причину сообщения («вы оставили заявку», «вы подписались на канал»);
  • встроить кнопку «отписаться» в каждое массовое сообщение;
  • сегментировать базу и не слать одинаковый текст всем подряд;
  • следить за метрикой complaint rate и останавливать рассылку при превышении 0.5%.

Согласие на рассылку и 152-ФЗ

В России рассылка рекламы регулируется законом «О рекламе» (ст. 18) и 152-ФЗ «О персональных данных». Базовые требования:

  • нужна прямая выраженная воля на получение рекламы (галочка по умолчанию выключена, отдельный чекбокс под рекламные сообщения);
  • хранение Telegram ID — обработка ПДн, нужна политика конфиденциальности;
  • кнопка «отписаться» должна работать в один клик;
  • хранить лог согласий с timestamp, IP и явной формулировкой согласия.

Штраф ФАС за рассылку без согласия — от 100 000 до 500 000 ₽ за факт. РКН за нарушение 152-ФЗ — отдельно. Подтверждать согласие лучше через бота: пользователь жмёт /start, бот показывает тариф/услугу и кнопку «Получать рассылку», бот пишет в БД флаг и timestamp.

Правильная архитектура отправки

Минимальная отказоустойчивая схема:

  1. Очередь сообщений (Redis, RabbitMQ) — все рассылочные задачи кладутся туда.
  2. Worker с rate-limiter — берёт по N задач в секунду, не превышая 30 RPS.
  3. Обработка ошибок Bot API:
    • 429 Too Many Requests с retry_after — подождать, повторить;
    • 403 Forbidden (бот заблокирован пользователем) — пометить чат как неактивный, больше не слать;
    • 400 chat not found — то же самое, удалить из базы рассылок.
  4. Логирование доставки и реакций — для аналитики и сегментации.
  5. Дедупликация — один и тот же chat_id не должен попасть в очередь дважды за рассылку.

Без этого механизма рассылка либо «упрётся» в 429-е и растянется в 10 раз, либо начнёт долбиться в неактивные чаты и заработает плохую репутацию.

Брокеры очередей: что выбрать

БрокерЯзык бэкендаКогда брать
CeleryPythonКлассика для Django/FastAPI, много готовых паттернов, тяжеловат
arqPython (asyncio)Лёгкий, нативно асинхронный, идеально под aiogram
RQPythonСамый простой, без сложных сценариев
BullMQNode.jsЛучший выбор под grammy/telegraf, отличный UI (Bull Board)
SidekiqRubyЕсли бэкенд на Rails
RabbitMQлюбойКогда нужны сложные роутинги, приоритеты, fan-out

Для большинства Telegram-проектов хватает Redis + arq (Python) или Redis + BullMQ (Node). RabbitMQ оправдан только при многотысячных RPS и сложной логике маршрутизации.

Rate limiter: token bucket per chat

Базовый паттерн — token bucket: на каждый chat_id ведётся «корзина» токенов, токен расходуется при отправке, восстанавливается с заданной скоростью. Глобально — sliding window на 30 RPS.

Пример на Python с Redis:

import time
import redis.asyncio as redis

class TelegramRateLimiter:
    def __init__(self, r: redis.Redis):
        self.r = r
        self.global_key = "tg:global:rate"
        self.global_limit = 28  # запас в 2 RPS

    async def acquire(self, chat_id: int) -> float:
        now = time.time()
        # Глобальный sliding window 1s
        pipe = self.r.pipeline()
        pipe.zremrangebyscore(self.global_key, 0, now - 1)
        pipe.zcard(self.global_key)
        pipe.zadd(self.global_key, {f"{now}:{chat_id}": now})
        pipe.expire(self.global_key, 2)
        _, count, _, _ = await pipe.execute()
        if count >= self.global_limit:
            return 1.0  # подождать секунду
        # Per-chat limit: 1 msg/sec
        chat_key = f"tg:chat:{chat_id}"
        last = await self.r.get(chat_key)
        if last and now - float(last) < 1.0:
            return 1.0 - (now - float(last))
        await self.r.set(chat_key, now, ex=5)
        return 0.0

В вызывающем коде:

wait = await limiter.acquire(chat_id)
if wait > 0:
    await asyncio.sleep(wait)
await safe_send(bot, chat_id, text)

Очередь рассылки на Celery

Полный пример производственной задачи рассылки на Celery с обработкой 429/403/400:

from celery import Celery
from celery.exceptions import Retry
from telegram import Bot
from telegram.error import RetryAfter, Forbidden, BadRequest

app = Celery("broadcast", broker="redis://localhost:6379/0")
bot = Bot(token=os.environ["BOT_TOKEN"])

@app.task(bind=True, max_retries=5, acks_late=True)
def send_broadcast_message(self, chat_id: int, text: str, broadcast_id: str):
    try:
        bot.send_message(chat_id=chat_id, text=text, parse_mode="HTML")
        DeliveryLog.create(
            broadcast_id=broadcast_id,
            chat_id=chat_id,
            status="delivered",
        )
    except RetryAfter as e:
        raise self.retry(countdown=e.retry_after + 1)
    except Forbidden:
        Subscriber.deactivate(chat_id, reason="bot_blocked")
        DeliveryLog.create(
            broadcast_id=broadcast_id,
            chat_id=chat_id,
            status="blocked",
        )
    except BadRequest as e:
        msg = str(e).lower()
        if "chat not found" in msg or "user is deactivated" in msg:
            Subscriber.deactivate(chat_id, reason="chat_not_found")
        else:
            raise

def enqueue_broadcast(broadcast_id: str, text: str):
    subscribers = Subscriber.active().values_list("chat_id", flat=True)
    for chat_id in subscribers:
        send_broadcast_message.apply_async(
            args=[chat_id, text, broadcast_id],
            queue="broadcast",
            rate_limit="25/s",
        )

rate_limit="25/s" — это Celery-уровень throttling с запасом ниже 30 RPS. Дополнительно рекомендуется ограничить параллелизм воркеров: celery -A app worker --concurrency=4 -Q broadcast.

Параллельные воркеры с лимитом на инстанс

Если запустить 10 воркеров, каждый с лимитом 30 RPS, суммарно бот будет слать 300 RPS и моментально получит бан. Поэтому:

  • лимит должен быть глобальным (через Redis), а не локальным (в памяти процесса);
  • альтернативно — ровно один воркер-отправитель на бота, остальные занимаются подготовкой данных, рендерингом шаблонов, логированием;
  • при горизонтальном масштабировании заменить инстансы на «диспетчер + N исполнителей», где диспетчер раздаёт токены через Redis.

Обработка ошибок: таблица реакций

Код / ИсключениеПричинаЧто делать
429 Too Many RequestsПревышен rate limitПодождать retry_after секунд, повторить
403 Forbidden: bot was blockedПользователь заблокировал ботаДеактивировать chat_id в БД, не пытаться больше
400 chat not foundchat_id не существуетДеактивировать в БД
400 user is deactivatedАккаунт удалёнДеактивировать в БД
400 message is too longПревышение 4096 символовРазрезать сообщение, повторить
400 can't parse entitiesБитый HTML/MarkdownЗалогировать, отправить как plain text
5xxВременная ошибка TelegramExponential backoff: 1s, 2s, 4s, 8s; после 5 попыток — DLQ
Connection timeoutСетевая проблемаRetry с backoff

DLQ (dead-letter queue) — отдельная очередь, куда падают сообщения, не доставленные после всех попыток. Раз в день эту очередь надо просматривать вручную или скриптом.

Шаблоны и персонализация

Сырой текст в коде — путь к ошибкам. Используйте Jinja2 для Python, MJML для HTML-писем (если параллельно есть email-канал) или Handlebars для Node. Пример:

from jinja2 import Template

template = Template("""
Привет, {{ name }}!

У вас есть промокод {{ promo }} со скидкой {{ discount }}% до {{ expires_at.strftime('%d.%m') }}.

<a href="https://example.com/?code={{ promo }}">Перейти к покупке</a>

<i>Чтобы отписаться, нажмите /unsubscribe</i>
""")

text = template.render(
    name=user.first_name or "друг",
    promo="SALE26",
    discount=15,
    expires_at=datetime(2026, 3, 31),
)

Хранить шаблоны лучше в БД (с версионированием) или в Git — чтобы не катить деплой ради правки одной запятой.

A/B-тесты копирайта

Половина успеха рассылки — текст. Тестируйте варианты:

  • Split 50/50 — две версии текста, по 50% базы на каждую;
  • Multivariate — 3-5 вариантов с разной длиной, эмодзи, CTA;
  • Темпоральный — одна и та же версия в разное время (утро vs вечер).

Минимальный объём для статзначимости — около 1000 получателей на вариант. Метрика — CTR (нажатие кнопки / переход по ссылке) или конверсия в целевое действие. Если CTR разнятся в 1.5-2 раза — победителем катите по основной базе.

Сегментация повышает доходимость

Сегментация — это не маркетинговая модель, а способ снизить долю репортов. Простейшие срезы:

  • по источнику регистрации (откуда пришёл — сайт, лендинг, реклама);
  • по дате последней активности (активные за 30 дней vs «спящие»);
  • по поведению (купил vs только смотрел каталог);
  • по интересам (категории товаров, тематика);
  • по тарифу/сегменту клиента;
  • по географии (для офлайн-офферов).

Чем ближе сообщение к интересу пользователя — тем меньше жалоб и выше конверсия. На практике сегментированная рассылка в 1000 человек часто даёт больше заказов, чем «по всем» в 50 000.

Метрики, которые надо считать

МетрикаФормулаНорма
Delivery rateдоставлено / отправленобольше 95%
Read rateпрочитано (если доступно) / доставлено60-80%
CTRкликов по кнопке / доставлено5-15% для промо
Conversion rateцелевое действие / доставлено1-5% для продаж
Unsubscribe rateотписок / доставлено за рассылкуменее 1%
Complaint rateрепортов / доставленоменее 0.1%

При complaint rate выше 0.5% — рассылка должна быть остановлена автоматически. Это hard-сигнал: продолжать = шаг к бану бота.

Время отправки и ритм

Несколько эмпирических правил, которые подтверждаются на больших объёмах:

  • утро 9:00-10:00 и вечер 19:00-21:00 — самые открываемые слоты;
  • не больше 1-2 рассылок в неделю на сегмент, иначе репорты растут;
  • транзакционные сообщения (статус заказа, напоминание) воспринимаются лучше промо;
  • триггерные цепочки (брошенная корзина, реактивация) работают надёжнее «ковровых» рассылок;
  • учитывайте часовые пояса: 9:00 МСК — это полночь в Магадане.

Отписка одной кнопкой

Кнопка «Отписаться» — must-have по UX и по закону. Минимальная реализация:

from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton

unsubscribe_kb = InlineKeyboardMarkup(inline_keyboard=[
    [InlineKeyboardButton(text="Отписаться", callback_data="unsub")],
])

@dp.callback_query(F.data == "unsub")
async def handle_unsub(callback):
    Subscriber.set_marketing_consent(callback.from_user.id, False)
    await callback.message.edit_reply_markup(reply_markup=None)
    await callback.answer("Вы отписаны от рассылки", show_alert=True)

Альтернатива — команда /unsubscribe или раздел в главном меню бота. Но кнопка под каждым промо-сообщением работает лучше всего.

Юридическая часть

Массовые рекламные сообщения подпадают под 152-ФЗ и закон «О рекламе»:

  • нужно согласие на получение рекламы (явное или вытекающее из оферты);
  • в каждом рекламном сообщении должна быть возможность отписаться;
  • хранение базы подписчиков — это обработка ПДн с соответствующей политикой;
  • для трансграничной передачи (а Telegram — это передача за рубеж) — отдельное уведомление в РКН.

Если бот находится в реестре операторов ПДн и предлагает «отписаться» в один клик — это сильно снижает риски как со стороны Telegram, так и со стороны регулятора. ФАС может оштрафовать на сумму от 100 000 до 500 000 ₽ за рассылку рекламы без согласия.

Что точно не стоит делать

  • Покупать готовые базы Telegram ID и слать им. Это прямой путь в бан.
  • Слать всем одинаковый текст «по всей базе» без сегментации.
  • Игнорировать 403 Forbidden и продолжать отправлять заблокировавшим бота.
  • Использовать сторонние «массовые рассыльщики», работающие через юзер-аккаунты, — это нарушает ToS Telegram и грозит блокировкой основного аккаунта владельца.
  • Парсить участников чужих чатов и каналов — двойное нарушение (и Telegram, и 152-ФЗ).
  • Прятать кнопку отписки или делать её неработающей.

Итого

Рассылка в Telegram-боте — это технический процесс с жёсткими лимитами, а не «нажать кнопку и разослать всем». Корректная архитектура с очередью (Celery/arq/BullMQ), глобальным rate-limiter через Redis (token bucket per chat + sliding window 30 RPS), обработкой ошибок (429 → wait, 403/400 → деактивация в БД) и сегментацией даёт миллионы доставленных сообщений без бана. Юридическая часть — согласие через бота, отписка одной кнопкой, политика ПДн. Игнорирование правил приводит к shadow-ban через 2-3 «ковровых» рассылки и потере канала продаж, плюс штрафу ФАС до 500 000 ₽.

Частые вопросы

Какие официальные лимиты у Telegram Bot API на рассылки?

Telegram декларирует три ограничения. Не больше 30 сообщений в секунду разным пользователям. Не больше 1 сообщения в секунду одному пользователю. Не больше 20 сообщений в минуту в одну группу. На практике это значит, что базу в 100 000 человек теоретически можно разослать за час — если качественно соблюдать темп. Но Telegram отслеживает не только частоту: важно соотношение «отправлено / прочитано / репорт». При плохом соотношении срабатывает shadow-ban даже в пределах формальных лимитов. Для крупных проектов через @BotSupport можно запросить расширенные лимиты до 1000+ RPS, но только при стабильной нагрузке и отсутствии нарушений.

Что такое FloodWait и как его обрабатывать?

FloodWait — это исключение, которое возвращает Bot API с кодом 429 Too Many Requests при превышении лимитов. В теле ответа есть параметр retry_after — количество секунд, которые нужно подождать перед повторной попыткой. Главное правило: retry_after — это не рекомендация, а обязательство. Если проигнорировать, следующий retry_after будет в 2-3 раза больше, и так до временной полной блокировки бота. В коде нужно ловить TelegramRetryAfter (aiogram), Forbidden, BadRequest по отдельности и реагировать корректно: для 429 — ждать и повторять, для 403/400 — деактивировать chat_id в БД и больше не слать.

Что такое shadow-ban в Telegram-боте и как его избежать?

Если 1-2% получателей нажимают «Report Spam» в первые минуты после рассылки, бот быстро улетает в ограничения. Сообщения начинают «теряться» без явной ошибки — Bot API возвращает 200, но пользователю ничего не приходит. Это и есть shadow-ban. Чтобы этого не происходило, важно: слать только тем, кто сам начал диалог с ботом; давать понятную причину сообщения; встроить кнопку «отписаться» в каждое массовое сообщение; сегментировать базу и не слать одинаковый текст всем подряд. Метрика complaint rate должна быть ниже 0.1%, при превышении 0.5% рассылку нужно автоматически останавливать.

Как технически устроена отказоустойчивая рассылка в Telegram?

Минимальная схема из четырёх компонентов. Очередь сообщений (Redis + arq/Celery, RabbitMQ, BullMQ для Node) — все рассылочные задачи кладутся туда. Worker с rate-limiter — берёт по N задач в секунду, не превышая 30 RPS, лимит должен быть глобальным через Redis, а не в памяти процесса. Обработка ошибок Bot API: 429 с retry_after — подождать, повторить; 403 Forbidden — пометить чат как неактивный, больше не слать; 400 chat not found — то же самое. Логирование доставки и реакций для аналитики. Дедупликация: один chat_id не должен попасть в очередь дважды за рассылку. DLQ для сообщений, не доставленных после всех попыток.

Как сегментация базы помогает избежать бана от Telegram?

Сегментация — это не маркетинговая модель, а способ снизить долю репортов. Простейшие срезы: по источнику регистрации, по дате последней активности (активные за 30 дней vs «спящие»), по поведению (купил vs только смотрел каталог), по интересам, по тарифу клиента, по географии. Чем ближе сообщение к интересу пользователя — тем меньше жалоб и выше конверсия. На практике сегментированная рассылка в 1000 человек часто даёт больше заказов, чем «по всем» в 50 000. A/B-тесты копирайта (split 50/50 или multivariate с 3-5 вариантами) на выборке от 1000 человек на вариант помогают найти лучшую формулировку.

Какие юридические требования к рассылкам в Telegram-боте?

Массовые рекламные сообщения подпадают под 152-ФЗ и закон «О рекламе» (ст. 18). Нужно согласие на получение рекламы — прямая выраженная воля, отдельный чекбокс под рекламные сообщения, не галочка по умолчанию. В каждом рекламном сообщении должна быть возможность отписаться в один клик. Хранение базы подписчиков — это обработка ПДн с соответствующей политикой, регистрация в реестре операторов ПДн. Для Telegram дополнительно — уведомление о трансграничной передаче ПДн в РКН. Штраф ФАС за рассылку без согласия — от 100 000 до 500 000 ₽ за факт. Подтверждать согласие лучше через самого бота с логом timestamp.

Какие метрики надо считать у Telegram-рассылки?

Шесть ключевых метрик. Delivery rate (доставлено / отправлено) — норма выше 95%. Read rate (прочитано / доставлено, если доступно) — 60-80%. CTR (кликов по кнопке / доставлено) — 5-15% для промо. Conversion rate (целевое действие / доставлено) — 1-5% для продаж. Unsubscribe rate (отписок / доставлено) — норма меньше 1% за рассылку. Complaint rate (репортов / доставлено) — критичная метрика, норма меньше 0.1%, при превышении 0.5% рассылку нужно автоматически останавливать. Без этих метрик невозможно понять, выгорает база или растёт.