To host a python-telegram-bot bot 24/7, run it on a machine that stays on instead of your PC: a Linux VPS with a systemd service that starts it again after a crash or a reboot, or a PlayNHost bot plan (from US$1.99 a month, see Telegram bot hosting), where you upload your code, paste the token and press Start. In our test a python-telegram-bot 22.8 bot used about 44 MB of memory at rest; our smallest bot plan, STARTER, has 0.5 GB. Both routes use long polling: run_polling() asks Telegram for new messages, so the bot needs no public address. The PlayNHost steps:
- In Telegram, send
/newbotto @BotFather and copy the token it gives you. - On Telegram bot hosting, click Create Server on a plan. In the order window, answer What is your bot for? with Telegram and choose Python.
- Open your server in the panel. The Set up your bot card on Overview opens at Connect to Telegram; its first step, What runs, should say This bot runs Python Bot.
- At Connect to Telegram, paste the token and click Connect. Check the bot name shown and click Yes, continue.
- At Your code, click Upload files to open Files and upload
bot.pyandrequirements.txt, or click Connect Git. No code yet? Create a starter bot writes a python-telegram-bot 22 bot for you. - On the Bot page, pane Code, check that Entry file is the file that calls
run_polling()(bot.pyby default). - Click Start bot, open Console and wait for
Logged in as @your_bot. The chip on Overview turns to Online in Telegram. Send your bot/start.
The first start takes longer while pip installs python-telegram-bot. After that it installs again only when your requirements file, the extra packages or the Python version change, or once a day. Step by step with screenshots: How to host a Telegram bot.
What we measured
We ran the three Python Telegram libraries on one of our own servers, in throwaway containers set up the way the panel runs a Python bot: Python 3.12 (the Runtime marked recommended), packages installed with the same pip command, a 1 GB memory limit and one CPU core.
| Library | Version | Memory at rest | Start to Telegram's answer | Installed size, with dependencies |
|---|---|---|---|---|
| python-telegram-bot | 22.8 | 44 MB | 0.9 s | 12.3 MB |
| aiogram | 3.31.0 | 173 MB | 4.3 s | 33.9 MB |
| pyTelegramBotAPI | 4.37.1 | 40 MB | 0.7 s | 21.9 MB |
| Python 3.12 alone | 9 MB |
Each bot loaded the library and asked Telegram who it is with a fake token (python-telegram-bot: Application.initialize()). Memory is what the bot's processes held 15 seconds after Telegram refused the token; start time runs from launching Python to the library reporting that answer, so it includes one round trip to Telegram. Both are the median of three runs after a warm-up run.
These figures are the library's floor: no chats, no handlers at work, nothing cached. Your own code, the data it keeps in memory and the number of chats it serves come on top, so size the plan by what your bot does. Our older page python-telegram-bot vs aiogram on PlayNHost measured a different setup (Python 3.13, a bot built and ready to start), so its numbers are not comparable with these.
A minimal python-telegram-bot bot
It answers /start, reads the token the panel gives it and prints a ready line. Put the library in requirements.txt:
python-telegram-bot>=22,<23
And the code in bot.py:
import os
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.effective_message.reply_text("Hello! I am running on PlayNHost.")
async def post_init(application: Application) -> None:
print(f"Logged in as @{application.bot.username}")
def main() -> None:
application = (
Application.builder()
.token(os.environ["TELEGRAM_BOT_TOKEN"])
.post_init(post_init)
.build()
)
application.add_handler(CommandHandler("start", start))
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
main()
post_init runs after the application has initialized, which is when python-telegram-bot checks the token with Telegram, so Logged in as @your_bot appears only once Telegram has accepted it. The panel reads that line to show Online in Telegram. The panel gives the token to your code as TELEGRAM_BOT_TOKEN, and also as BOT_TOKEN, TOKEN and TELEGRAM_TOKEN, so os.environ["TOKEN"] works too if you start from the library's examples, which use a "TOKEN" placeholder.
If the token is wrong
In our test, with a token Telegram refuses, our test bot printed:
InvalidToken: The token `1000000000:AAFakeTokenForAMemoryTestNotReal00000` was rejected by the server.
python-telegram-bot checks the token before polling starts, so the bot above stops at once with exit code 1. The console first shows Network Retry Loop (Bootstrap Initialize Application): Invalid token. Aborting retry loop. with a traceback, then the traceback of the error itself, whose last line reads telegram.error.InvalidToken: The token `...` was rejected by the server. On PlayNHost the panel shows Telegram did not accept your bot token above the console, with Replace the token. Note that both tracebacks contain the token itself: do not paste that console output anywhere public.
To fix it, copy the token again: in @BotFather send /mybots, choose your bot, then API Token. Paste it on the Bot page, pane Telegram, under Token, and start the bot again. If the token is revoked while the bot is already polling, python-telegram-bot logs Network Retry Loop (Polling Updates): Invalid token. Aborting retry loop. and stops polling, but the process keeps running, so the bot looks running while nothing answers. The panel shows the same banner.
What a host should know about python-telegram-bot
- Package and import names: you install
python-telegram-botand importtelegramandtelegram.ext. - Python version: python-telegram-bot 22.8 needs Python 3.10 or newer. The Runtime on the Code pane offers 3.10 to 3.14, with 3.12 marked recommended, so every choice meets that.
- A webhook left from another host: before it starts polling,
run_polling()deletes any webhook set on the bot, so a bot moved from a webhook host works without extra code. Messages waiting at Telegram are kept unless you passdrop_pending_updates=True. - Logging: python-telegram-bot logs its start at
INFOlevel, which Python does not show until you set up logging, so the bot above prints its own ready line. Withlogging.basicConfig(level=logging.INFO)it logsINFO:telegram.ext.Application:Application started, which the panel also reads as ready. If you do that, also addlogging.getLogger("httpx").setLevel(logging.WARNING), as the library's own examples do: atINFOthe HTTP library logs every request URL, and Telegram's request URLs contain your token. - Scheduled jobs: the
JobQueueneeds an extra. Writepython-telegram-bot[job-queue]>=22,<23inrequirements.txt; without itapplication.job_queueisNoneand the library warnsNo `JobQueue` set up. - A second copy: when another copy polls with the same token, Telegram answers
409 Conflict. python-telegram-bot logsException happened while polling for updates.and keeps retrying, so the bot looks running but misses messages. The panel shows Your bot is running somewhere else too. See Fix Telegram bot 409 Conflict.
Host it yourself on a Linux VPS
On Debian or Ubuntu, install Python, create a user for the bot and install the library in a virtual environment. Check that python3 --version says 3.10 or newer.
sudo apt update
sudo apt install -y python3 python3-venv
sudo useradd --system --create-home --home-dir /opt/tgbot --shell /usr/sbin/nologin tgbot
sudo cp bot.py requirements.txt /opt/tgbot/
sudo -u tgbot python3 -m venv /opt/tgbot/venv
sudo -u tgbot /opt/tgbot/venv/bin/pip install -r /opt/tgbot/requirements.txt
Put the token in a file only root can read. Open it with sudo nano /etc/tgbot.env, write one line, save, then run sudo chmod 600 /etc/tgbot.env:
TELEGRAM_BOT_TOKEN=123456789:your-token-from-botfather
Create /etc/systemd/system/tgbot.service:
[Unit]
Description=Telegram bot (python-telegram-bot)
Wants=network-online.target
After=network-online.target
[Service]
User=tgbot
WorkingDirectory=/opt/tgbot
EnvironmentFile=/etc/tgbot.env
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/tgbot/venv/bin/python /opt/tgbot/bot.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Start it now and at every boot, then follow its output:
sudo systemctl daemon-reload
sudo systemctl enable --now tgbot
sudo journalctl -u tgbot -f
PYTHONUNBUFFERED=1 matters here: when Python's output does not go to a terminal it is block-buffered, so without it Logged in as can sit in a buffer instead of reaching the journal. systemctl stop tgbot sends SIGTERM, which run_polling() handles by shutting down cleanly. After you change the code, run sudo systemctl restart tgbot. Stop any copy on your PC first, or Telegram answers 409 Conflict.
Self-hosting or a bot plan
- On a VPS you manage: system updates, the Python version, the virtual environment, reinstalling packages, the unit file, reading the journal and noticing when the bot stops answering. In return you have root, and you can run a webhook if you add a domain and HTTPS.
- On a bot plan the panel does: it installs
requirements.txtwhen it changes, starts the bot again after a crash, checks the token with Telegram before saving it, gives it to your code under five names, reads the console and names the cause when the bot stops answering (a refused token, a second copy, a webhook), with a button for the token and the webhook. Our bot plans do not include a public HTTPS address yet, so bots there use long polling, which is whatrun_polling()does.
See also How to use a MySQL database with a python-telegram-bot bot (aiomysql).
Questions people ask
Do I need a webhook to keep a python-telegram-bot bot online?
No. run_polling() keeps a connection open to Telegram and asks for new messages, so the bot answers as long as the process runs. A webhook needs a public HTTPS address, which our bot plans do not include yet. Moving a bot that used one: How to move a Telegram bot to PlayNHost.
Why did my token appear in the console?
It shows up in two common places: the InvalidToken message when Telegram refuses the token, and the HTTP library's request lines when logging is at INFO. Set the httpx logger to WARNING as shown above. Our starter bot also catches InvalidToken and prints a plain line instead.
Can I use run_repeating and other scheduled jobs?
Yes, with the job-queue extra in requirements.txt. The extra installs APScheduler at the next start, because the requirements file changed. Jobs run inside the bot process, so they keep running while the bot runs.
Does the starter bot use python-telegram-bot?
Yes. On a Python server, Create a starter bot writes bot.py and a requirements.txt with python-telegram-bot 22. It answers /start, prints Logged in as @your_bot and keeps the token out of its log lines. Change it as you like, then restart.
python-telegram-bot runs on every PlayNHost bot plan, from STARTER (0.5 GB) up. Compare the plans and click Create Server on Telegram bot hosting.



