Telegram Bots

Long Polling vs Webhooks for Telegram Bots

How long polling and webhooks deliver Telegram updates, their real trade-offs in latency, load and setup, and how to choose and switch between them.

On this page
  1. How long polling works
  2. How webhooks work
  3. The trade-offs
  4. When long polling is the right choice
  5. When webhooks are the right choice
  6. Using them in code
  7. Switching between them
  8. Common mistakes
  9. What happens when your bot is down
  10. Checking webhook health
  11. Summary

Every Telegram bot has to decide how it receives updates. The two options — long polling and webhooks — both work well in production, and the internet is full of strong opinions about which is “correct”. This article explains how each works and gives practical criteria for choosing.

How long polling works

Your bot calls the getUpdates method with a timeout, typically 30–60 seconds. Telegram holds the request open until an update arrives or the timeout expires, then responds. The bot processes the updates and immediately calls getUpdates again, passing an offset to confirm which updates it has handled.

bot → getUpdates(offset=101, timeout=50)
         … Telegram waits until a message arrives …
bot ← [update 101, update 102]
bot → getUpdates(offset=103, timeout=50)

Because the request waits for data rather than returning empty immediately, long polling is efficient — there’s no busy loop hammering the API. Updates arrive almost as quickly as with a webhook.

How webhooks work

You call setWebhook with an HTTPS URL. From then on, Telegram sends each update to that URL as an HTTP POST, and your web server responds with 200 OK. Your bot doesn’t make any requests to receive updates at all.

Telegram’s requirements for webhooks:

  • The URL must use HTTPS with a valid certificate (or a self-signed one uploaded with setWebhook).
  • It must be on port 443, 80, 88 or 8443.
  • Your server must respond reasonably quickly; if it keeps failing, Telegram retries and eventually backs off.

You can also set a secret_token when registering. Telegram then includes it in the X-Telegram-Bot-Api-Secret-Token header of every request, so your server can reject anything that didn’t come from Telegram.

The trade-offs

Long polling Webhooks
Setup Just run the bot Needs a public HTTPS endpoint
Domain & TLS Not needed Required
Works behind NAT / on a laptop Yes Not without a tunnel
Idle load One open request at a time None
Latency Near-instant Near-instant
Horizontal scaling One poller per bot Several servers behind a load balancer
Debugging Easy locally Needs a tunnel or deployed endpoint
Needs always-on process Yes Yes (the web server)

The latency difference people argue about is negligible for almost every bot. The real differences are setup complexity and how you scale.

When long polling is the right choice

Long polling is the best default for most bots:

  • You want zero infrastructure. No domain, no certificate, no reverse proxy. Run the bot and it works.
  • You develop locally. The same code runs on your laptop and in production.
  • Your bot runs as one process. Only one process can poll a given bot at a time, which is fine for the vast majority of bots.

The one requirement is a host that doesn’t suspend your process for lack of inbound traffic. Many free web platforms do exactly that, which is why polling bots on them go silent. On Kerit Cloud your process is persistent, so a polling loop runs indefinitely and is supervised — if it crashes, it restarts within seconds.

When webhooks are the right choice

Webhooks shine in a few situations:

  • Very high volume. A web server can process many updates concurrently, and you can run several instances behind a load balancer.
  • You already run a web app. If your bot is part of a web service with a domain and TLS, adding a webhook route is trivial.
  • Serverless or event-driven designs. The handler only needs to run when an update arrives.
  • You want Telegram to do the queuing. If your server is briefly down, Telegram keeps retrying delivery for a while.

Every Kerit Cloud Telegram server gets an HTTPS endpoint you can register with one call, so webhooks don’t require setting up your own domain or certificate. See setting up a Telegram webhook over HTTPS.

Using them in code

aiogram 3 — polling

await dp.start_polling(bot)

aiogram 3 — webhook

from aiohttp import web
from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application

async def on_startup(bot: Bot):
    await bot.set_webhook(f"{BASE_URL}/webhook", secret_token=WEBHOOK_SECRET)

dp.startup.register(on_startup)
app = web.Application()
SimpleRequestHandler(dispatcher=dp, bot=bot, secret_token=WEBHOOK_SECRET).register(app, path="/webhook")
setup_application(app, dp, bot=bot)
web.run_app(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))

grammY — polling and webhook

// Polling
bot.start();

// Webhook (Express)
const { webhookCallback } = require('grammy');
app.use(express.json());
app.post('/webhook', webhookCallback(bot, 'express', { secretToken: process.env.WEBHOOK_SECRET }));

Switching between them

You can switch at any time, but the two modes are mutually exclusive:

  • While a webhook is set, getUpdates fails with 409 Conflict: can't use getUpdates method while webhook is active. Call deleteWebhook before polling.
  • Setting a webhook stops polling from receiving updates.

When switching, decide what to do with updates that arrived in between. Both deleteWebhook and setWebhook accept drop_pending_updates=true if you’d rather start fresh than process a backlog.

Common mistakes

Running two pollers. A local copy and a deployed copy both calling getUpdates produce 409 Conflict: terminated by other getUpdates request, and updates get split randomly between them. Only run one.

Blocking the polling loop. If a handler does slow synchronous work, no other updates are processed until it finishes. Use async libraries, or run heavy work in background tasks.

Webhook handlers that do all the work before responding. Telegram waits for your 200 response. Acknowledge quickly and process in the background if a task is slow, or Telegram may retry and deliver the update twice.

No secret token on webhooks. Without it, anyone who discovers your URL can send fake updates.

What happens when your bot is down

Neither mode loses updates during a short outage. Telegram stores incoming updates until your bot receives them, for up to 24 hours:

  • With long polling, updates wait on Telegram’s side. When the bot starts again and calls getUpdates, it receives the backlog in order.
  • With webhooks, Telegram keeps retrying delivery to your URL, backing off as failures continue. When your endpoint comes back, delivery resumes.

That’s useful, but it can surprise you. A bot that was offline for an hour may wake up and answer a pile of stale messages at once. Decide what you want: process the backlog (the default), or skip it with drop_pending_updates=True when deleting or setting the webhook at startup. Bots that respond to time-sensitive commands usually skip; bots that record data, like a group logger, usually process everything.

Checking webhook health

When a webhook bot goes quiet, getWebhookInfo tells you why without guessing:

curl "https://api.telegram.org/bot$BOT_TOKEN/getWebhookInfo"

The response includes the registered url, pending_update_count (how many updates are waiting), and — most usefully — last_error_date and last_error_message, such as “Connection timed out” or “SSL error”. A growing pending count with a recent error means Telegram can’t reach your endpoint. An empty url means no webhook is set, so the bot should be polling.

For polling bots, the equivalent check is your own logs: a healthy poller logs regularly and never shows repeated conflict errors.

Summary

Long polling and webhooks deliver updates equally fast. Long polling needs no domain or certificate and is the best default for single-process bots — as long as the host keeps the process running. Webhooks suit high-volume bots, existing web apps and multi-instance deployments, and require an HTTPS endpoint and a secret token. You can switch any time; just delete the webhook before polling and decide whether to keep pending updates.