# How to Receive Telegram Updates Using Long Polling in Telego

> Learn to receive Telegram updates via long polling in Telego. Call Bot UpdatesViaLongPolling to get a channel of Update objects that closes automatically with your context.

- Repository: [Artem Yadelskyi/telego](https://github.com/mymmrac/telego)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Call `Bot.UpdatesViaLongPolling(ctx, params, opts...)` to start a long-polling loop that returns a receive-only channel of `Update` objects, which closes automatically when the context is cancelled.**

The Telego library provides a robust, production-ready implementation of Telegram's long-polling mechanism for Go applications. By using the `UpdatesViaLongPolling` method, you can receive Telegram updates using long polling in Telego without managing the underlying HTTP retry logic, offset tracking, or concurrency controls yourself.

## How UpdatesViaLongPolling Works

The `UpdatesViaLongPolling` method, defined in [`long_polling.go`](https://github.com/mymmrac/telego/blob/main/long_polling.go), orchestrates the entire polling lifecycle through several coordinated steps.

### Single Update Source Enforcement

Before starting the loop, Telego checks the bot's internal `running` state to ensure that only one update source is active at a time. If a webhook or another long-polling instance is already running, the method returns an error immediately【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L79-L82】.

### Configuration and Defaults

The method constructs a `longPolling` configuration via `createLongPolling`, applying the following defaults when no options are provided【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L56-L71】:

- `updateChanBuffer`: `100` (channel capacity)
- `updateInterval`: `0` (no artificial delay between requests)
- `retryTimeout`: `8s` (wait time before retrying on error)

If you pass `nil` for `params`, Telego automatically applies a default timeout of `8` seconds to the `GetUpdatesParams`【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L91-L102】.

### The Polling Loop

Telego spawns a goroutine running `doLongPolling`, which repeatedly calls `Bot.GetUpdates` (the low-level HTTP wrapper from [`methods.go`](https://github.com/mymmrac/telego/blob/main/methods.go)) while respecting the supplied context【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L104-L107】.

Inside the loop, the following logic applies:

1. **Context cancellation**: The loop exits immediately when the caller-provided `ctx` is cancelled【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L17-L20】.
2. **Error handling**: If `GetUpdates` returns an error, Telego checks whether it is fatal or if retries are disabled (`retryTimeout == 0`). In either case, the channel closes; otherwise, it logs the error and sleeps for `retryTimeout` before retrying【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L24-L34】.
3. **Offset tracking**: For each update with `UpdateID >= params.Offset`, the offset is advanced and the update (augmented with the request context via `WithContext`) is sent into the channel【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L36-L47】.
4. **Rate limiting**: If `updateInterval` is greater than zero, the loop sleeps for that duration after each successful batch【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L50-L52】.

When the context finishes, the channel closes and the bot's `running` flag is reset【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L11-L15】.

## Customizing Long Polling Behavior

Telego provides three functional options to tune the polling mechanism:

- **`WithLongPollingUpdateInterval(d time.Duration)`**: Sets a pause between consecutive `GetUpdates` calls. Useful to reduce request rate when you don't need minimum latency【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L30-L42】.
- **`WithLongPollingRetryTimeout(d time.Duration)`**: Changes the back-off after a recoverable error. Set to `0` to disable retries and close the channel on first error【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L45-L56】.
- **`WithLongPollingBuffer(buf uint)`**: Changes the internal channel capacity from the default `100`【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L59-L66】.

Pass these options variadically to `UpdatesViaLongPolling`:

```go
updates, err := bot.UpdatesViaLongPolling(ctx, nil,
    telego.WithLongPollingUpdateInterval(2*time.Second),
    telego.WithLongPollingRetryTimeout(5*time.Second),
    telego.WithLongPollingBuffer(200))

```

## Graceful Shutdown Handling

Because the update channel is tied to a `context.Context`, cancelling the context (e.g., on `SIGINT`) stops the polling loop and closes the channel. The repository includes a complete example demonstrating how to wait for the channel to drain before stopping the handler: **examples/graceful_shutdown_long_polling/main.go**【/cache/repos/github.com/mymmrac/telego/main/examples/graceful_shutdown_long_polling/main.go#L14-L78】.

## Complete Working Examples

### Minimal Long Polling Bot

This example shows the bare minimum to receive Telegram updates using long polling in Telego:

```go
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/mymmrac/telego"
)

func main() {
	// Bot token from the environment
	bot, err := telego.NewBot(os.Getenv("TOKEN"))
	if err != nil {
		panic(err)
	}
	defer bot.Stop() // clean shutdown

	// Start long-polling; nil params → default timeout (8 s)
	updates, err := bot.UpdatesViaLongPolling(context.Background(), nil)
	if err != nil {
		panic(err)
	}

	// Consume updates
	for update := range updates {
		fmt.Printf("Received update %d from chat %d\n",
			update.UpdateID, update.Message.Chat.ID)
		// …your handling logic here…
	}
}

```

**Key points:**
- `UpdatesViaLongPolling` returns a read-only `<-chan Update`.
- The channel closes automatically when the supplied context is cancelled or an unrecoverable error occurs.

### Production-Ready Bot with Graceful Shutdown

This example mirrors the official **graceful_shutdown_long_polling** sample【/cache/repos/github.com/mymmrac/telego/main/examples/graceful_shutdown_long_polling/main.go#L14-L78】:

```go
package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"time"

	"github.com/mymmrac/telego"
	th "github.com/mymmrac/telego/telegohandler"
)

func main() {
	bot, _ := telego.NewBot(os.Getenv("TOKEN"))
	defer bot.Stop()

	// Context that ends on Ctrl+C
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	// Long-polling with custom options
	updates, _ := bot.UpdatesViaLongPolling(
		ctx,
		nil,
		telego.WithLongPollingUpdateInterval(2*time.Second), // slower request rate
		telego.WithLongPollingRetryTimeout(5*time.Second),  // quicker retries
	)

	// Attach a handler (optional, but shows integration with telegohandler)
	handler, _ := th.NewBotHandler(bot, updates)
	handler.Handle(func(c *th.Context, upd telego.Update) error {
		fmt.Printf("Processing update %d\n", upd.UpdateID)
		return nil
	})

	// Run the handler in its own goroutine
	go func() { _ = handler.Start() }()

	// Wait until the signal is received
	<-ctx.Done()
	fmt.Println("Shutting down…")
	// Give the handler a chance to finish current work
	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer shutdownCancel()
	_ = handler.StopWithContext(shutdownCtx)
}

```

This implementation demonstrates:
- **Signal handling** for graceful shutdown.
- **Custom polling intervals** to control request frequency.
- **Integration with `telegohandler`** for structured update processing.

## Summary

- **Primary method**: Use `Bot.UpdatesViaLongPolling(ctx, params, opts...)` from [`long_polling.go`](https://github.com/mymmrac/telego/blob/main/long_polling.go) to receive Telegram updates using long polling in Telego.
- **Concurrency safety**: The method enforces a single active update source via an internal `running` state check.
- **Configurable behavior**: Tune the polling loop with `WithLongPollingUpdateInterval`, `WithLongPollingRetryTimeout`, and `WithLongPollingBuffer`.
- **Context-driven lifecycle**: The polling loop respects context cancellation, automatically closing the update channel and resetting internal state on shutdown.
- **Graceful shutdown**: Reference the official example at [`examples/graceful_shutdown_long_polling/main.go`](https://github.com/mymmrac/telego/blob/main/examples/graceful_shutdown_long_polling/main.go) for production-ready signal handling.

## Frequently Asked Questions

### What is the default timeout for long polling in Telego?

When you pass `nil` for the `params` argument to `UpdatesViaLongPolling`, Telego automatically creates a `GetUpdatesParams` struct with a default timeout of **8 seconds**. This is implemented in [`long_polling.go`](https://github.com/mymmrac/telego/blob/main/long_polling.go) where a shallow copy of the parameters is prepared before entering the polling loop【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L91-L102】.

### How do I stop long polling gracefully?

To stop long polling gracefully, cancel the `context.Context` you passed to `UpdatesViaLongPolling`. When the context is cancelled, the internal `doLongPolling` goroutine detects this, closes the update channel, and resets the bot's `running` state【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L17-L20】. For production applications, use `signal.NotifyContext` to trigger shutdown on SIGINT and drain remaining updates before exiting, as shown in the [`examples/graceful_shutdown_long_polling/main.go`](https://github.com/mymmrac/telego/blob/main/examples/graceful_shutdown_long_polling/main.go) sample【/cache/repos/github.com/mymmrac/telego/main/examples/graceful_shutdown_long_polling/main.go#L14-L78】.

### Can I use both webhook and long polling simultaneously?

No, Telego explicitly prevents simultaneous use of webhooks and long polling. The `UpdatesViaLongPolling` method checks the bot's internal `running` atomic state at startup and returns an error if another update source (such as a webhook or existing long-polling loop) is already active【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L79-L82】. This design ensures thread safety and prevents duplicate update processing.

### How do I handle errors during long polling?

Telego's long-polling implementation includes automatic retry logic for transient errors. By default, if `GetUpdates` returns a non-fatal error, the loop logs the error and sleeps for the `retryTimeout` duration (default **8 seconds**) before retrying【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L32-L34】. You can customize this behavior using `WithLongPollingRetryTimeout`—setting it to `0` disables retries and causes the channel to close immediately on the first error【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L45-L56】. Fatal errors (such as context cancellation) always terminate the loop immediately【/cache/repos/github.com/mymmrac/telego/main/long_polling.go#L24-L31】.