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 batch | Paginated stream |
|---|---|
GetMessages | IterMessages |
GetChatMembers | IterChatMembers |
GetDialogs | IterDialogs |
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
Itermeans unbounded; zero onGetmeans 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
AddOffsetto skip N results after the offset id. - MinID / MaxID
int32 - Bound the id range you want back. Setting
MinIDis 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
Iteris 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,
nilto 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.
