This is the maintained continuation of
go-telegram-bot-api/telegram-bot-api. The original project stopped at Bot API 6.0 (April 2022) and no longer accepts changes. This fork supports Bot API 10.3 (August 24, 2026) and follows the official changelog.
All methods are fairly self-explanatory, and reading the godoc page should explain everything. If something isn't clear, open an issue or submit a pull request.
The scope of this project is just to provide a wrapper around the API without any additional features. There are other projects for creating something with plugins and command handlers without having to design all that yourself.
More tutorials and high-level information live in the docs
directory.
go get github.com/bssth/telegram-bot-api/v6@latestimport tgbotapi "github.com/bssth/telegram-bot-api/v6"Go 1.24 or newer is required.
The major version is v6: the API is not source-compatible with upstream
v5 (see below), so the fork starts a new major version instead of breaking
code that pins /v5.
-
Change the import path. The package name stays
tgbotapi, so nothing else in your code needs to be renamed:grep -rl 'github.com/go-telegram-bot-api/telegram-bot-api/v5' --include='*.go' . \ | xargs sed -i 's#github.com/go-telegram-bot-api/telegram-bot-api/v5#github.com/bssth/telegram-bot-api/v6#g' go get github.com/bssth/telegram-bot-api/v6@latest go mod tidy
(On macOS use
sed -i ''.) Areplacedirective ingo.modis not enough: Go requires a replacement module to declare the same module path as the one it replaces. -
Fix whatever no longer compiles using BREAKING.md. It lists every source-incompatible change between upstream v5.5.1 (Bot API 6.0) and this fork, grouped by topic, with before/after snippets. Most of them come from Telegram itself:
ThumbbecameThumbnail,ReplyToMessageIDbecameReplyParameters,DisableWebPagePreviewbecameLinkPreviewOptions, and so on.
BREAKING.md is written so it can be handed to a coding assistant together
with the files that use tgbotapi; it handles most of the mechanical
rewrites in one pass. Review the diff, run go build ./..., done.
This is a very simple bot that just displays any gotten updates, then replies it to that chat.
package main
import (
"log"
tgbotapi "github.com/bssth/telegram-bot-api/v6"
)
func main() {
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken")
if err != nil {
log.Panic(err)
}
bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName)
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates := bot.GetUpdatesChan(u)
for update := range updates {
if update.Message != nil { // If we got a message
log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text)
msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text)
msg.ReplyParameters = &tgbotapi.ReplyParameters{
MessageID: update.Message.MessageID,
}
bot.Send(msg)
}
}
}If you need to use webhooks, you may use a slightly different method.
package main
import (
"log"
"net/http"
tgbotapi "github.com/bssth/telegram-bot-api/v6"
)
func main() {
bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken")
if err != nil {
log.Fatal(err)
}
bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName)
wh, _ := tgbotapi.NewWebhookWithCert("https://www.example.com:8443/"+bot.Token, tgbotapi.FilePath("cert.pem"))
_, err = bot.Request(wh)
if err != nil {
log.Fatal(err)
}
info, err := bot.GetWebhookInfo()
if err != nil {
log.Fatal(err)
}
if info.LastErrorDate != 0 {
log.Printf("Telegram callback failed: %s", info.LastErrorMessage)
}
updates := bot.ListenForWebhook("/" + bot.Token)
go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil)
for update := range updates {
log.Printf("%+v\n", update)
}
}If you need, you may generate a self-signed certificate, as this requires HTTPS / TLS. The above example tells Telegram that this is your certificate and that it should be trusted, even though it is not properly signed.
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3560 -subj "//O=Org\CN=Test" -nodes
Now that Let's Encrypt is available, you may wish to generate your free TLS certificate there.
Every Bot API version from 6.1 through 10.3 is supported: all types, fields, methods and parameters of the current specification are present. Highlights of the last releases:
- 10.3 — rich message buttons (
RichMessageButton, the "buttons", "document" and "expandable_blockquote" blocks), structured ephemeral send parameters (EphemeralMessageParameters), disabled buttons and force-reply keyboards (DisabledButton,InlineKeyboardButton.Disabled), stoppable drafts (CanStop/KeepOnStop,Update.StoppedMessageGeneration), welcome message rights,CommunityChatJoined, gift text fields. - 10.2 — block-structured rich messages (
InputRichBlock), explicit media for rich messages (InputRichMessageMedia,InputMediaVoiceNote), ephemeral messages (ReplyParameters.EphemeralMessageID, theEditEphemeralMessage*andDeleteEphemeralMessageconfigs,Message.ReceiverUser), communities, payment subscription updates. - 10.1 — rich messages (
NewRichMessage,SendRichMessageDraftConfig,RichMessage/RichText/RichBlock), join request queries (AnswerChatJoinRequestQueryConfig,SendChatJoinRequestWebAppConfig), poll links (InputMediaLink). - 10.0 — guest mode (
AnswerGuestQuery,Update.GuestMessage), live photos (NewLivePhoto,InputMediaLivePhoto), poll media for questions, options and explanations, reaction administration, managed bot access settings. - 9.x — managed bots, checklists, suggested posts, direct messages in channels, gifts and Telegram Stars, business accounts, stories.
- 7.x – 8.x — replies 2.0 (
ReplyParameters), link preview options, reactions, boosts, giveaways, business connections, paid media, Mini App improvements, and much more.
See the git history for the per-version commits; each Full support of API X
commit message lists what that version added.
internal/cmd/specdiff compares the code with the machine-readable
Bot API specification
and lists every missing type, field, method and parameter:
go run ./internal/cmd/specdiff # add -v to also list non-spec extrasThe Bot API spec workflow runs it weekly, so a new Bot API release shows up as a failed run.
The bulk of the Bot API 6.1 → 10.3 work comes from go-telegram-bot-api/telegram-bot-api#794 by @kirugan, which was never merged upstream. This fork merges it with authorship preserved and builds on top of it with a spec-driven audit (see below), plus ideas from other unmerged upstream pull requests.
- Placeholder types that only kept raw JSON (
VideoQuality,UserRating,UserProfileAudios,GiftBackground,UniqueGiftColors) are real structs. OwnedGiftdecodes unique gifts correctly;repostStory,getUserGifts,getChatGifts,setStickerSetThumbnail,setGameScore(force) andsendDocument(caption_entities) send the parameters the Bot API expects;suggested_post_parametersis supported by every send method;setPassportDataErrorsis available.- Files nested in polls and rich messages are uploaded via
attach://instead of being serialized into the JSON. Update.FromChat/SentFromcover all update kinds and no longer panic on callback queries from inline messages.- Transport errors no longer leak the bot token, and
GetFileDirectURLhonoursSetFileEndpoint.
Real bugs and gaps reported against the original repository that are now closed in this fork:
- #781
/ #745
—
SetGameScoreConfigserialized the score under the keyscrore; game scores never reached Telegram. - #628
—
GetUpdatesChanlogged rawhttp.Posterrors, which embed the full request URL including the bot token. The token is now redacted before logging. - #683
—
FileEndpointwas a hard-coded package constant, soGetFileDirectURLstill pointed atapi.telegram.orgwhen you were running a local Bot API server. There is now aSetFileEndpoint/FileLinkpair and the field is per-bot. - #740
—
InlineConfig.CacheTime = 0was silently dropped byAddNonZero, so Telegram applied its 300s default instead of disabling the cache.cache_timeis now serialized unconditionally. - #639
/ #705
—
Send()tried to unmarshal the baretruereturned by methods likebanChatMember/setChatTitle/sendChatAction, producingjson: cannot unmarshal bool into Message. It now returns a zeroMessage, nilfor those shapes. (PreferRequestfor methods whose documented return type is not aMessage.) - #624
— README webhook example passed
"cert.pem"as astringtoNewWebhookWithCert, which takes aRequestFileData. Example now usestgbotapi.FilePath("cert.pem").
Small enhancements that didn't correspond to a filed issue but were worth doing while the code was already open:
- Multipart upload name collision fix.
prepareInputMediaFilewas usingfile-%dfor both the main media and the thumbnail onInputMediaAudio/InputMediaDocument. The two multipart fields collided in the same form body; any audio or document upload with a thumbnail probably didn't work on the wire. Thumbnails now usefile-%d-thumbnail, matching the existingInputMediaVideopattern. closeBodydrains response body before close.json.Decodercan stop short of EOF, andnet/httpwill discard the underlying TCP connection if the body isn't fully consumed — no keep-alive reuse. The fork drains the remainder beforeClose().Params.AddAny(key, value any) error. Replaces the olderAddInterface— same behavior, but usesanyand has a clearer name.AddInterfaceis kept as an alias for existing callers.Params.AddFirstValiderrors are propagated. The upstream pattern ignored its return value insideparams()methods, so JSON marshaling errors in complex fields were silently swallowed. The fork consistently captures and returns it.json.RawMessagefor JSON-serialized string fields. Fields the Telegram docs describe as "JSON-serialized object" (e.g.provider_dataonInvoiceConfig/InvoiceLinkConfig) are nowjson.RawMessageinstead ofstring, so you can hand them an already-marshaled payload without double-encoding.