Telegram bots

Send Telegram messages from a Python script

A Telegram bot can message you from any script. Get a token from @BotFather, find your chat_id with getUpdates, then POST to sendMessage. Plain requests is enough for alerts; python-telegram-bot 22.8 is async-only. A message holds 1 to 4,096 characters, and Telegram asks for no more than about 1 message per second per chat.

3 min readNotifications and alerts

At a glance

  • /newbot in @BotFather returns the bot token; keep it secret.
  • Message the bot first, then read message.chat.id from getUpdates.
  • sendMessage takes 1 to 4,096 characters; parse_mode HTML 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.

  1. Open a chat with @BotFather in Telegram.
  2. Send /newbot.
  3. Pick a display name, then a username that ends in "bot", like tetris_bot.
  4. 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:

  • getUpdates does 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.
Python
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.

Python
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.

Python
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.

LimitValue
Message text length1 to 4,096 characters after entities parsing
Messages to one chatAbout 1 per second; short bursts may pass before 429 errors
Messages to a groupNo more than 20 per minute
Bulk broadcastAbout 30 messages per second
Over the limitHTTP 429 with parameters.retry_after, the seconds to wait
Undelivered updatesKept 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:

Python
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()

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.

Get a live linkpip install tqdm-live

Sources