Skip to main content
By default Telebot uses long polling (LongPoller) to fetch updates. Webhooks let Telegram push updates directly to your server, which is more efficient in production.

Long polling vs webhooks

Long PollingWebhook
SetupZero configRequires a public HTTPS URL
Latency~100 ms round-tripNear-instant push
Suitable forDevelopment, low trafficProduction, high traffic
Horizontal scalingSingle instanceMultiple instances behind a load balancer

The Webhook struct

type Webhook struct {
    Listen         string   // address to bind, e.g. "0.0.0.0:8443"
    MaxConnections int      // max simultaneous connections (1-100)
    AllowedUpdates []string // filter update types; empty = all
    IP             string   // fixed IP for Telegram to connect to
    DropUpdates    bool     // discard pending updates on startup
    SecretToken    string   // verify X-Telegram-Bot-Api-Secret-Token header
    IgnoreSetWebhook bool   // skip the setWebhook API call

    TLS      *WebhookTLS      // local TLS configuration
    Endpoint *WebhookEndpoint // public URL / cert for Telegram
}

Basic setup with a trusted certificate

If your server uses a certificate from a trusted CA (Let’s Encrypt, etc.), only the public URL is needed:
b, _ := tele.NewBot(tele.Settings{
    Token: "TOKEN",
    Poller: &tele.Webhook{
        Listen: "0.0.0.0:443",
        TLS: &tele.WebhookTLS{
            Cert: "/etc/letsencrypt/live/example.com/fullchain.pem",
            Key:  "/etc/letsencrypt/live/example.com/privkey.pem",
        },
        Endpoint: &tele.WebhookEndpoint{
            PublicURL: "https://example.com/",
        },
    },
})

b.Start()
When the poller starts it automatically calls b.SetWebhook() and then begins serving HTTP requests.

Setup with a self-signed certificate

For self-signed certificates, provide the .pem file in both TLS and Endpoint.Cert so Telegram can trust it:
poller := &tele.Webhook{
    Listen: "0.0.0.0:8443",
    TLS: &tele.WebhookTLS{
        Cert: "webhook.pem",
        Key:  "webhook.key",
    },
    Endpoint: &tele.WebhookEndpoint{
        PublicURL: "https://203.0.113.5:8443/",
        Cert:      "webhook.pem", // uploaded to Telegram
    },
}

b, _ := tele.NewBot(tele.Settings{
    Token:  "TOKEN",
    Poller: poller,
})

b.Start()
Telegram supports webhooks on ports 443, 80, 88, and 8443 only.

Behind a reverse proxy

When a load balancer or reverse proxy (nginx, Caddy, etc.) handles TLS, leave TLS empty and only set Endpoint:
poller := &tele.Webhook{
    Listen: "127.0.0.1:8080", // bind to localhost; proxy forwards here
    Endpoint: &tele.WebhookEndpoint{
        PublicURL: "https://bot.example.com/webhook",
        // Cert left empty — public cert, no upload needed
    },
}

Integrating into an existing HTTP server

Leave Listen empty to prevent Telebot from starting its own server. Then pass the webhook handler to your mux:
webhook := &tele.Webhook{
    // Listen is empty — no server started
    Endpoint: &tele.WebhookEndpoint{
        PublicURL: "https://example.com/telegram",
    },
}

b, _ := tele.NewBot(tele.Settings{
    Token:  "TOKEN",
    Poller: webhook,
})

// The Webhook implements http.Handler
http.Handle("/telegram", webhook)
http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)

Securing with SecretToken

Set SecretToken to have Telebot reject requests that do not include the matching X-Telegram-Bot-Api-Secret-Token header:
poller := &tele.Webhook{
    Listen:      "0.0.0.0:8443",
    SecretToken: "my-very-secret-token-123",
    // ...
}
Telegram will include this header on every webhook request automatically after you call SetWebhook.

Managing the webhook

// Register or update the webhook manually
err := b.SetWebhook(webhook)

// Get current webhook status
info, err := b.Webhook()
fmt.Println(info.PendingUpdates, info.ErrorMessage)

// Remove the webhook (switches back to long polling)
err = b.RemoveWebhook()

// Remove and drop all pending updates
err = b.RemoveWebhook(true)

Processing updates manually

If you need full control over how updates are dispatched — for example in a serverless function — use b.ProcessUpdate() directly:
func lambdaHandler(body []byte) error {
    var update tele.Update
    if err := json.Unmarshal(body, &update); err != nil {
        return err
    }
    b.ProcessUpdate(update)
    return nil
}
ProcessUpdate is also useful for testing: inject synthetic updates without a live Telegram connection.

Filtering update types

Reduce unnecessary traffic by telling Telegram which update types to send:
poller := &tele.Webhook{
    Listen: "0.0.0.0:443",
    AllowedUpdates: []string{
        "message",
        "callback_query",
        "inline_query",
    },
    // ...
}

DropUpdates on startup

Set DropUpdates: true to discard any updates queued while the bot was offline:
poller := &tele.Webhook{
    Listen:      "0.0.0.0:8443",
    DropUpdates: true,
    // ...
}
For bots that react to real-time events (like live support), dropping stale updates prevents handlers from processing outdated messages after a restart.

Build docs developers (and LLMs) love