Skip to main content
SwiftBot - Ultra-Fast Telegram Bot Framework

PyPI Python License

SwiftBot

SwiftBot is a fast, async-first Telegram bot framework built for simplicity and correctness. One decorator registers a handler, a rich Context object does the talking, and the framework quietly handles everything else — HTTP/2 connection pooling, Retry-After compliance, persistent state storage, and a fully typed error hierarchy. Everything is typed, everything is optional, and you never need an external database to get started.

Telegram Bot API compatibility: SwiftBot is compatible with Telegram Bot API 10.2.

pip install swiftbot

The built-in webhook server is optional. Polling does not require aiohttp; install the webhook extra only when you need webhook mode:

pip install "swiftbot[webhook]"

Why SwiftBot

SwiftBot is a strong fit for asynchronous, route-heavy bots where local dispatch speed, predictable resource use, typed APIs, and operational controls matter. The checked-in benchmark snapshot measured SwiftBot at 19,803 updates per second and 50.5 microseconds per update through its public raw-update test path for a controlled ten-route offline workload on Python 3.12.3. Every framework routed all expected handler calls correctly; these measurements exclude Telegram network latency, databases, business logic, and application-specific middleware.

Advantage What it means in practice Evidence in this repository
Fast local dispatch More handler work can be processed per process before application logic or network I/O becomes the bottleneck. In the corrected public raw-update comparison, SwiftBot measured 1.91× pyTelegramBotAPI, 2.10× python-telegram-bot, and 16.24× aiogram for this workload. These are versioned public-path observations, not universal production claims.
Low measured framework footprint Smaller framework distributions and lower measured peak RSS can help constrained deployments, while dependency footprints still need to be evaluated for the full application. The corrected public-path snapshot measured 33.2 MiB peak RSS for SwiftBot; package and dependency footprints should be evaluated separately.
Useful async concurrency I/O-like handlers can scale with bounded worker concurrency rather than relying on unbounded task creation. The worker-pool benchmark reached 2,804 completed updates per second at eight workers versus 438 at one worker, with all 400 updates completed in every repeat.
Operational safeguards HTTP/2 pooling, retry-after handling, bounded backpressure, dead-letter handling, and typed Telegram errors reduce the amount of reliability plumbing bot authors must build themselves. These are implemented in the connection pool, worker pool, exception hierarchy, and webhook server.
Network-free testing Handlers can be exercised without Telegram credentials or network calls, with outgoing API requests captured for assertions. The built-in TestClient harness and the repository’s 212 passing tests provide the local testing path; 10 live-environment tests remain skipped without credentials.

The benchmark results are controlled local measurements, not a universal claim that SwiftBot is faster for every production bot. Framework choice should still account for ecosystem maturity, compatibility requirements, workload shape, network latency, and a project-specific load test. See the complete benchmark report and reproduction guide for methods, charts, raw results, and caveats.

Quick Start

import asyncio
from swiftbot import SwiftBot, Filters as F
from swiftbot.types import Message

bot = SwiftBot(token="YOUR_BOT_TOKEN")

@bot.on(Message(text="hello"))
async def hello(ctx):
    await ctx.reply("Hi there!")

@bot.on(Message(filters=F.command("start")))
async def start(ctx):
    await ctx.reply("Welcome! Send me hello.")

asyncio.run(bot.run())

That is the whole bot. No router setup, no dispatch table, no middleware boilerplate.

Core Concepts

Handlers and Filters

Handlers are registered with @bot.on(Message(...)) and the first matching handler wins. Filters are composable — combine commands, regex patterns, and predicates:

@bot.on(Message(filters=F.command("ban") & F.chat(-100123456789)))
async def ban(ctx):
    ...

@bot.on(Message(pattern=r"^/weather (\w+)"))
async def weather(ctx):
    city = ctx.match.group(1)
    await ctx.reply(f"Weather in {city}: sunny")

Context

Every handler receives a Context with typed access to the update, plus shortcuts that cover most use cases: ctx.reply(), ctx.edit(), ctx.delete(), ctx.answer_callback(), and ctx.forward_to().

Buttons and Callbacks

from swiftbot import InlineKeyboard, Button, CallbackData

# Declare the payload shape once
page = CallbackData("page", int)

# Attach packed payloads to buttons
kb = InlineKeyboard([]).add_row(
    Button.inline("1", page.pack(1)),
    Button.inline("2", page.pack(2)),
)
await ctx.reply("Pick a page:", reply_markup=kb.to_dict())

