Install & Compatibility
Where this runs
tested against v22.8 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.910 runs
installs and imports cleanly · install 0.0s · import 1.076s · 51.6MB
glibcpy 3.10–3.910 runs
installs and imports cleanly · install 3.3s · import 0.952s · 52MB
51MB installed
● package 51MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
Application
✓ from telegram.ext import Application
CommandHandler
✓ from telegram.ext import CommandHandler
MessageHandler
✓ from telegram.ext import MessageHandler
filters
✓ from telegram.ext import filters
ContextTypes
✓ from telegram.ext import ContextTypes
✗ from telegram.ext import CallbackContext
While CallbackContext still exists, ContextTypes is the modern and type-hinted way to refer to the context object in handler callbacks, especially since v20.0.
Update
✓ from telegram import Update
Updater
✓ N/A
✗ from telegram.ext import Updater
The Updater class was removed in version 20.0. Use Application.builder() and Application.run_polling() or run_webhook() instead.
This quickstart code sets up a simple echo bot that replies to `/start` and `/help` commands, and echoes back any other text message it receives. It demonstrates the use of `Application`, `CommandHandler`, and `MessageHandler` with filters. Your bot token should be provided via the `TELEGRAM_BOT_TOKEN` environment variable.
import os
import logging
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes
# Enable logging
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# set higher logging level for httpx to avoid all GET and POST requests being logged
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Sends a message when the command /start is issued."""
user = update.effective_user
await update.message.reply_html(
f"Hi {user.mention_html()}!\nI'm an echo bot. Send me anything!",
)
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Sends a message when the command /help is issued."""
await update.message.reply_text("Help! Send me a message and I will echo it back!")
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Echo the user message."""
await update.message.reply_text(update.message.text)
async def main() -> None:
"""Start the bot."""
# Replace with your bot's token from BotFather. Get from environment variable.
BOT_TOKEN = os.environ.get('TELEGRAM_BOT_TOKEN')
if not BOT_TOKEN:
raise ValueError("TELEGRAM_BOT_TOKEN environment variable not set.")
# Create the Application and pass it your bot's token.
application = Application.builder().token(BOT_TOKEN).build()
# On different commands - add handlers
application.add_handler(CommandHandler("start", start_command))
application.add_handler(CommandHandler("help", help_command))
# On non command messages - echo the message on Telegram
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
# Run the bot until the user presses Ctrl-C
logger.info("Starting bot polling...")
await application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Debug
Known issues
breakingVersion 20.0 introduced significant breaking changes. The `Updater` class was removed and replaced by `Application`. The architecture shifted to `asyncio`, requiring async/await for handler functions. The `File.download` method was split into `File.download_to_drive` and `File.download_to_memory`. Most third-party dependencies became optional, requiring explicit installation for certain features.fixRewrite bot initialization to use `Application.builder().build()`, update handler functions to be `async def`, and adjust file download calls. Install optional dependencies if needed, e.g., `pip install "python-telegram-bot[job-queue]"`.
affects: >=20.0.0
gotchaSince v20.0, `python-telegram-bot` is built on `asyncio` and is generally not thread-safe. Avoid using `telegram.ext.Application/Updater.update_queue`, `telegram.ext.ConversationHandler.check/handle_update`, `telegram.ext.CallbackDataCache`, and runtime modifications to `telegram.ext.filters` classes in multi-threaded environments to prevent race conditions.fixDesign your bot with asyncio concurrency in mind. If you must use threads for specific tasks, ensure proper synchronization for PTB components or isolate PTB interactions to the main async loop.
affects: >=20.0.0
deprecatedTimeout arguments (`read_timeout`, `write_timeout`, `connect_timeout`, `pool_timeout`) for `Application.run_polling()` were removed in v22.0.fixConfigure timeouts using `ApplicationBuilder` methods (e.g., `ApplicationBuilder().read_timeout(seconds)`) or by specifying them via `telegram.Bot.get_updates_request`.
affects: >=22.0.0
deprecatedAttributes representing durations/time periods (e.g., `ChatFullInfo.slow_mode_delay`) are migrating from `int` to `datetime.timedelta`. While `int` is currently supported, it's deprecated and will be removed in a future major version.fixSet the environment variable `PTB_TIMEDELTA=true` or `PTB_TIMEDELTA=1` to opt into `datetime.timedelta` objects now. Update your code to handle `timedelta` objects for these attributes.
affects: >=22.6.0
gotchaFor new Telegram Bot API update types (e.g., `MESSAGE_REACTION_COUNT`, `BUSINESS_CONNECTION`), you must explicitly list them in `Application.run_polling(allowed_updates=...)` or `Bot.set_webhook(allowed_updates=...)` to receive them. Using `Update.ALL_TYPES` is generally safer for comprehensive update reception.fixAlways include relevant new update types in the `allowed_updates` parameter, or use `allowed_updates=Update.ALL_TYPES` if you want to receive all available updates by default.
affects: >=13.4 (for older types), >=21.1 (for newer types)
breakingVersion 22.5 addressed a breaking change accidentally introduced in v22.4 regarding the `ReplyParameters` class, where adding a new parameter broke positional arguments.fixUpgrade to v22.5.0 or later. For future compatibility, always use keyword arguments when calling methods, especially those with many parameters or new parameters in recent versions.
affects: 22.4.0
Errors
Common errors & fixes
ImportError: cannot import name 'Updater' from 'telegram.ext'
The `Updater` class was removed in `python-telegram-bot` version 20.0 and later, replaced by the `Application` class for managing the bot's lifecycle.
fixUpdate your code to use `Application.builder().token('YOUR_TOKEN').build()` and `application.run_polling()` or `application.run_webhook()` instead of `Updater`. AttributeError: 'CallbackContext' object has no attribute 'dispatcher'
In `python-telegram-bot` version 20.0 and later, the `Dispatcher` object was removed, and the `bot` instance is now directly accessible via `context.bot` within handlers.
fixAccess the bot instance directly using `context.bot` (e.g., `await context.bot.send_message(...)`) instead of `context.dispatcher.bot` or `update.dispatcher.bot`.
NameError: name 'MessageFilter' is not defined
The `MessageFilter` class was replaced by the `filters` module in `python-telegram-bot` version 20.0 and later, offering a more granular way to filter updates.
fixImport `filters` from `telegram.ext` and use its attributes (e.g., `filters.TEXT`, `filters.COMMAND`) directly within `MessageHandler` or other handlers. For example, `MessageHandler(filters.TEXT & ~filters.COMMAND, my_handler)`.
TypeError: object NoneType can't be used in 'await' expression
This error typically indicates that an `await` keyword was used on a function call that either returned `None` or did not return a valid awaitable (coroutine) object, often because the function itself was not defined as `async` or was called incorrectly.
fixEnsure that any function called with `await` is an `async def` function and that it actually returns an awaitable object. Check for typos or missing `await`s on functions like `context.bot.send_message`.
Upgrade
Version history
22.8latest on PyPI · released Jun 12, 2026
Audit
Dependencies
httpxrequiredRequired for telegram.request.HTTPXRequest, the default networking backend.
aiolimiteroptionalOptional, for rate limiting functionality (`[rate-limiter]`).
tornadooptionalOptional, for webhook functionality (`[webhooks]`).
cachetoolsoptionalOptional, for callback data caching (`[callback-data]`).
APScheduleroptionalOptional, for job queue functionality (`[job-queue]`).
httpx[socks]optionalOptional, for Socks5 proxy support (`[socks]`).
httpx[http2]optionalOptional, for HTTP/2 support (`[http2]`).
cryptographyoptionalOptional, for Telegram Passport related functionality (`[passport]`).