Telegram bots have a clean, well-documented API and two deployment models that suit very different hosting setups. Choosing between long polling and webhooks is the most important decision you'll make. This guide covers both, plus the production essentials.
Long polling vs webhooks
- Long polling: your bot repeatedly calls
getUpdatesto fetch new messages. It needs no public URL and works behind any firewall, but it requires a continuously running process. Great for development and simple always-on bots. - Webhooks: you register an HTTPS URL with Telegram via
setWebhook, and Telegram POSTs updates to you. This is push-based, scales better, and works well with request-driven hosting — but you need a public HTTPS endpoint with a valid certificate.
| Long polling | Webhook | |
|---|---|---|
| Public URL needed | No | Yes (HTTPS) |
| Process model | Always-running loop | Request-driven |
| Setup | Simplest | One setWebhook call |
| Scale-to-zero friendly | No | Yes |
A long-polling bot (python-telegram-bot)
import os
from telegram.ext import ApplicationBuilder, CommandHandler
async def start(update, context):
await update.message.reply_text('Hello!')
app = ApplicationBuilder().token(os.environ['TELEGRAM_TOKEN']).build()
app.add_handler(CommandHandler('start', start))
app.run_polling()A webhook bot
With webhooks you run a small web server and register it once:
app = ApplicationBuilder().token(os.environ['TELEGRAM_TOKEN']).build()
app.add_handler(CommandHandler('start', start))
app.run_webhook(
listen='0.0.0.0',
port=int(os.environ['PORT']),
webhook_url=os.environ['WEBHOOK_URL']
)Use a secret token on the webhook (secret_token parameter) so you can verify that incoming requests genuinely come from Telegram.
Protect your token
The bot token grants full control of the bot. Load it from an environment variable, never commit it, and rotate it via BotFather if it leaks.
Persist state
Conversation state, user preferences, and reminders must live in a database. A deploy restarts the process and clears memory. For a small bot, a single PostgreSQL table is plenty.
Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "bot.py"]Deploying on PandaStack
Long-polling bot: deploy as a container app on a tier that stays running — a polling loop must not scale to zero. Set TELEGRAM_TOKEN as a secret in the [dashboard](https://dashboard.pandastack.io).
Webhook bot: deploy as a container app exposing your web server's PORT. PandaStack gives you automatic SSL, which satisfies Telegram's HTTPS requirement out of the box. Set WEBHOOK_URL to your app's URL and call setWebhook once (or let run_webhook do it). Because it's request-driven, a webhook bot tolerates scale-to-zero better than a polling bot.
For either model, attach a managed PostgreSQL database for state; DATABASE_URL is injected automatically.
Switching from polling to webhooks later
A common path is to start with polling for simplicity and switch to webhooks as traffic grows. The handler code stays the same — only the run mode and a setWebhook call change. PandaStack's free SSL means the webhook switch doesn't require any certificate work on your side.
References
- [Telegram Bot API documentation](https://core.telegram.org/bots/api)
- [Telegram: Marvin's Marvellous Guide to All Things Webhook](https://core.telegram.org/bots/webhooks)
- [python-telegram-bot documentation](https://docs.python-telegram-bot.org/)
- [Telegram: setWebhook reference](https://core.telegram.org/bots/api#setwebhook)
Deploy your Telegram bot with free SSL and a managed database on PandaStack's free tier — start at [dashboard.pandastack.io](https://dashboard.pandastack.io).