Library / SDK
taskiq-python/taskiq avatar
taskiq-python/taskiq

taskiq: an async task queue for Python that admits it is still alpha

Distributed task queue with full async support

2,354 stars151 forksPythonMIT

At a glance

What is it?
The broker-agnostic design, the dependency injection that makes web dependencies reusable in workers, and what 117 open issues say about a 0.13 release.
Who is it for?
taskiq is worth trying when you already live in asyncio and a synchronous worker model is the thing in your way. The core is small, the broker is a plugin, and dependency injection means a task can reuse the database session your FastAPI app already builds.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The pitch is one decorator and a broker you choose

Celery is the default answer to background jobs in Python, and it is synchronous. taskiq starts from the observation that a codebase built on asyncio does not want a worker pool that blocks on every job, so it is written around coroutines end to end and accepts ordinary synchronous functions too.

The README says the inspiration was Celery and Dramatiq. What it changes is the transport choice and the calling convention. The broker is not baked in; separate packages provide it, and the README lists NATS, Redis, RabbitMQ through taskiq-aio-pika, and Kafka through taskiq-aio-kafka. Declaring a task is a decorator on a function, and the whole example fits on a screen.

python
import asyncio

from taskiq_nats import JetStreamBroker

broker = JetStreamBroker("nats://localhost:4222", queue="my_queue2")


@broker.task
async def my_task(a: int, b: int) -> None:
    print("AB", a + b)


async def main():
    await broker.startup()

    await my_task.kiq(1, 2)

    await broker.shutdown()


if __name__ == "__main__":
    asyncio.run(main())

Three details are worth noticing. The broker lifecycle is explicit rather than implicit, enqueueing is a method on the task object rather than a global call, and the type annotations survive into the invocation. `kiq` is the kick interface, and `pip install taskiq[reload]` is listed as an extra, which tells you hot reload is not part of the base install.

Broker plugins live in separate repositories, which is the real design decision

Splitting every broker into its own distribution means the core package does not pull in a client library you do not use. The dependency list in `pyproject.toml` contains no broker client at all: aiohttp, anyio, pycron, pydantic and taskiq_dependencies. typing-extensions is conditional on Python below 3.11, and the floor is `>=3.10,<4`.

That last dependency is the interesting one. taskiq_dependencies is the piece that lets a task ask for something the web app already has, and it is why the framework integrations exist. The README describes FastAPI and AioHTTP integrations for reusing dependencies from your web app inside tasks, which names the specific problem: a job that needs a database session should not invent its own connection management, and should see the same session configuration the request path sees.

The trade is that a broker swap is a dependency change rather than a configuration change, and that four broker packages are maintained by people other than the core author. The repository topics include `celery-like`, which is an honest self description. The goal was Celery's reach with a different concurrency model, not a new task model.

Running workers, and the two flags worth knowing

A worker is pointed at the module that defines the broker variable, using a module and variable pair.

bash
taskiq worker path.to.the.module:broker

If tasks are spread across modules, the `--fs-discover` flag walks the current directory and imports every module named tasks.py it finds, so a large project does not need one enormous import list. The second flag is `--reload`, which restarts the worker when the code changes, and which the README ties to the reload extra rather than the base package.

The same addressing scheme appears in the entry points, which is a tidy sign of how small the command surface is.

toml
[project.scripts]
taskiq = "taskiq.__main__:main"

[project.entry-points.taskiq_cli]
worker = "taskiq.cli.worker.cmd:WorkerCMD"
scheduler = "taskiq.cli.scheduler.cmd:SchedulerCMD"

Two commands, worker and scheduler, registered as plugins rather than hardcoded branches. The scheduler exists because periodic jobs are part of the package, with pycron in the core dependencies. An OpenTelemetry instrumentor is registered the same way under `opentelemetry_instrumentor`, so distributed tracing arrives through a normal auto-instrumentation entry point instead of hand-written middleware.

What 0.13 changed, and what the labels say

The release notes are the best evidence of where the project stands. Version 0.13.0 on 2026-09-26 drops Pydantic v1 support, listed as a breaking change, and adds an optional `send_timeout` to the scheduler so a hung kick fails loudly instead of stalling silently. Version 0.12.6 adds per-task `types_of_exceptions` and stops using f-strings in logs. Version 0.12.5 adds per-task acknowledgement types and manual acknowledgement, and fixes a missing psutil dependency on the OpenTelemetry path.

That list describes a library finding its edges. Acknowledgement control, per-task exception types and a timeout on a send path are all things you add after somebody reports the absence. The scheduler fix is the clearest case: a kick that hangs is the kind of failure that looks like a slow worker rather than a defect.

