Erlang library for Telegram Bot API.
It contains all methods and types available in Telegram Bot API 9.6, released April 3, 2026.
telegram_bot_api is a modern, fully-featured library that supports all Bot API methods and types. It's designed for developers who want a stable and comprehensive foundation for their projects, with built-in support for the Cowboy web server and a worker pool for efficient request handling.
In the world of messaging platforms, Telegram stands out for its robust API and bot support. For developers who prioritize fault tolerance, scalability, and real-time performance, using Erlang to build Telegram bots is a strategic choice. Leveraging the power of the BEAM virtual machine ensures that your bot can handle millions of users and concurrent messages without breaking a sweat.
- Use case: Ideal for production-grade bots that require high reliability and full API coverage.
Erlang was designed for massive concurrency and "nine-nines" availability. When building a Telegram bot, these features translate to:
- Massive Scalability: Effortlessly manage thousands of simultaneous webhook updates or long-polling connections.
- Hot Code Reloading: Update your bot’s logic on the fly without restarting the service or dropping user sessions.
- Fault Isolation: If one bot process crashes, it won't affect the rest of the system, thanks to Erlang’s "let it crash" philosophy.
Building a Telegram bot with Erlang is the best path for developers who value reliability and performance. Whether you are creating a complex fintech assistant or a high-traffic notification service, an Erlang-based library provides the foundation you need for professional-grade messaging solutions.
Explore our Erlang Telegram Bot Library today and start scaling your bot to the moon!
The package can be installed by adding telegram_bot_api to your list of dependencies
in
rebar.config:
{deps, [telegram_bot_api]}.1. Get a Telegram API token, use telegram bot @BotFather for receiving a token
Token = <<"1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ">>,2. Create pool. Each bot is a separate pool of workers: 1 Bot = 1 Pool → N Workers
Pool=mybot1,
{ok, Pid} = telegram_bot_api_sup:start_pool(#{
name=>Pool,
token=>Token,
workers=>1
}).telegram_bot_api_sup:start_pool
3. Send request Telegram Bot Api.
After creating an HTTP pool, you can call any Telegram API method directly using the same names as in the official documentation.
Simply use:
Result=telegram_bot_api:MethodName(Pool, Params, Async, Timeout)Parameters explained:
Pool– your HTTP poolParams– request parameters are always mapsAsync– true for asynchronous calls, false for synchronousTimeout– call timeout in milliseconds
💡 Method names match the Telegram Bot API exactly — no extra wrapping.
Return value of the function:
case Result of
{ok, Ref}->ok; %is Async=true
{ok, HttpCode, MapJson}->ok; %is Async=false
{error, Er}->error;
{Error, Reason}->error
end.sendMessage → pool → worker → Telegram API
{ok,200,Result} = telegram_bot_api:sendMessage(Pool,#{
chat_id=>ChatId,
text=><<"Text">>
}
).{ok,Ref} = telegram_bot_api:sendMessage(Pool,#{
chat_id=>ChatId,
text=><<"Text">>
},
true).
%%the response will come as process messages
receive
{async,Ref,{ok,200,Map}}->ok
%%use in telegram_bot_api_file:download
{async, Ref, saved_to_file}->ok
{error, Reason}->error
end.{ok, Pid1}=gen_event:start_link({global, my_event}),
gen_event:add_handler(my_event, my_event_handler1, [#{name=>Pool}]),
{ok, Pid2} = telegram_bot_api_sup:start_update(#{
name=>Pool,
update_time=>1000,
offset=>0,
limit=>100,
event=>{global,my_event},% or Pid1. new messages will be sent to this process
allowed_updates=>[message]% see https://hexdocs.pm/telegram_bot_api/telegram_bot_api.html#t:update_type/0
}).telegram_bot_api_sup:start_update
telegram_bot_api_updater_server
WebhookId=telegram_bot_api_webhook_server:name_server({0,0,0,0},8443),
{ok,WebhookPid}=telegram_bot_api_sup:start_webhook(#{
id=>WebhookId,%% name process, may not be specified, then create an ID for the supervisor by calling telegram_bot_api_webhook_server:name_server
secret_token=><<"my_secret">>,%this is the secret_token that is set in the Parameter method setWebhook https://core.telegram.org/bots/api#setwebhook
bots=>#{
%%set bots when creating a webhook or add them later via add_bot
%% add 1 bot
atom_to_binary(Pool)=>#{
event=>{global,my_event},% the message will come here
name=>Pool
}
%%.. other bots
},
transport_opts=>#{
ip=>{0,0,0,0},
port=>8443,
%% see https://core.telegram.org/bots/self-signed
certfile=>"/etc/telegram_bot_api/ssl/YOURPUBLIC.pem",
keyfile=>"/etc/telegram_bot_api/ssl/YOURPRIVATE.key",
verify=> verify_none
}
}).
{ok,WebhookPid}=global:whereis_name(WebhookId).telegram_bot_api_sup:start_webhook
telegram_bot_api_webhook_server
%%add
ok= telegram_bot_api_webhook_server:add_bot(
{global,WebhookId},%or WebhookPid
<<"mybot_pool">>,
#{
event=>{global,my_event},
name=>mybot_pool
}
),
%%delete
telegram_bot_api_webhook_server:delete_bot({global,WebhookId},mybot_pool).telegram_bot_api_webhook_server:add_bot telegram_bot_api_webhook_server:delete_bot
telegram_bot_api:setWebhook(Pool,#{
url=>telegram_bot_api_webhook_server:make_url(<<"8.8.8.8">>, <<"8443">>, <<"mybot_pool">>),% make url: https://8.8.8.8:8443/telegram/mybot_pool/update
ip_address=><<"8.8.8.8">>,
certificate=>#{
file=><<"/etc/telegram_bot_api/ssl/YOURPUBLIC.pem">>,
name=><<"YOURPUBLIC.pem">>
},
secret_token=><<"my_secret">>% this token will be checked inside the handler cowboy, must match start_webhook
}). {ok,200,Result} = telegram_bot_api:getMe(Pool,#{}).{ok,200,#{ok := true,result := true}} = telegram_bot_api:logOut(Pool,#{}).telegram_bot_api:setMyName(Pool,#{name=><<"Бот">>,language_code=>ru}).telegram_bot_api:setMyProfilePhoto(Pool,#{
photo=>
#{
type=><<"static">>,
photo=><<"attach://myfile">>
},
myfile=>#{
file=><<"/etc/telegram_bot_api/file.jpg">>,
name=><<"file.jpg">>
}
}).telegram_bot_api:setMyCommands(Pool,#{
commands=>[
#{
command=><<"/test">>,
description=><<"Test>>
}]
}).telegram_bot_api:sendContact(Pool,#{
chat_id=>ChatId,
phone_number=><<"+79281111111">>,
first_name=><<"CONTACT">>
})telegram_bot_api:sendPhoto(Pool,#{
chat_id=><<"@channelusername">>,
photo=>#{file=><<"/dir/file.jpg">>}
}).telegram_bot_api:sendAudio(Pool,#{
chat_id=>ChatId,
audio =>#{file=><<"/dir/file.mp3">>,name=><<"file123.mp3">>}
}).telegram_bot_api:sendVoice(Pool,#{
chat_id=> ChatId,
voice =>#{file=> <<"/dir/sample-3s.mp3">>}
}).%% After sending a file, you can send the file again knowing the file_id
{ok,200,#{ok := true, result := #{photo:=[#{file_id:=FileId}|_]} }} = telegram_bot_api:sendPhoto(Pool,#{
chat_id=> ChatId,
photo=> #{file=> <<"/dir/photo_1.jpg">>},
caption=> <<"input file">>
}),
%% Send files photo, 3 in three different ways:
AttachFileKey1=file1,
telegram_bot_api:sendMediaGroup(Pool,#{
chat_id=> ChatId,
media =>[
%% 1. file_attach_url
#{
type=>photo,
media=><<"attach://",(atom_to_binary(AttachFileKey1))/binary>>
},
%% 2. file_id
#{
type=>photo,
media=>FileId
},
%% 3. file_local_url - if start_pool http endpoint=my site
#{
type=>photo,
media=><<"file://","/var/lib/telegram/photo.jpg">>
}
],
%% 1.1 key map = match media attach://key map
AttachFileKey1 =>
#{
file=> <<"/dir/photo_1.jpg">>
}
}).telegram_bot_api:getFile(Pool,{file_id=>FileId}).-include_lib("telegram_bot_api/include/message_reaction.hrl").
telegram_bot_api:setMessageReaction(Pool,#{
chat_id=>ChatId,
message_id=>MessageId,
reaction=>[
#{
type=>?REACTION_TYPE_EMOJI, % REACTION_TYPE_EMOJI | REACTION_TYPE_CUSTOM_EMOJI | REACTION_TYPE_PAID see https://core.telegram.org/bots/api#reactiontype
emoji=>?REACTION_OK_HAND % or telegram_bot_api_emoji:random_reaction()
}
]},Async),-include_lib("telegram_bot_api/include/message_effect.hrl").
telegram_bot_api:sendMessage(Pool,#{
chat_id=>ChatId,
text=><<"text">>,
message_effect_id=>?MESSAGE_EFFECT_FIRE
}).telegram_bot_api:editMessageText(Pool,#{
chat_id=>ChatId,
text=><<"text">>,
message_id=>MessageId
}).telegram_bot_api:editMessageCaption(Pool,#{
chat_id=>ChatId,
message_id=>MessageId,
parse_mode =><<"HTML">>,
caption=><<"<a href=\"tg://user?id=123\">User</a><code>123</code>">>
}).telegram_bot_api:deleteMessage(Pool,#{
chat_id=>ChatId,
message_id=>MessageId
}).telegram_bot_api:sendMessage(Pool,#{
chat_id=>ChatId,
text=><<"inline_keyboard">>,
reply_markup=>
#{
inline_keyboard=>
[
[
#{
text=><<"yes">>,
callback_data=><<"callback_yes">>
},
#{
text=><<"no">>,
callback_data=><<"callback_no">>
}
]
]
}
}).telegram_bot_api:sendMessage(Pool,#{
chat_id=>ChatId,
text=><<"inline_keyboard_style">>,
reply_markup=>
#{
inline_keyboard=>
[
[
#{
text=><<"red">>,
style=><<"danger">>,
callback_data=><<"callback_red">>
},
#{
text=><<"green">>,
style=><<"success">>,
callback_data=><<"callback_green">>
},
#{
text=><<"blue">>,
style=><<"primary">>,
callback_data=><<"callback_blue">>
},
#{
text=><<"default">>,
callback_data=><<"callback_default">>
}
]
]
}
}).telegram_bot_api:answerCallbackQuery(Pool,#{
callback_query_id=>Id
}).telegram_bot_api:editMessageReplyMarkup(Pool,#{
message_id=>MessageId,
chat_id=>ChatId,
reply_markup=>
#{
inline_keyboard=>
[
[
#{
text=><<"ok">>,
callback_data=><<"callback_ok">>
}
]
]
}
}).-include_lib("telegram_bot_api/include/chat_action.hrl").
telegram_bot_api:sendChatAction(Pool,#{
chat_id=>ChatId,
action=>?CHAT_ACTION_UPLOAD_PHOTO
}).telegram_bot_api:setChatPhoto(Pool,#{
chat_id=> ChatId,
photo=> #{file=> <<"/dir/photo_1.jpg">>}
},false),-include_lib("telegram_bot_api/include/message_dice.hrl").
Result=telegram_bot_api:sendDice(Pool,#{
chat_id=>ChatId,
emoji=>?DICE_BOWLING,% or telegram_bot_api_emoji:random_dice()
protect_content=>true
}).
%%
%%{ok,200,#{ok := true,result :=#{ message_id := MessageId, chat:=#{id:=ChatId}, dice :=#{value :=Value emoji:= Emoji} } }}=Result,
%%IsWin=telegram_bot_api_emoji:is_win_dice(Emoji,Value). % true or falsetelegram_bot_api:sendMessageDraft(Pool,#{
chat_id=>ChatId,
draft_id=>DrafId,
text=>Text,
message_thread_id=>ThreadId
})-include_lib("telegram_bot_api/include/message_topic.hrl").
telegram_bot_api:createForumTopic(Pool,#{
chat_id=>ChatId,
name=><<"name topic">>,
icon_color=>?TOPIC_ICON_COLOR_CREAMY
icon_custom_emoji_id=>?TOPIC_EMOJI_NEWSPAPER_ID % or telegram_bot_api_emoji:random_topic()
}).telegram_bot_api:editForumTopic(Pool,#{
chat_id=>ChatId,
message_thread_id=>ThreadId,
name=>Text
}).mute_chat_member(Pool, ChatId, UserId, Minute) ->
Result = telegram_bot_api:restrictChatMember(Pool, #{
chat_id => ChatId,
user_id => UserId,
until_date => erlang:system_time(seconds) + (60 * Minute),
permissions => #{
can_send_messages => false,
can_send_audios => false,
can_send_documents => false,
can_send_photos => false,
can_send_videos => false,
can_send_video_notes => false,
can_send_voice_notes => false,
can_send_polls => false,
can_send_other_messages => false,
can_add_web_page_previews => false,
can_change_info => false,
can_invite_users => false,
can_pin_messages => false,
can_manage_topics => false
}
}),
case Result of
{ok, 200, #{ok := true, result := true}} -> true;
_ -> false
end.ban_chat_member(Pool, ChatId, UserId, Minute) ->
Result = telegram_bot_api:banChatMember(Pool, #{
chat_id => ChatId,
user_id => UserId,
until_date => erlang:system_time(seconds) + (60 * Minute)
}),
case Result of
{ok, 200, #{ok := true, result := true}} -> true;
_ -> false
end.%%Get a user role in a group
case telegram_bot_api:getChatMember(Pool, #{
chat_id=>ChatId,
user_id=>UserId
}) of
{ok,200, #{ok := true, result := #{status := Status} } }->
Status;%creator or administrator or member or restricted or left or kicked; see type ChatMember
_->false
end.
telegram_bot_api:getUserProfileAudios(Pool,#{user_id=>1234}).case telegram_bot_api:setChatMemberTag(Pool, #{
chat_id=>ChatId,
user_id=>UserId,
tag=><<"tag">>
}) of
{ok,200,#{ok := true,result := true}}->ok;
%If you can't install the tag, you'll get an error example {ok,400,#{ok => false,description => <<"Bad Request: CHAT_CREATOR_REQUIRED">>,error_code => 400}} ->error;
_->error
end.