Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
🤝 layer-client
High-level async Telegram client for Rust.
Connect, authenticate, send messages, and stream updates with a clean async API.
Table of Contents
- Installation
- Feature Flags
- Minimal Bot — 15 Lines
- Connecting — ClientBuilder
- Authentication
- String Sessions — Portable Auth
- Update Stream
- Messaging
- InputMessage Builder
- Keyboards
- Media Upload & Download
- Text Formatting
- Reactions
- Typing Guard — RAII
- Participants & Chat Management
- Search
- Dialogs & Iterators
- Peer Types
- Peer Resolution & PeerRef
- Session Backends
- Transport & Networking
- Feature Flags
- Configuration Reference
- Error Handling
- Raw API Escape Hatch
- Shutdown
- Client Methods — Full Reference
📦 Installation
[]
= "0.4.6"
= { = "1", = ["full"] }
Get your api_id and api_hash from my.telegram.org.
🚩 Feature Flags
# SQLite session persistence
= { = "0.4.6", = ["sqlite-session"] }
# libsql / Turso session (local or remote)
= { = "0.4.6", = ["libsql-session"] }
# Hand-rolled HTML parser
= { = "0.4.6", = ["html"] }
# Spec-compliant html5ever parser (replaces built-in)
= { = "0.4.6", = ["html5ever"] }
StringSessionBackend, InMemoryBackend, and BinaryFileBackend are always available — no flag needed.
⚡ Minimal Bot — 15 Lines
use ;
async
🔨 Connecting — ClientBuilder
use Client;
let = builder
.api_id
.api_hash
.session // BinaryFileBackend
.catch_up // replay missed updates on reconnect
.connect
.await?;
ClientBuilder methods
| Method | Description |
|---|---|
.api_id(i32) |
Telegram API ID (required) |
.api_hash(str) |
Telegram API hash (required) |
.session(path) |
Binary file session at path |
.session_string(s) |
Portable base64 string session |
.in_memory() |
Non-persistent in-memory session (tests) |
.session_backend(Arc<dyn SessionBackend>) |
Inject a custom backend |
.catch_up(bool) |
Replay missed updates on connect (default: false) |
.dc_addr(str) |
Override first DC address |
.socks5(Socks5Config) |
Route all connections through a SOCKS5 proxy |
.allow_ipv6(bool) |
Allow IPv6 DC addresses (default: false) |
.transport(TransportKind) |
MTProto transport (default: Abridged) |
.retry_policy(Arc<dyn RetryPolicy>) |
Override flood-wait retry policy |
.build() |
Build Config without connecting |
.connect() |
Build and connect — returns (Client, ShutdownToken) |
🔑 Authentication
Bot login
if !client.is_authorized.await?
User login (phone + code + optional 2FA)
use SignInError;
if !client.is_authorized.await?
Sign out
client.sign_out.await?;
🔑 String Sessions — Portable Auth
// Export — works from any running client
let session_string = client.export_session_string.await?;
set_var;
// Restore — no phone/code flow needed
let session = var.unwrap_or_default;
let = builder
.api_id
.api_hash
.session_string
.connect
.await?;
📡 Update Stream
let mut stream = client.stream_updates;
while let Some = stream.next.await
IncomingMessage accessors
NewMessage =>
💬 Messaging
// Any peer format works — PeerRef accepts &str, i64, tl::enums::Peer
client.send_message.await?;
client.send_message.await?;
client.send_to_self.await?;
// From an incoming message
if let Some = msg.peer_id
// Edit
client.edit_message.await?;
// Forward
client.forward_messages.await?;
// Delete
client.delete_messages.await?;
// Pin / unpin
client.pin_message.await?; // notify = true
client.unpin_message.await?;
client.unpin_all_messages.await?;
// Get pinned message
let pinned = client.get_pinned_message.await?;
// Get reply-to message
let replied_to = client.get_reply_to_message.await?;
// Scheduled messages
let scheduled = client.get_scheduled_messages.await?;
client.delete_scheduled_messages.await?;
📝 InputMessage Builder
use ;
use ;
let = parse_markdown;
let kb = new
.row
.row;
client
.send_message_to_peer_ex
.await?;
InputMessage builder methods
| Method | Description |
|---|---|
InputMessage::text(str) |
Create with text |
.set_text(str) |
Change the text |
.reply_to(Option<i32>) |
Reply to message ID |
.silent(bool) |
No notification sound |
.background(bool) |
Send in background |
.clear_draft(bool) |
Clear the draft |
.no_webpage(bool) |
Suppress link preview |
.invert_media(bool) |
Show media above caption (Telegram ≥ 10.3) |
.schedule_once_online() |
Send when recipient goes online |
.schedule_date(Option<i32>) |
Schedule for Unix timestamp |
.entities(Vec<MessageEntity>) |
Formatted text entities |
.reply_markup(ReplyMarkup) |
Raw TL reply markup |
.keyboard(impl Into<ReplyMarkup>) |
InlineKeyboard or ReplyKeyboard |
.copy_media(InputMedia) |
Attach media from an existing message |
.clear_media() |
Remove attached media |
⌨️ Keyboards
Inline keyboard (triggers CallbackQuery)
use ;
let kb = new
.row
.row;
client
.send_message_to_peer_ex
.await?;
All button types: callback, url, url_auth, switch_inline, switch_elsewhere,
webview, simple_webview, request_phone, request_geo, request_poll, request_quiz,
game, buy, copy_text, text (reply keyboards only).
Reply keyboard
use ;
let kb = new
.row
.row
.resize
.single_use;
client
.send_message_to_peer_ex
.await?;
Answer callback queries
CallbackQuery =>
📎 Media Upload & Download
// Upload from bytes
let uploaded = client.upload_file.await?;
// Upload — parallel chunks (faster for large files)
let uploaded = client.upload_file_concurrent.await?;
// Upload from async reader
use File;
let f = open.await?;
let uploaded = client.upload_stream.await?;
// Send file (false = as document, true = as photo/media)
client.send_file.await?;
// Send album
client.send_album.await?;
// Download to bytes
let bytes = client.download_media.await?;
// Download to file
client.download_media_to_file.await?;
// Use the Downloadable trait (Photo, Document, Sticker)
use ;
let photo = from_media?;
let bytes = client.download.await?;
🖊️ Text Formatting
use ;
let = parse_markdown;
client
.send_message_to_peer_ex
.await?;
let md = generate_markdown;
With the html feature:
use ;
let = parse_html;
let html_str = generate_html;
💥 Reactions
use InputReactions;
// Simple emoji (&str converts automatically)
client.send_reaction.await?;
// Custom (premium) emoji
client.send_reaction.await?;
// Big animated reaction
client.send_reaction.await?;
// Remove all reactions
client.send_reaction.await?;
⌛ Typing Guard — RAII
TypingGuard keeps a typing/uploading indicator alive and cancels it on drop:
// Convenience methods on Client
let _typing = client.typing.await?;
let _typing = client.uploading_document.await?;
let _typing = client.recording_video.await?;
let _typing = client.typing_in_topic.await?;
// Any SendMessageAction via TypingGuard::start
use TypingGuard;
let _guard = start.await?;
// Forum topic with custom repeat delay
let _guard = start_ex.await?;
// Cancel immediately without waiting for drop
guard.cancel;
👥 Participants & Chat Management
use ;
// Fetch members
let members = client.get_participants.await?;
// Lazy iterator
let mut iter = client.iter_participants;
while let Some = iter.next.await?
// Kick (basic group)
client.kick_participant.await?;
// Ban with granular rights
client
.ban_participant
.await?;
// Permanent full ban
client
.ban_participant
.await?;
// Promote admin
client
.promote_participant
.await?;
// Get permissions
let perms = client.get_permissions.await?;
// Profile photos
let mut iter = client.iter_profile_photos;
while let Some = iter.next.await?
🔍 Search
use MessagesFilter;
// In-chat search
let results = client
.search
.min_date
.filter
.limit
.fetch
.await?;
// Only messages sent by me
let mine = client.search.sent_by_self.fetch.await?;
// Global search (all chats)
let global = client
.search_global_builder
.broadcasts_only
.limit
.fetch
.await?;
📜 Dialogs & Iterators
// Fetch first N dialogs
let dialogs = client.get_dialogs.await?;
for d in &dialogs
// Lazy dialog iterator
let mut iter = client.iter_dialogs;
while let Some = iter.next.await?
// Lazy message iterator for a peer
let mut iter = client.iter_messages;
while let Some = iter.next.await?
// Fetch by ID
let messages = client.get_messages_by_id.await?;
let latest = client.get_messages.await?;
// Read / clear
client.mark_as_read.await?;
client.clear_mentions.await?;
client.delete_dialog.await?;
🧑 Peer Types
High-level wrappers over raw TL types — no constant pattern-matching:
use ;
// User
if let Some = from_raw
// Channel / supergroup
if let Some = from_raw
// Unified Chat (Group or Channel)
if let Some = from_raw
🔗 Peer Resolution & PeerRef
Every method that takes a peer accepts impl Into<PeerRef>:
// All of these work anywhere a peer is expected:
client.send_message_to_peer.await?;
client.send_message_to_peer.await?;
client.send_message_to_peer.await?;
client.send_message_to_peer.await?; // Bot-API channel ID
client.send_message_to_peer.await?; // already resolved
// Explicit resolution
let peer = client.resolve_peer.await?;
let peer = client.resolve_peer.await?;
let peer = client.resolve_username.await?;
💾 Session Backends
// Binary file (default)
builder.session
// In-memory (tests)
builder.in_memory
// String session (serverless, env var)
builder.session_string
// SQLite (feature = "sqlite-session")
builder.session_backend
// LibSql / Turso (feature = "libsql-session")
builder.session_backend
🚂 Transport & Networking
use TransportKind;
use Socks5Config;
// Transport (default: Abridged)
builder.transport
builder.transport // DPI bypass
// SOCKS5 proxy — no auth
builder.socks5
// SOCKS5 proxy — with auth
builder.socks5
// Force request to a specific DC
client.invoke_on_dc.await?;
// Signal network restored (skips exponential backoff)
client.signal_network_restored;
⚠️ Error Handling
use ;
match client.send_message.await
FLOOD_WAIT is handled automatically by the default AutoSleep retry policy. To disable:
use NoRetries;
builder.retry_policy
🔩 Raw API Escape Hatch
Every Layer 224 method (2,329 total) is accessible:
use tl;
let req = SetBotCommands ;
client.invoke.await?;
🛑 Shutdown
let = connect.await?;
// Graceful shutdown from any task
shutdown.cancel;
// Immediate disconnect
client.disconnect;
ShutdownToken is a CancellationToken wrapper — clone and pass to multiple tasks.
Client Methods — Full Reference
All Client methods. Every async method returns Result<T, InvocationError> unless noted.
Connection & Session
| Method | Signature | Description |
|---|---|---|
Client::builder() |
→ ClientBuilder |
Fluent builder (recommended) |
Client::connect() |
async (Config) → (Client, ShutdownToken) |
Low-level connect |
client.is_authorized() |
async → bool |
Check if logged in |
client.save_session() |
async → () |
Persist current session |
client.export_session_string() |
async → String |
Export portable base64 session |
client.disconnect() |
sync |
Immediate disconnect |
client.signal_network_restored() |
sync |
Skip reconnect backoff |
client.sync_update_state() |
async → () |
Sync update sequence numbers |
Authentication
| Method | Signature | Description |
|---|---|---|
client.bot_sign_in(token) |
async → String |
Bot token login |
client.request_login_code(phone) |
async → LoginToken |
Start user login |
client.sign_in(token, code) |
async → String |
Complete user login |
client.check_password(token, pw) |
async → String |
Submit 2FA password |
client.sign_out() |
async → bool |
Log out |
client.get_me() |
async → tl::types::User |
Get own user info |
client.get_users_by_id(ids) |
async → Vec<Option<User>> |
Fetch users by ID |
Updates
| Method | Signature | Description |
|---|---|---|
client.stream_updates() |
sync → UpdateStream |
Typed async update stream |
stream.next() |
async → Option<Update> |
Next typed update |
stream.next_raw() |
async → Option<RawUpdate> |
Next raw TL update |
Messaging
| Method | Signature | Description |
|---|---|---|
client.send_message(peer, text) |
async → () |
Send text |
client.send_message_to_peer(peer, text) |
async → () |
Send text (explicit peer) |
client.send_message_to_peer_ex(peer, msg) |
async → () |
Send InputMessage |
client.send_to_self(text) |
async → () |
Send to Saved Messages |
client.edit_message(peer, id, text) |
async → () |
Edit a message |
client.edit_inline_message(id, msg) |
async → () |
Edit an inline message |
client.forward_messages(from, to, ids) |
async → () |
Forward messages |
client.forward_messages_returning(from, to, ids) |
async → Vec<Message> |
Forward + return new messages |
client.delete_messages(peer, ids) |
async → () |
Delete messages |
client.get_messages(peer, limit) |
async → Vec<Message> |
Fetch latest messages |
client.get_messages_by_id(peer, ids) |
async → Vec<Option<Message>> |
Fetch by ID |
client.get_pinned_message(peer) |
async → Option<Message> |
Get pinned message |
client.pin_message(peer, id, notify) |
async → () |
Pin a message |
client.unpin_message(peer, id) |
async → () |
Unpin a message |
client.unpin_all_messages(peer) |
async → () |
Unpin all messages |
client.get_reply_to_message(peer, id) |
async → Option<Message> |
Get message replied to |
client.get_scheduled_messages(peer) |
async → Vec<Message> |
List scheduled messages |
client.delete_scheduled_messages(peer, ids) |
async → () |
Cancel scheduled messages |
Inline Mode (bots)
| Method | Signature | Description |
|---|---|---|
client.answer_inline_query(qid, results, cache, personal, next) |
async → () |
Answer inline query |
client.answer_callback_query(qid, text, alert) |
async → () |
Answer callback query |
Media
| Method | Signature | Description |
|---|---|---|
client.upload_file(name, bytes) |
async → UploadedFile |
Upload (sequential) |
client.upload_file_concurrent(name, bytes) |
async → UploadedFile |
Upload (parallel chunks) |
client.upload_stream(name, reader) |
async → UploadedFile |
Upload from AsyncRead |
client.send_file(peer, file, as_photo) |
async → () |
Send uploaded file |
client.send_album(peer, files) |
async → () |
Send multiple files as album |
client.download_media(loc) |
async → Vec<u8> |
Download to memory |
client.download_media_to_file(loc, path) |
async → () |
Stream download to file |
client.download_media_concurrent(loc) |
async → Vec<u8> |
Parallel download |
client.download(item) |
async → Vec<u8> |
Download Downloadable (Photo/Doc/Sticker) |
client.iter_download(location) |
sync → DownloadIter |
Lazy chunk iterator |
Chat Actions
| Method | Signature | Description |
|---|---|---|
client.send_chat_action(peer, action, topic) |
async → () |
One-shot chat action |
client.typing(peer) |
async → TypingGuard |
RAII typing indicator |
client.uploading_document(peer) |
async → TypingGuard |
RAII upload indicator |
client.recording_video(peer) |
async → TypingGuard |
RAII video recording indicator |
client.typing_in_topic(peer, topic_id) |
async → TypingGuard |
RAII typing in forum topic |
client.mark_as_read(peer) |
async → () |
Mark all messages as read |
client.clear_mentions(peer) |
async → () |
Clear @mention badges |
Reactions
| Method | Signature | Description |
|---|---|---|
client.send_reaction(peer, msg_id, reaction) |
async → () |
React / unreact; accepts &str or InputReactions |
Dialogs & Peers
| Method | Signature | Description |
|---|---|---|
client.get_dialogs(limit) |
async → Vec<Dialog> |
Fetch dialogs |
client.iter_dialogs() |
sync → DialogIter |
Lazy dialog iterator |
client.iter_messages(peer) |
sync → MessageIter |
Lazy message iterator |
client.delete_dialog(peer) |
async → () |
Leave and delete dialog |
client.join_chat(peer) |
async → () |
Join public chat |
client.accept_invite_link(link) |
async → () |
Accept invite link |
Client::parse_invite_hash(link) |
sync → Option<&str> |
Extract hash from link |
client.resolve_peer(str) |
async → Peer |
Resolve username / phone / "me" |
client.resolve_username(str) |
async → Peer |
Resolve bare username |
client.resolve_to_input_peer(str) |
async → InputPeer |
Resolve to InputPeer |
Search
| Method | Signature | Description |
|---|---|---|
client.search(peer, query) |
sync → SearchBuilder |
In-chat search builder |
client.search_messages(peer, q, limit) |
async → Vec<IncomingMessage> |
Quick in-chat search |
client.search_global_builder(query) |
sync → GlobalSearchBuilder |
Global search builder |
client.search_global(q, limit) |
async → Vec<IncomingMessage> |
Quick global search |
Participants
| Method | Signature | Description |
|---|---|---|
client.get_participants(peer, limit) |
async → Vec<Participant> |
Fetch members |
client.iter_participants(peer) |
sync → impl Iterator |
Lazy member iterator |
client.search_peer(query) |
async → Vec<Peer> |
Search contacts/dialogs |
client.kick_participant(peer, user_id) |
async → () |
Kick from basic group |
client.ban_participant(peer, user_id, rights) |
async → () |
Ban/restrict with BannedRightsBuilder |
client.promote_participant(peer, user_id, rights) |
async → () |
Promote with AdminRightsBuilder |
client.get_permissions(peer, user_id) |
async → ParticipantPermissions |
Check user's effective permissions |
client.get_profile_photos(user_id, offset, limit) |
async → Vec<Photo> |
Fetch profile photos |
client.iter_profile_photos(user_id) |
sync → ProfilePhotoIter |
Lazy profile photo iterator |
Raw & Advanced
| Method | Signature | Description |
|---|---|---|
client.invoke(req) |
async → R::Return |
Call any Layer 224 TL method |
client.invoke_on_dc(req, dc_id) |
async → R::Return |
Call on a specific DC |
client.cache_users_slice_pub(users) |
async → () |
Manually populate peer cache |
client.cache_chats_slice_pub(chats) |
async → () |
Manually populate peer cache |
client.rpc_call_raw_pub(req) |
async → Vec<u8> |
Raw RPC bytes |