Перейти к содержанию

Webhook vs Polling

Рекомендация MAX

Получение обновлений с помощью Long Polling ограничено по скорости и сроку хранения событий — этот способ не подходит для production-окружения. Рекомендуется на всех этапах работы использовать Webhook.

Polling

async def main():
    await dp.start_polling(bot, skip_updates=False)

Параметры:

  • bot — экземпляр бота
  • skip_updates — пропускать старые события (по умолчанию False)

Плюсы:

  • Простая настройка
  • Не требует публичного URL
  • Подходит для разработки

Минусы:

  • Постоянное подключение к API
  • Не подходит для production (ограничения по скорости и хранению событий)

Остановка polling

await dp.stop_polling()

stop_polling() — единственный корректный способ остановиться: он прерывает висящий запрос get_updates, дожидается цикла, инлайн-обработчиков и фоновых задач (use_create_task=True), а затем закрывает изоляцию событий.

Дожидается он именно выхода из цикла, а не задачи, которая вызвала start_polling(). Если await dp.start_polling(bot) стоит внутри более крупной корутины, та задача после остановки продолжает работу как обычно — и stop_polling() из обработчика её не ждёт (иначе получилось бы кольцо, когда продолжение задачи ждёт останавливающий обработчик).

  • task.cancel() на задаче start_polling заменой не является: фоновые задачи не будут дожданы, изоляция не будет закрыта. После отмены вызовите await dp.shutdown(), а перед новым запуском дождитесь самой отменённой задачи.
  • Повторный start_polling на ещё живом цикле поднимает RuntimeError — сначала остановите текущий. А если цикл уже вышел, но уборка за ним ещё идёт (отложенный дренаж после инлайн-остановки — см. ниже — или shutdown() внешнего stop_polling()), новый запуск не отклоняется, а дожидается её окончания: иначе прошлая уборка закрыла бы изоляцию уже нового цикла. Поэтому идиома while True: await dp.start_polling(bot) переживает остановку снаружи.
  • Обработчик может остановить polling сам (await dp.stop_polling() внутри хендлера). Поведение зависит от того, как обработчик выполняется:
    • Инлайн-обработчик (use_create_task=False, обработчик выполняется прямо в задаче polling) — вызов только выставляет флаги, цикл завершится сразу после возврата из обработчика; дренаж фоновых задач и закрытие изоляции произойдут автоматически сразу после выхода из цикла.
    • Фоновый обработчик (use_create_task=True) — сам stop_polling() блокируется до выхода из цикла polling, но остальные фоновые задачи, ещё не завершившиеся к этому моменту, дожидаться не будет, а изоляция событий закрыта не будет — они доработают сами. Чтобы дождаться их явно, вызовите await dp.shutdown() снаружи обработчиков.
  • Регистрация хендлеров, роутеров, middleware и фильтров после старта работает: индекс обработчиков перестраивается автоматически перед следующим событием. После остановки диспетчер снова считается неподготовленным, поэтому зарегистрированный в этот момент @dp.on_started() штатно сработает при следующем start_polling(). Предупреждение об опоздавшем @dp.on_started() печатается только в двух случаях: подготовка уже завершена или колбэк регистрируют изнутри самого on_started. Регистрация в окне подготовки — до того, как диспетчер прочитал колбэк (например из-под долгого check_me), — не опаздывает: колбэк будет вызван в этом же запуске.

Ограничения Long Polling (с 11.05.2026)

На стороне API MAX действуют следующие ограничения на использование Long Polling:

  • Максимальное количество запросов — 2 RPS (запроса в секунду).
  • Таймаут запроса — 30 секунд.
  • Максимальный размер батча — 100 событий.
  • TTL событий — 24 часа (события старше этого периода удаляются).

Для корректной работы чат-бота рекомендуется перейти на схему с Webhook.

Webhook

async def main():
    webhook_url = "https://ваш-домен.рф/webhook"  # <-- укажите свой
    await bot.subscribe_webhook(url=webhook_url)
    await dp.handle_webhook(bot, host="0.0.0.0", port=8080, path="/webhook")

Параметры:

  • bot — экземпляр бота
  • host — хост сервера (по умолчанию '0.0.0.0')
  • port — порт сервера (по умолчанию 8080)
  • path — URL-путь для маршрута вебхука (по умолчанию '/')
  • secret — секрет для проверки заголовка X-Max-Bot-Api-Secret
  • **kwargs — дополнительные параметры для запуска веб-сервера

Плюсы:

  • Эффективнее для больших нагрузок
  • События приходят мгновенно
  • Меньше нагрузка на API

Минусы:

  • Требует публичный URL
  • Нужна настройка сервера

Остановка вебхука

await dp.shutdown() дожидается фоновых задач (use_create_task=True) и закрывает изоляцию событий. Вызывать его нужно после остановки приёма запросов — shutdown-хуки интеграций (maxapi.webhook) делают это сами.

Вызов dp.shutdown() изнутри обработчика дренаж и закрытие изоляции пропускает (с предупреждением в логе) — в том числе при use_create_task=False, когда обработчик выполняется прямо в задаче HTTP-запроса. Иначе получилось бы кольцо: остальные обработчики того же пользователя ждут блокировку изоляции, которую держит сам вызывающий. Оставшиеся задачи доработают сами; чтобы дождаться их, вызовите await dp.shutdown() снаружи обработчиков.

Требования безопасности (с 25.05.2026)

Для повышения безопасности прекращается поддержка получения вебхуков по HTTP, а также самоподписных сертификатов. Используйте HTTPS и сертификаты, выданные доверенным центром сертификации. Подробнее о требованиях безопасности — в описании POST /subscriptions (метод subscribe_webhook).

Важно

Если у бота есть подписки на Webhook, start_polling предупредит об этом в логах. Удалите подписки через await bot.delete_webhook().