Methods

Get vs Iter

Most list-returning helpers come in two flavours. Get takes one round-trip and returns a slice. Iter pages for you across many round-trips and delivers each element to a callback as it arrives.

The pattern

Wherever you see Get*, there is usually an Iter* counterpart:

Single batchPaginated stream
GetMessagesIterMessages
GetChatMembersIterChatMembers
GetDialogsIterDialogs
GetProfilePhotos

Get returns a slice you walk yourself. Iter drives pagination in the background and feeds you items as they come; you do not deal with offsets, deduplication, or flood waits.

When to use Get

For everything fixed-size: "give me the latest N messages," "list the admins," "fetch this user's last 10 photos."

msgs, err := client.GetMessages(peerID, &telegram.SearchOption{
	Limit: 50,
})
fmt.Printf("got %d\n", len(msgs))
msgs, err := client.GetMessages(peerID, &telegram.SearchOption{
	Limit: 50,
})
fmt.Printf("got %d\n", len(msgs))

Predictable cost, single round-trip, no flood-wait surprises.

When to use Iter

For everything open-ended: "scan all messages from a user since some date," "export every participant of a 100k supergroup," "backfill an archive."

err := client.IterMessages(peerID, func(m *telegram.NewMessage) error {
	fmt.Println(m.Text())
	return nil
}, &telegram.SearchOption{})
err := client.IterMessages(peerID, func(m *telegram.NewMessage) error {
	fmt.Println(m.Text())
	return nil
}, &telegram.SearchOption{})

The callback gets one item per server page. Return nil to continue, an error to stop. gogram handles pagination, dedup, and waits between pages internally.

Options

For message iteration, SearchOption is the shared knob for both GetMessages and IterMessages. Real fields:

Limit int32
Maximum items to yield. Zero on Iter means unbounded; zero on Get means one server page (100).
IDs any
Fetch specific message ids. Bots use this to look up a message they saw earlier.
Query string
Full-text search inside the chat. Empty means no search filter.
FromUser any
Messages only from a specific peer (username, id, or resolved InputPeer).
Filter MessagesFilter
Restrict to a media kind — InputMessagesFilterPhotos, InputMessagesFilterVideo, InputMessagesFilterDocument, and so on.
Offset / AddOffset int32
Sequential offset from the newest message. Use AddOffset to skip N results after the offset id.
MinID / MaxID int32
Bound the id range you want back. Setting MinID is how you resume from a specific point without an offset date.
MinDate / MaxDate int32
Bound by unix timestamp.
TopMsgID int32
Restrict to a forum topic thread.
SleepThresholdMs int32
How long Iter is willing to sleep between chunks on a soft flood wait before surfacing the error.
Context context.Context
Cancel long-running iterations from your side.
ErrorCallback
Called on each per-chunk error with progress info. Return an error to stop, nil to retry.

No Reversefield — message history walks newest-first by design. To iterate oldest-first, drive the loop yourself using MinID and the AddOffset: -limit trick.

Early exit

To stop iteration mid-stream, return telegram.ErrStopIteration from the callback. The library treats it as a clean stop and unwinds the inner pagination loop without surfacing an error.

count := 0
err := client.IterMessages(peerID, func(m *telegram.NewMessage) error {
	if !shouldProcess(m) {
		return nil
	}
	count++
	if count >= 100 {
		return telegram.ErrStopIteration
	}
	return nil
}, &telegram.SearchOption{})
count := 0
err := client.IterMessages(peerID, func(m *telegram.NewMessage) error {
	if !shouldProcess(m) {
		return nil
	}
	count++
	if count >= 100 {
		return telegram.ErrStopIteration
	}
	return nil
}, &telegram.SearchOption{})

Iter via channels

Some iterators predate the callback API and return Go channels instead. The most common is IterChatMembers, which gives you a participant channel and an error channel:

out, errs := client.IterChatMembers("@my_group", &telegram.ParticipantOptions{
	Filter: &telegram.ChannelParticipantsRecent{},
})

for {
	select {
	case u, ok := <-out:
		if !ok { return nil }
		handle(u)
	case err := <-errs:
		if err != nil { return err }
	}
}
out, errs := client.IterChatMembers("@my_group", &telegram.ParticipantOptions{
	Filter: &telegram.ChannelParticipantsRecent{},
})

for {
	select {
	case u, ok := <-out:
		if !ok { return nil }
		handle(u)
	case err := <-errs:
		if err != nil { return err }
	}
}

The result channel is closed when pagination finishes; the error channel is closed at the same time. Close from the consumer side by stopping the read loop — gogram observes the next send failing and tears down the producer goroutine.