Every Telegram bot developer eventually has to decide how the bot should receive messages. The Bot API offers two options. With polling, the bot keeps asking Telegram whether anything new has arrived. With a webhook, Telegram pushes every update to a URL on your server. Both are reliable, but they place different demands on your server, network and code.
How polling works
The bot calls getUpdates repeatedly. Libraries use long polling: the request carries a timeout, and Telegram holds it open until an update arrives, so messages are delivered almost instantly without hammering the API. The bot advances offset after each batch so nothing is processed twice.
curl "https://api.telegram.org/bot<TOKEN>/getUpdates?timeout=30&offset=0"
Polling needs no public IP, domain or certificate, so it works on a laptop or behind NAT. The catch is that the bot process must run continuously; if it stops, updates wait in Telegram's queue until it comes back. Run it under systemd or a process manager so it restarts automatically after a crash or a server reboot.
How webhooks work
You register one HTTPS URL, such as https://bot.example.uz/tg/webhook, and Telegram sends each update there as a JSON POST request. The bot behaves like a normal web app, which saves resources and lets you run several instances behind a load balancer. According to Telegram's documentation, webhooks must use HTTPS on port 443, 80, 88 or 8443. A Let's Encrypt certificate works fine; a self-signed one must be uploaded with the setWebhook call.
curl -F "url=https://bot.example.uz/tg/webhook" \
-F "secret_token=long-random-string" \
-F "drop_pending_updates=true" \
"https://api.telegram.org/bot<TOKEN>/setWebhook"
curl "https://api.telegram.org/bot<TOKEN>/getWebhookInfo"
Securing the webhook
Because the URL is public, anyone can send fake requests to it. Set secret_token, and Telegram will include the X-Telegram-Bot-Api-Secret-Token header in every request; reject anything without the correct value. Never publish the bot token, and if it leaks, regenerate it in BotFather immediately. The old token stops working at once, so nobody holding it can control your bot any longer. Choosing a hard-to-guess path for the webhook URL adds one more small layer of protection.
A real-world example
A Tashkent training center builds a course sign-up bot. During development it runs with polling on the developer's laptop. Once it is linked to the website's application form, it moves to bot.example.uz and switches to a webhook, with Nginx forwarding requests to the bot process:
location /tg/webhook {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
}
If the site already runs on HTTPS on the same server, no extra certificate is needed. On sayt.uz hosting, free Let's Encrypt certificates renew automatically for subdomains too, so an expired certificate is unlikely to break the webhook.
Common mistakes
- Calling
getUpdateswhile a webhook is set returns 409 Conflict; calldeleteWebhookfirst. - Two polling processes with the same token interfere with each other.
last_error_messageingetWebhookInfoexplains failures, and a growingpending_update_countmeans updates are not being delivered.- Answer webhook requests quickly and move heavy work to a background queue.
Which one to choose
Start with polling for small bots on a simple VPS or home server without a public HTTPS address. Choose a webhook when the bot serves many users, lives inside an existing web app or needs several instances. Plain PHP hosting usually cannot keep a process running, but it can handle webhook requests with an ordinary script. Many teams poll during development and use webhooks in production.