At a glance
- Runpeek:
pip install tqdm-live, swap the import and callstart_run(), and one link shows every bar live;notify()sends finished, failed and stalled alerts to a Discord or Slack webhook, no bot needed. - tqdm.contrib: install
"tqdm[telegram]","tqdm[discord]"or"tqdm[slack]", then importtqdmfrom the matching module. - Telegram needs a bot token and
chat_id; Discord a bot token andchannel_id(not a webhook URL); Slack a bot token withchat:writeand a channel. - Each tqdm.contrib bar edits one message, so nothing new arrives when the job finishes, crashes or hangs.
- In our test with tqdm 4.70.1, a wrong Telegram or Discord token stopped the loop with an
UnboundLocalError.
Runpeek: a live link plus Discord or Slack alerts
Runpeek's tqdm-live package keeps tqdm's terminal bar and adds a live link instead of a chat message. Import tqdm from tqdm_live, call tqdm_live.start_run("name"), and every bar in the script shows on one link with the run's status, duration and host. Nobody needs an account to watch. Start the script with tqdml run -- python train.py instead and the link also streams its printed output, with no code changes.
Alerts go to a webhook URL, so there's no bot to create: tqdm_live.notify(url) sends a message when the run finishes or fails, and stall_after_minutes=15 adds one when no update arrives for 15 minutes. Discord and Slack webhook URLs get a formatted chat message, and any other webhook URL gets a JSON POST.
Call notify() after start_run(), because alerts belong to a run and notify() returns False without one. You can add up to 5 webhooks per run, or list them in TQDMLIVE_NOTIFY so every run picks them up, tqdml run included.
How the chat integrations work
When you import tqdm or trange from tqdm.contrib.telegram, tqdm.contrib.discord or tqdm.contrib.slack, you get a drop-in class that still draws the normal progress bar in your terminal. When the bar is created, it also posts a single message to your chat app, and then it edits that same message in place as the bar moves forward.
To keep network calls from slowing down your loop, a background worker handles the edits. That worker holds at most one pending edit in its queue and drops any older ones that haven't been sent yet, so a fast loop won't pile up hundreds of outdated requests.
All three modules live in tqdm.contrib, which the tqdm README lists as experimental modules.
Telegram
Telegram is usually the fastest of the three to set up. You need the telegram extra, which pulls in requests and envwrap:
- Run
pip install "tqdm[telegram]"in your environment. - Message
@BotFatherin Telegram to create a new bot and copy its token. - Add the bot to a chat and send it a message such as
/start, then openhttps://api.telegram.org/bot<token>/getUpdatesin a browser (with your bot token in place of<token>) and copy thechat_idfrom the response. - Import
tqdmortrangefromtqdm.contrib.telegramand pass bothtokenandchat_id, or setTQDM_TELEGRAM_TOKENandTQDM_TELEGRAM_CHAT_IDso you don't have to pass them in code.
# export TQDM_TELEGRAM_TOKEN="123456:ABC-your-bot-token"
# export TQDM_TELEGRAM_CHAT_ID="987654321"
from time import sleep
from tqdm.contrib.telegram import tqdm
for item in tqdm(range(200), desc="training"):
sleep(0.5)Discord
tqdm.contrib.discord talks to Discord's REST API v10 using requests, which means it requires a bot token and will not work with a Discord webhook URL.
- Install the extra with
pip install "tqdm[discord]". - Create a Discord bot that isn't public and only has permission to send messages, and invite it to the channel where you want the bar to show up.
- Pass
tokenandchannel_idtotqdm(), or exportTQDM_DISCORD_TOKENandTQDM_DISCORD_CHANNEL_IDin your shell before running your script.
from time import sleep
from tqdm.contrib.discord import tqdm
for item in tqdm(range(200), token="your-bot-token", channel_id="123456789012345678"):
sleep(0.5)Slack
The Slack integration relies on slack_sdk under the hood and requires a Slack app rather than an incoming webhook.
- Run
pip install "tqdm[slack]"to get tqdm withslack_sdkandenvwrap. - Create a Slack app, grant it the
chat:writescope, install it to your workspace, and invite the app into the channel you want to post in. - Call
tqdm(iterable, token="...", channel="...")fromtqdm.contrib.slack, or set theTQDM_SLACK_TOKENandTQDM_SLACK_CHANNELenvironment variables.
Because Slack is strict about how often you edit a message, the tqdm Slack class forces mininterval to at least 1.5 seconds, even if you pass a smaller value.
from time import sleep
from tqdm.contrib.slack import tqdm
for item in tqdm(range(200), token="xoxb-your-bot-token", channel="C0123456789"):
sleep(0.5)Credentials and environment variables
Runpeek's link needs no credentials, and its alerts only need a webhook URL. Bot tokens are secrets, so keep them in environment variables instead of hardcoding them in your scripts. If the matching environment variables are set, you can call tqdm(iterable) without passing credentials in Python at all.
| Option | Install | You need | Environment variables |
|---|---|---|---|
Runpeek (tqdm-live) | pip install tqdm-live | Nothing for the link; a webhook URL for alerts | TQDMLIVE_NOTIFY |
tqdm.contrib.telegram | pip install "tqdm[telegram]" | Bot token and chat_id | TQDM_TELEGRAM_TOKEN, TQDM_TELEGRAM_CHAT_ID |
tqdm.contrib.discord | pip install "tqdm[discord]" | Bot token and channel_id | TQDM_DISCORD_TOKEN, TQDM_DISCORD_CHANNEL_ID |
tqdm.contrib.slack | pip install "tqdm[slack]" | Bot token with chat:write and a channel | TQDM_SLACK_TOKEN, TQDM_SLACK_CHANNEL |
Limits of the tqdm.contrib bots
Because these modules only edit a single status message, they don't send a separate "finished" or "failed" message. When your script ends normally or crashes partway through, the chat message just stops changing, and there is no alert when a script hangs either. With leave=False, the Discord class deletes the message when the bar closes. The Telegram class is meant to as well, but a bug in tqdm 4.70.1 (its delete URL is never filled in) leaves the message in place.
You can also run into API rate limits if you create bars too quickly. If creating the initial chat message hits an HTTP 429 response on Telegram or Discord, tqdm prints a warning:
Creation rate limit: try increasing mininterval.
Test your credentials on a short loop before a long run. In our test with tqdm 4.70.1, a wrong Telegram or Discord token printed a 401 Client Error: Unauthorized line and then stopped the loop with UnboundLocalError: cannot access local variable 'req'; tqdm issue #1812 reports the same error for connection failures. A wrong Slack token printed invalid_auth and the loop carried on.
FAQ
Can I use a Discord webhook URL with tqdm.contrib.discord?
No, tqdm.contrib.discord requires a bot token and a channel_id, not a webhook URL. It uses requests to call Discord's REST API v10 directly, so you have to create a non-public bot with send message permissions and invite it to your channel. If a webhook URL is all you have, Runpeek's tqdm_live.notify() takes it as is.
Does tqdm tell me when the script finishes or crashes?
Runpeek does: call tqdm_live.start_run() and tqdm_live.notify() with a Discord or Slack webhook URL, and you get a message when the script finishes, fails or stalls. tqdm's chat integrations don't. They send no separate message for completion or failure and no alert if the script hangs; the existing chat message simply stops updating.
How do I find my Telegram chat_id?
After you create your bot with @BotFather and copy its token, add the bot to a chat and send it a message. Then open https://api.telegram.org/bot<token>/getUpdates in your browser, with your bot's token in place of <token>, and look for the chat_id in the response.
Why do I get a rate limit warning?
Telegram and Discord return an HTTP 429 error when you hit their message creation rate limits, which makes tqdm warn Creation rate limit: try increasing mininterval. Raise mininterval on your bars to send fewer requests; the Slack class already forces at least 1.5 seconds.
Why did my loop crash with UnboundLocalError?
In tqdm 4.70.1, the Telegram and Discord classes can hide the real error behind UnboundLocalError: cannot access local variable 'req': we got it with a wrong token, and tqdm issue #1812 reports it for connection failures. Check the token, the chat_id or channel_id, and that the bot was added to that chat, then run a short test loop.
Can Runpeek send alerts to Telegram?
Not as a formatted message. Runpeek formats alerts for Discord and Slack webhook URLs, and any other webhook URL gets a JSON POST with the event, the run and its link. For Telegram, you'd forward that JSON to the Bot API with a small relay of your own, or use tqdm.contrib.telegram for the bar itself.
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
- tqdm README: contrib modules github.com
- tqdm.contrib.telegram source github.com
- tqdm.contrib.discord source github.com
- tqdm.contrib.slack source github.com
- tqdm issue #1812: UnboundLocalError hides the original error github.com
- Telegram Bot API: getUpdates core.telegram.org
- Slack chat:write scope docs.slack.dev
- Discord message resource docs.discord.com