IntegrationsIntermediate

How to switch a Telegram bot from polling to webhooks

Move a bot off long polling and onto a webhook URL so Telegram pushes updates to your server.

9 minIntermediate

Polling is great for local development: your bot repeatedly asks Telegram for new messages. In production, webhooks are better. Telegram pushes each update to a public HTTPS URL the instant it arrives, which is faster and cheaper. This guide migrates a bot from one to the other.

What you need

  • A working bot and token
  • A public HTTPS endpoint (a deployed server or a tunnel like ngrok)
  • A valid TLS certificate (Telegram requires HTTPS)

Step 1: Understand the trade-off

AspectPollingWebhook
SetupZero configNeeds a public HTTPS URL
LatencyUp to a few secondsNear instant
Best forLocal devProduction
ServerCan run anywhereMust be reachable from internet

Step 2: Build an HTTP endpoint

Telegram POSTs each update as JSON to your URL. Here is a minimal Express handler that reads the message and replies through sendMessage.

server.mjs
import express from "express";

const app = express();
app.use(express.json());
const token = process.env.TELEGRAM_BOT_TOKEN;

app.post("/webhook", async (req, res) => {
  const msg = req.body.message;
  if (msg?.text) {
    await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        chat_id: msg.chat.id,
        text: `You said: ${msg.text}`,
      }),
    });
  }
  res.sendStatus(200);
});

app.listen(3000, () => console.log("Listening on :3000"));
Always return 200 fast
Telegram retries any update your endpoint does not acknowledge with a 2xx status. Respond quickly and do slow work in the background, or you will get duplicate updates.

Step 3: Register the webhook

Tell Telegram where to send updates with setWebhook. Adding a secret token header lets you verify that incoming requests really come from Telegram.

zsh - setWebhook
$curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
$ -d url="https://api.example.com/webhook" \
$ -d secret_token="my-shared-secret"
{"ok":true,"result":true,"description":"Webhook was set"}
$
Verify the request header
POST /webhook
X-Telegram-Bot-Api-Secret-Token: my-shared-secret
Content-Type: application/json
----------------------------------
if (header !== "my-shared-secret") return 403
Reject any POST that lacks the matching secret header.

Step 4: Confirm and roll back if needed

Check the active webhook with getWebhookInfo. To go back to polling later, call deleteWebhook first, otherwise the two methods conflict.

curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/getWebhookInfo"
# to revert:
curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/deleteWebhook"

Result

Telegram now delivers updates straight to your server with almost no delay, and the secret header keeps the endpoint safe from spoofed requests.

Watch related tutorials

Tags
#telegram#webhook#polling#bot#deployment