Against that, the metadata in `pyproject.toml` still carries Development Status :: 3 - Alpha, alongside Typing :: Typed and classifiers for Python 3.10 through 3.14. The repository has 2,345 stars, 147 forks and 117 open issues, and the last push was 2026-09-26, the same day as the release. An alpha classifier on a package at 0.13 with that much usage reads as a deliberate signal rather than an oversight: the API is still moving, and 0.13 dropping a Pydantic major version is what that looks like in practice.

One smaller inconsistency is worth recording. The homepage field is empty in the GitHub metadata, while `pyproject.toml` sets both Documentation and Homepage to the documentation site, which is where the README sends you anyway.

Acknowledgement and scheduling are where the queue semantics get decided

Two features in the 0.12.x line tell you more about the project's priorities than the README does. The first is acknowledgement control, split into a per-task ack type and a manual acknowledgement mode. Acknowledgement is the contract between worker and broker about when a message may be discarded: an automatic broker with no ack assumes the worker crashed means redelivery, while manual ack lets a task finish its work and then confirm, which is the only safe arrangement when a job has an outside side effect.

Making that per-task rather than global is the interesting part, because a single queue often mixes a search index rebuild, which is idempotent and safe to retry, with a payment capture, which is not. A library that lets you set the policy at the task declaration is answering a question that most queues leave you to solve with separate queues.

The scheduler is the other half. Periodic jobs are part of the core rather than an add-on, built on pycron, and the 0.13.0 fix added an optional `send_timeout`. A scheduler thread that awaits a broker send with no timeout can block indefinitely on a broker that accepted a connection and then stopped consuming, and the symptom is a scheduler that appears alive while nothing is ever enqueued. Making the timeout optional rather than mandatory keeps the common case simple while giving an operator an escape hatch, and the per-task `types_of_exceptions` added in 0.12.6 serves the same goal from the error-handling side: which exception classes count as a retryable failure is a policy you set per task, not a global setting you have to live with.

Repository shape and the contributor surface

The tree is small and readable: a taskiq directory for the package, tests beside it, then docs, imgs, and the usual .github, .pre-commit-config.yaml, .python-version and uv.lock at the root. There is a package.json in the repository, which does not make this a JavaScript project. It is named taskiq-docs and its only job is the documentation site, built on VuePress with mermaid for diagrams and sass for styling, pinned to release candidates such as vuepress 2.0.0-rc.26.

The default branch is master, which matters for anyone scripting against the repository. Development uses uv, with ruff and basedpyright driven from pre-commit, and the README states that pytest runs with no extra options. The documentation site, separately, wants yarn.

Pavel Kirilin is listed as both author and maintainer with the same address. A single-maintainer library carrying 117 open issues is a real adoption risk to plan around, and it is the one thing here that no amount of clean design addresses.

Editorial conclusion

taskiq is worth trying when you already live in asyncio and a synchronous worker model is the thing in your way. The core is small, the broker is a plugin, and dependency injection means a task can reuse the database session your FastAPI app already builds. Two facts sit awkwardly together and you should weigh both before committing. The package classifier still says Development Status :: 3 - Alpha at version 0.13.0 with 2,345 stars, and 117 issues are open against 147 forks, which is a busy ratio for a library rather than an application. Read together they say the design is settled and the edges are still being found. The 0.13.0 release dropping Pydantic v1 support is a concrete reminder of that: a breaking change in a package that has not yet declared itself stable.

Frequently asked questions

How does Python's async system work?

An async function runs until it hits an await that cannot proceed, then yields control back to an event loop so another coroutine can run. Nothing blocks a thread while waiting on the network, which is how one process holds many concurrent requests. A coroutine is created with async def, started with await, and gathered in parallel with asyncio.gather. The consequence for a task queue is that a worker written this way can interleave many jobs in one process instead of holding a thread per job.

Does Celery support asyncio?

Not natively. Celery workers are process or thread based and its task API is synchronous, so running a coroutine inside a Celery task means bridging the two yourself, commonly with asyncio.run inside the task body. That gives up the concurrency benefit, because the worker still occupies a slot for the whole call. taskiq is async from the start, so the decorator and the enqueue call are both awaitable, which is the gap it set out to fill.

What are some alternatives to Celery for Python task queues?

Dramatiq is the closest comparison, and taskiq names it as inspiration alongside Celery. Other options cover different needs: Huey is a lighter single-process queue, RQ is Redis-only and deliberately simple, and arq is async-first with Redis only. taskiq differs mainly in making the broker a separate plugin package, so the core stays small and you choose NATS, Redis, RabbitMQ or Kafka by installing one extra distribution.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. taskiq-python/taskiq on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/taskiq-python-taskiq.svg)](https://hysenlabs.com/projects/taskiq-python-taskiq)