django-aiogram
Run aiogram next to Django: write handlers as ordinary Django app code, and send Telegram messages from anywhere in the project.
One container runs the bot. Every other process — web, Celery, a management
command — hands the call to a broker and returns, so a request never waits on
Telegram. Four transports can carry it, and BROKER says which:
┌─ Redis list ────┐
web, celery ──bot.send()──▶ ├─ Redis Streams ─┤ ──▶ start_tgbot ──▶ Telegram
├─ RabbitMQ ──────┤
└─ Kafka ─────────┘
| transport | BROKER |
extra | its own required settings |
|---|---|---|---|
| Redis list (default) | django_aiogram.broker.redis_list.RedisListBroker |
[redis] |
— |
| Redis Streams | django_aiogram.broker.redis_streams.RedisStreamsBroker |
[redis] |
REDIS_STREAM_KEY |
| RabbitMQ | django_aiogram.broker.rabbitmq.RabbitMQBroker |
[rabbitmq] |
RABBITMQ_URL, RABBITMQ_QUEUE |
| Kafka | django_aiogram.broker.kafka.KafkaBroker |
[kafka] |
KAFKA_BOOTSTRAP, KAFKA_TOPIC |
Your code does not change with the row: the same bot.send(), handlers, event log and
manage.py start_tgbot. What differs is what becomes of a message whose worker was
killed mid-send, and what recovery is — a command, a clock, or the broker's own doing.
Delivery compares them;
each transport has a page of its own below.
Install
pip install 'django-aiogram[redis]' # Redis list, the default, or Redis Streams
pip install 'django-aiogram[rabbitmq,redis]' # RabbitMQ
pip install 'django-aiogram[kafka,redis]' # Kafka
One extra per transport, so a deployment downloads only the queue driver it uses.
redis is in the other two lines for the FSM store, not the queue: FSM_STORAGE
defaults to aiogram's Redis store, so a bot keeping chat state needs that driver whichever
transport carries its messages, and FSM_STORAGE: 'memory' is what drops it.
Nothing is inferred from what happens to be installed: BROKER names the transport, and a
base pip install django-aiogram imports and runs manage.py but carries no message.
manage.py check names every extra that is missing, with the pip install line;
Installation has the
exceptions.
# settings.py
import os
INSTALLED_APPS = [..., 'django_aiogram']
TELEGRAM_BOT_DEFAULTS = {
'TOKEN': os.environ.get('TELEGRAM_BOT_TOKEN', ''),
# unset, BROKER resolves to RedisListBroker; the table above has the other three,
# and each transport reads its own settings on top of these two
'REDIS_URL': os.environ.get('REDIS_URL', ''),
}
Both may be empty: nothing connects or validates credentials at import time, so tests and migrations run without them. Requires Python 3.10–3.14, Django 5.2+ and aiogram 3.30+; each transport then pins its own driver and asks for its own server — Installation has both, per row.
Use it
# myapp/tg_router.py — imported automatically from every installed app
from aiogram import F, types
from django_aiogram import bot
@bot.message(F.text == '/start')
async def start(message: types.Message) -> None:
await message.answer('hi')
# anywhere else in the project — `bots['support']` is the second bot, if there is one
from django_aiogram import bot, bots
bot.send(chat_id=CHAT_ID, text='Order approved')
python manage.py start_tgbot
More than one bot is a section each under TELEGRAM_BOTS, inheriting the defaults above and
overriding what it names. One dispatcher and one handler tree serve all of them, bots configured
alike share the connection and the consumer thread, and a queued message names the bot it is for
wherever that bot has an identity —
Multiple bots. Bots that
arrive while it runs come from a provider, reconciled without a redeploy —
Dynamic bots.
A router module, a call, and one process running the bot. That process gets Django's
between-requests connection handling without having any requests — every update is bracketed
with close_old_connections(), so a database that restarts under a long-running bot does not
leave every handler raising InterfaceError until somebody notices. Nothing to configure;
Deployment says what the
healthcheck can and cannot see about it.
Everything else — rate limits, per-process opt-out, healthchecks — is configuration, documented rather than required. Webhook mode is the one alternative that also asks for a URL route: Webhook has the four steps.
Documentation
The documentation site is the
documentation. Pages live in docs/wiki/, so they are reviewed in
the same pull request as the code they describe and published from master.
| Installation | install, configure, run |
| Settings | every setting, with defaults and check ids |
| Handlers | routers, filters, FSM, the async ORM |
| Sending messages | routes, keyboards, files, errors |
| Testing | your suite without a broker, asserting what was queued |
| Multiple bots | aliases, identities, what a set of bots shares and what it costs |
| Dynamic bots | bots that arrive while it runs: providers, reconciliation, quarantine, leases |
| API | the instance, its internals, and what stays public |
| Delivery | how queued messages reach Telegram |
| Redis list | the default transport: what it guarantees, and why the worker's name matters |
| Redis Streams | the same server, a consumer group, and no worker identity to keep |
| RabbitMQ | a broker that tracks its own consumers, and one thread per connection |
| Kafka | offsets settle a prefix, ordering is per partition, a refusal rewinds |
| Webhook | receiving updates over HTTP instead of polling |
| Scaling | where polling stops, why a webhook costs nothing per bot, and what grows with the bots |
| Rate limits | staying inside Telegram's published limits |
| Tokens | where a bot's credential is kept, and rotating the key that wraps it |
| Admin | configuring bots, profiles and queues, and the permission the token is behind |
| Deployment | compose recipes, healthchecks, per-process opt-out |
| Logging | the logger and its structured fields |
| Event log | recording what the bot did to a table, and a signal to count it without one |
| Serialization | what can be queued |
| Troubleshooting | symptoms and their usual causes |
| Upgrading | what each major release changed, and what you must do |
| AI assistants | the brief to hand a coding agent |
Upgrading from 3.x: the distribution is django-aiogram and the import path is
django_aiogram, the driver is an extra you now name, and the event log has a table of its
own. The upgrading page walks it in order, migrate included.
Contributing
CONTRIBUTING.md for the workflow, AGENTS.md for the same ground in the form coding agents read. Changes are in CHANGELOG.md; security reports go through SECURITY.md.
Metadata
Release files for django-aiogram 5.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_aiogram-5.1.0.tar.gz | 1.4 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_aiogram-5.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.9 MB
Release files / django_aiogram-5.1.0.tar.gz
| Download URL | django_aiogram-5.1.0.tar.gz |
|---|---|
| Size | 1.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e96294529ed59ee4a528ae90b6cb63096ad9515b27a7ee30fbc18bb79c301d5e
|
|
BLAKE2b-256 checksum How to use checksums |
825a79a5f29163c334c39e50f3dcff82568e8c35abfd6725e95a6b8082d26a17
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency logRelease files / django_aiogram-5.1.0-py3-none-any.whl
| Download URL | django_aiogram-5.1.0-py3-none-any.whl |
|---|---|
| Size | 520.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6af06dca12f6bad81dc12f1001facb2b660c200c7d1e319f6297eb6a7a86ce8d
|
|
BLAKE2b-256 checksum How to use checksums |
7a5469fc092d7fac81a130e8a9983bdbe4d1ede0cb47c11fc1d33fbb23df0e3e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency log