At a glance
/newbotin @BotFather returns the bot token; keep it secret.- Message the bot first, then read
message.chat.idfromgetUpdates. sendMessagetakes 1 to 4,096 characters;parse_modeHTML avoids MarkdownV2 escaping.- Limits: about 1 message per second per chat and 20 per minute in a group.
Create a bot with BotFather
Your script needs a bot token before it can message you.
- Open a chat with
@BotFatherin Telegram. - Send
/newbot. - Pick a display name, then a username that ends in "bot", like
tetris_bot. - Copy the token BotFather sends back, which looks like
123456789:AA....
Keep that token secret: it controls the bot.
Find your chat_id
A bot can't start a conversation, so message it first. Then call getUpdates and read message.chat.id: that number is where your script sends alerts.
For alerts in a group, add the bot to the group first. Privacy mode is on by default for bots that are not group admins, so the bot only sees commands meant for it. Send a command like /start@your_bot in the group to create an update.
Two things trip people up:
getUpdatesdoes not work while an outgoing webhook is set on the bot.- Telegram keeps undelivered updates for at most 24 hours, so send a fresh message if the result is empty.
import os
import requests
token = os.environ["TELEGRAM_BOT_TOKEN"]
updates = requests.get(f"https://api.telegram.org/bot{token}/getUpdates", timeout=10).json()
for update in updates["result"]:
message = update.get("message") or update.get("channel_post") or {}
chat = message.get("chat")
if chat:
print(chat["id"], chat.get("title") or chat.get("first_name"))Send a message with requests
With the token and chat_id, an alert is one HTTPS POST to https://api.telegram.org/bot<token>/sendMessage with chat_id and text. The text must be 1 to 4,096 characters after entities parsing.
parse_mode accepts HTML or MarkdownV2, and HTML is easier in scripts. MarkdownV2 needs a backslash before _, *, [, ], (, ), ~, ` `, >, #, +, -, =, |, {, }, . and !, which breaks on almost every filename or traceback. With HTML you only escape <, > and &, and html.escape` does that.
import html
import os
import requests
TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
CHAT_ID = os.environ["TELEGRAM_CHAT_ID"]
def send(text):
response = requests.post(
f"https://api.telegram.org/bot{TOKEN}/sendMessage",
json={"chat_id": CHAT_ID, "text": text, "parse_mode": "HTML"},
timeout=10,
)
response.raise_for_status()
return response.json()["result"]["message_id"]
filename = html.escape("db_2026-10-30.sql.gz")
send(f"<b>Backup finished</b>\nFile: <code>{filename}</code>")Send with python-telegram-bot v22
python-telegram-bot is at version 22.8 (released 2026-06-12) and needs Python 3.10 or newer. It has been asyncio-only since version 20, so you await bot.send_message(...) inside an async function and start it with asyncio.run.
The library is worth it when your script also has to receive commands. For one-way alerts from a job or server, plain requests is enough.
import asyncio
import os
from telegram import Bot
async def main():
async with Bot(os.environ["TELEGRAM_BOT_TOKEN"]) as bot:
await bot.send_message(chat_id=os.environ["TELEGRAM_CHAT_ID"], text="Backup finished")
asyncio.run(main())Telegram limits for bots
The rate limit depends on whether you message one chat, a group or many users.
| Limit | Value |
|---|---|
| Message text length | 1 to 4,096 characters after entities parsing |
| Messages to one chat | About 1 per second; short bursts may pass before 429 errors |
| Messages to a group | No more than 20 per minute |
| Bulk broadcast | About 30 messages per second |
| Over the limit | HTTP 429 with parameters.retry_after, the seconds to wait |
| Undelivered updates | Kept until the bot fetches them, at most 24 hours |
When you get a 429, wait the number of seconds in parameters.retry_after and send again:
import time
def send_with_retry(text, attempts=3):
for _ in range(attempts):
response = requests.post(
f"https://api.telegram.org/bot{TOKEN}/sendMessage",
json={"chat_id": CHAT_ID, "text": text},
timeout=10,
)
if response.status_code != 429:
response.raise_for_status()
return response.json()["result"]["message_id"]
time.sleep(response.json()["parameters"]["retry_after"])
response.raise_for_status()Progress bars and run links in Telegram
To watch a loop without a new message per step, tqdm ships tqdm.contrib.telegram. Pass token and chat_id to tqdm(iterable, token=..., chat_id=...), or set TQDM_TELEGRAM_TOKEN and TQDM_TELEGRAM_CHAT_ID, and it posts one message and keeps editing it as the bar moves.
FAQ
How do I get my Telegram chat_id?
Send any message to your bot, then call getUpdates and read message.chat.id from the response. In a group, send a command addressed to the bot, like /start@your_bot, so a bot in privacy mode receives it.
Why does getUpdates return an empty result?
Nobody has messaged the bot in the last 24 hours, or the bot sits in a group in privacy mode and you have not sent it a command. getUpdates also stops working while an outgoing webhook is set.
Do I need python-telegram-bot to send a message?
No. One HTTPS POST to sendMessage with requests is enough for one-way alerts. python-telegram-bot 22.8 needs Python 3.10 or newer and asyncio, so it pays off when your script also listens for commands.
How long can a Telegram bot message be?
sendMessage accepts 1 to 4,096 characters after entities parsing. Split or trim longer logs and tracebacks before sending them.
How many messages can a Telegram bot send per second?
About one per second in a single chat, 20 per minute in a group and about 30 per second for bulk broadcasts. Over the limit, Telegram returns a 429 error with parameters.retry_after in seconds.
Check on long runs from your phone
Change one import and every tqdm bar prints a link you can open on any device. Get a Discord or Slack message when the run finishes, fails or stalls.
pip install tqdm-liveSources
- Telegram Bot API: sendMessage, getUpdates and formatting options core.telegram.org
- Telegram Bots FAQ: broadcasting limits core.telegram.org
- Telegram bot features: BotFather and privacy mode core.telegram.org
- PyPI: python-telegram-bot pypi.org
- tqdm docs: contrib.telegram tqdm.github.io