Le Telegram API Python guide: working code for every approach
sendMessage call with demandes, a minimal bot with python-telegram-bot, a real linked account with Telethon, Pyrogram, or the Unipile SDK, and how to handle rate limits without your script crashing on the first 429. Whether you landed here looking for a telegram bot api python example or the full telegram api python picture, every approach below ships with working code.import demandes
Jeton = "123456:ABC-your-bot-token"
url = f"https://api.telegram.org/bot{TOKEN}/sendMessage"
charge utile = {
"chat_id": 123456789,
"texte": "Hello from Python",
"parse_mode": "MarkdownV2"
}
response = requêtes.poste(url, json=payload)
print(réponse.json())Three ways to build the Telegram API in Python, and which one to pick
demandes or a wrapper library like python-telegram-bot. The fastest way to ship, but a bot can only message a user who has written to it first.api_id et api_hash. Logs in as a real account, but you own session storage, reconnects and the 2FA flow.api_id / api_hash in the first place, see the step-by-step access guide.Build your Python integration
The Python Telegram Bot API with requests
demandes, no wrapper library involved. As of Bot API 10.2 (July 14, 2026), a bot token from BotFather and two parameters are all that sendMessage requires.123456:ABC-your-bot-token. Every Bot API call, including sendMessage, is a POST à https://api.telegram.org/bot<TOKEN>/sendMessage with a JSON body.import système d'exploitation
import demandes
Jeton = os.environ["TELEGRAM_BOT_TOKEN"]
URL = f"https://api.telegram.org/bot{TOKEN}/sendMessage"
charge utile = {
"chat_id": 123456789,
"texte": "Your order #4821 has shipped.",
"parse_mode": "MarkdownV2"
}
response = requêtes.poste(URL, json=payload, timeout=10)ok boolean. A non-200 status code or ok: false means the message was not sent, and the payload includes a human-readable description to log.données = réponse.json()
si response.status_code == 200 et données.obtenir("ok"):
message_id = données["result"]["message_id"]
print(f"sent, message_id={message_id}")
sinon:
print(f"failed: {data.get('description')}")| Parameter | Type | Description |
|---|---|---|
chat_id Requis | int or str | Target chat identifier, or @username for a public channel. |
text Requis | chaîne | Message text, 1 to 4096 characters after entity parsing. |
parse_mode Facultatif | chaîne | MarkdownV2 ou HTML, to render bold, links and code formatting in the text. |
disable_notification Facultatif | bool | Sends the message silently, without a push notification sound. |
reply_parameters Facultatif | dictionnaire | Sends the message as a reply to an existing message in the chat. |
Build past the bot-only limit
The Telegram Bot API in Python with python-telegram-bot
demandes calls work fine for a single outbound message. Past that, most Python codebases wrap the Telegram Bot API en python-telegram-bot, a library that turns the same HTTP endpoints into an async Python object model with built-in update polling.Application object that manages the update loop for you (polling or webhook)bot.send_message() instead of hand-built JSON payloads/start is a decorator, not a loopchat_id, text, parse_mode. The library still cannot make a bot message a user first, that restriction lives in Telegram's platform rules, not in the client you use to call it.from telegram import Mettre à jour
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
asynchrone déf commencer(update: Update, context: ContextTypes.DEFAULT_TYPE):
await context.bot.envoyer_message(
chat_id=update.effective_chat.id,
Texte="Hello from python-telegram-bot",
parse_mode="MarkdownV2"
)
application = ApplicationBuilder().jeton("123456:ABC-your-bot-token").construire()
app.add_handler(CommandHandler("début", start))
application.run_polling()A real user account in Python: Telethon and Pyrogram
api_id et api_hash from my.telegram.org. Two Python libraries do the MTProto heavy lifting: Telethon and Pyrogram.Telethon
Pyrogram
from telethon import TelegramClient
from telethon.errors import SessionPasswordNeededError
api_id = 1234567
api_hash = "your_api_hash_from_my.telegram.org"
# "my_account" is the session file name on disk, reused on every run
client = TelegramClient("my_account", api_id, api_hash)
asynchrone déf principal():
await client.commencer(phone="+15551234567")
await client.envoyer_message("username_or_id", "Hello from Telethon")
avec client :
client.loop.run_until_complete(main())client.start(phone=...) wraps auth.sendCode, which texts a login code to the phone number and returns a phone_code_hash used for the next call. The library prompts for the code and calls
auth.signIn with it. This is where a happy-path-only implementation stops. If the account has two-factor authentication enabled, Telegram answers with a 400 error: SESSION_PASSWORD_NEEDED. Telethon raises this as
SessionPasswordNeededError, an expected branch, not a bug to catch and ignore. Passing the account's cloud password to
client.start(password=...) runs the SRP exchange and auth.checkPassword for you. Calling the raw MTProto layer directly means building the InputCheckPasswordSRP object yourself. Telethon and Pyrogram both write a local session file (or an in-memory
StringSession) after the first successful login. Lose it, or deploy without persisting it, and your script has to run the phone-code and 2FA flow again on every restart. PASSWORD_HASH_INVALID
API_ID_PUBLISHED_FLOOD
api_id from my.telegram.org: reusing the example ID shipped in open-source sample code triggers API_ID_PUBLISHED_FLOOD for your end users, and only one api_id is issued per phone number.A real Telegram account in Python, without writing MTProto
providers: "TELEGRAM". There is no api_id, no session file to persist, and no SESSION_PASSWORD_NEEDED branch to write yourself: the linked account either shows as connecté or it doesn't.start_chat takes the linked account's ID and a user_ids list, and either opens a new conversation or delivers into the existing one if a chat already exists with that recipient.api_id / api_hash pair to request or rotateimport unipile
configuration = unipile.Configuration()
configuration.api_key["apiKey"] = "apikey"
api_client = unipile.ApiClient(configuration)
messaging_api = unipile.MessagingApi(api_client)
chat = messaging_api.start_chat(
"acc_123456789",
{"user_ids": ["0123456789"], "texte": "Hi, following up on your request."}
)Handling errors and rate limits in Python
429 or a FLOOD_WAIT_X, and what your Python code does next decides whether that is a five-second pause or a banned account.Maximum Bot API messages to the same individual chat
Maximum Bot API messages inside a single group
Approximate Bot API ceiling when broadcasting across chats
Status returned once any of these limits is crossed
import temps
import demandes
déf envoyer_avec_réessai(url, payload, max_retries=3):
pour tentative en plage(nombre_tentatives_max)
r = requêtes.poste(url, json=payload)
si r.status_code != 429:
return r
retry_after = r.json().obtenir("parameters", {}).obtenir("retry_after", 1)
temps.Dormir(retry_after)
soulever RuntimeError("too many 429 responses")from telethon.errors import FloodWaitError
import asyncio
asynchrone déf send_safely(client, entity, text):
essayer:
await client.envoyer_message(entity, text)
sauf FloodWaitError En tant que e :
# e.seconds is the FLOOD_WAIT_X value Telegram sent back
await asyncio.Dormir(e.seconds)
await client.envoyer_message(entity, text)Python Telegram Bot API vs Telethon/Pyrogram vs Unipile SDK
| Dimension | Bot API (requests / python-telegram-bot) | Telethon / Pyrogram (MTProto) | Unipile Python SDK |
|---|---|---|---|
| Can message a user first | Non | Yes, privacy-dependent | Oui, |
| Setup requirement | Bot token from BotFather | api_id / api_hash from my.telegram.org | Linked account via QR code or Hosted Auth |
| Gestion des sessions | Token-based, nothing to persist | You persist the session file yourself | Managed by Telegram's own Devices feature |
| 2FA / SESSION_PASSWORD_NEEDED | Sans objet | You handle the SRP exchange | Pris en charge pour vous |
| Group participants (get / add / remove) | Limited to bot permissions | Yes, self-built | Yes, dedicated v2 endpoints |
| Channels, communities, broadcasts | Yes, if added as admin | Oui, | Non pris en charge |
| Meilleur pour | Notifications and support bots | A fully custom Python MTProto client | Shipping fast without owning MTProto |
Telegram API in Python - FAQ
Common questions about the Python Telegram Bot API, python-telegram-bot, Telethon, Pyrogram, and connecting a real account with Unipile.
Oui. Le Telegram Bot API is a plain HTTP interface, so a single POST request with Python's demandes library to https://api.telegram.org/bot<TOKEN>/sendMessage, with a chat_id et text in the JSON body, is enough. No SDK or wrapper library is required for this call.
Le Telegram Bot API is Telegram's own HTTP interface, callable from Python with demandes or any HTTP client. python-telegram-bot is a third-party Python library that wraps those same HTTP endpoints in an async object model, with an Application class, typed methods like bot.send_message, and built-in update polling or webhook handling.
This is a structural rule of the Bot API, not a bug in your code. A bot cannot call sendMessage against a chat_id until that user has sent at least one message to the bot first. No parameter or Python library works around it. A real linked Telegram account, through Telethon, Pyrogram, or the Unipile SDK, does not have this restriction.
No. The Bot API only needs a bot token issued by BotFather. api_id et api_hash from my.telegram.org are required for the Client API (MTProto), the protocol used by Telethon, Pyrogram, and any client that logs in as a real user account instead of a bot.
SESSION_PASSWORD_NEEDED is a 400 error Telegram returns from auth.signIn when the account has two-factor authentication enabled. Telethon raises it as SessionPasswordNeededError. Clearing it means running the SRP protocol and calling auth.checkPassword with the account's cloud password, which Telethon and Pyrogram both handle when you pass the password to their login method.
The Bot API returns a 429 status once you exceed roughly one message per second to the same chat, 20 messages per minute in a group, or about 30 messages per second when broadcasting across chats. The response includes a retry_after value in seconds. On the MTProto side, the equivalent is a 420 FLOOD error or a FLOOD_WAIT_X exception, where X is the number of seconds to wait before retrying.
Oui. Le Unipile Python SDK connects an existing Telegram user account through Telegram's own Devices feature, either a QR code scan or Hosted Auth, and exposes messaging through a few SDK calls such as start_chat. There is no api_id to request, no MTProto implementation, and no session file or 2FA branch to write yourself.
Group participant management is supported: listing, adding, and removing participants through dedicated v2 endpoints. Channels, communities, broadcasts, group administration actions like approving or promoting members, chat archiving, and voice or video calls are not supported.
Vous avez encore des questions ? Notre équipe est là pour vous aider.