# Filter on valid payloads and decode the typed data
@bot.on(CallbackQuery(pattern=rf"^{page.prefix}:[^:]+:[^:]+$"))
async def on_page(ctx):
    page_num = page.unpack(ctx.callback_query.data)[0]
    await ctx.answer_callback(f"Page {page_num}")

State and Dialogues

State survives restarts with pluggable storage backends — in-memory, JSON file, or Redis — and the dialogue system models multi-step conversations as declared transition graphs with per-state timeouts:

survey = bot.dialogue("survey")

@survey.state("ask_name", next=["ask_age"])
async def ask_name(ctx, prev=None):
    await ctx.reply("What's your name?")
    return Dialogue.next("ask_age", carry=ctx.text)

@survey.state("ask_age", timeout=120.0)
async def ask_age(ctx, prev=None):
    await ctx.reply(f"Hi {prev}, how old are you?")
    return Dialogue.end

Middleware, Pipelines and Scopes

Middleware runs around every handler, and the Composer bundles middleware chains with error boundaries. Pipeline adds dependency injection so handlers stay pure and testable, while scope() attaches middleware to only the updates matching a predicate.

from swiftbot.middleware import Logger, RateLimiter
from swiftbot.pipeline import Pipeline

bot.use(Logger())
bot.use(RateLimiter(rate=20, per=60))

pipe = Pipeline().deps(db=my_db)
async def stats(ctx, db):
    await ctx.reply(f"{await db.count()} users")
pipe.handle(F.command("stats"), stats)
bot.pipeline(pipe)

bot.scope(F.private).use(RateLimiter(rate=1, per=1.0))

Safety Nets

Fallback handlers catch anything that no other handler matched, and outbound throttling keeps your bot under Telegram's limits automatically:

@bot.fallback
async def catch_all(ctx):
    await ctx.reply("Try /help.")

bot.api.config.use(throttle(max_per_second=20.0, per_chat=4.0))

Testing Without Telegram

The built-in harness runs your real handlers with zero network calls — every outgoing API request is captured and every response is scriptable:

from swiftbot.testing import TestClient

async with TestClient(bot) as client:
    await client.send_update({
        "update_id": 1,
        "message": {
            "message_id": 1, "date": 1,
            "chat": {"id": 1, "type": "private"},
            "from": {"id": 2, "is_bot": False, "first_name": "A"},
            "text": "hello",
        },
    })

assert client.outgoing[0]["method"] == "sendMessage"

More

Topic What you get
Commands Declarative command specs with auto-generated /help
Transformers Outbound API layer with auto-typing and recording
Deep Linking Typed ?start= payloads for referrals and auth
Webhooks aiohttp server with secret-token verification and metrics
Storage In-memory, JSON-file, and Redis FSM backends
Exceptions Typed ChatNotFound, TooManyRequests, Forbidden, ...
Benchmarks Reproducible dispatch, memory, worker-pool, backpressure, and read-only Telegram smoke tests with reports and graphs

The complete documentation lives in the docs/ folder. The complete benchmark report and reproduction guide are in tests/benchmark/README.md.

Contributing

Contributions are welcome — open an issue or submit a pull request.

License

MIT — Copyright (c) 2026 Arjun-M/SwiftBot

Metadata

Release files for swiftbot 1.6.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for swiftbot 1.6.4
File Size Uploaded
swiftbot-1.6.4.tar.gz 101.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for swiftbot 1.6.4
File Interpreter ABI Platform
swiftbot-1.6.4-py3-none-any.whl Python 3 none any Details

Total release size: 219.1 kB

Release files / swiftbot-1.6.4.tar.gz

Download URL swiftbot-1.6.4.tar.gz
Size 101.8 kB
Tags Source
SHA-256 checksum
How to use checksums
418ff1be637d6a9330209b344cf4e2e2c9f374583d0f5ec71f9c810c024834c9
BLAKE2b-256 checksum
How to use checksums
b1f3d722ec1f804d0b4fa92ffcfe8fd924af55ac2ea51c83a7b9e1ac99fcb854
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / swiftbot-1.6.4-py3-none-any.whl

Download URL swiftbot-1.6.4-py3-none-any.whl
Size 117.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
93589c3e77817b84a8f839a897d03fcf35d135a989d46feb655642061cc106b6
BLAKE2b-256 checksum
How to use checksums
d512f0da9a07670f164e13247b3b4a3d0cdecf95351c31ed3630a9c3832abb66
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

This release

1.6.4 This release

2 release files

1.6.3

2 release files

1.6.2

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.0.2

2 release files

1.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page