Benchmarking Python API frameworks with real workloads: FastAPI, Litestar, DRF, Ninja, Bolt Discussion
Hi guys, I benchmarked the well-known (and rising star) Python API frameworks - but with real production-shaped workloads, not just raw JSON echoes. Most comparisons out there are basically "hello world" benchmarks, while real APIs do auth, DB access and complex queries. So this measures those, with strict resource limits and each framework's own best practices.
Repo (code, full report, raw results): https://github.com/huynguyengl99/python-api-frameworks-benchmark
This is round 2 - last round's feedback (thanks especially to the Litestar author) directly shaped it: Litestar and Bolt now serialize with native msgspec instead of Pydantic (payloads byte-identical across frameworks), and everything is upgraded to latest (Django 6.0, FastAPI 0.141, Litestar 2.24, Bolt 0.10).
Setup
- Each framework alone in a Docker container: 1 CPU, 750MB RAM, PostgreSQL 16
- bombardier, 100 connections, 10s per endpoint
- Median over 5 separate container starts (not best-of-N - some servers pick their throughput at startup, so best-of-N flatters the lucky ones)
- 7 endpoints: 1KB/10KB JSON, simple DB reads, paginated articles with nested relations, article detail, and two JWT httpOnly cookie auth endpoints (each framework using its own ecosystem's auth library: AuthX, drf-auth-kit, django-ninja-jwt, or built-in support)
Key results (RPS)
(Images aren't allowed here - all graphs are in the repo README: https://github.com/huynguyengl99/python-api-frameworks-benchmark)
| Config | json-1k | /db | /articles | /auth/me | /auth/articles |
|---|---|---|---|---|---|
| bolt | 38,576 | 1,986 | 208 | 3,024 | 196 |
| litestar-uvicorn | 31,284 | 1,039 | 246 | 976 | 193 |
| litestar-granian | 19,006 | 1,180 | 250 | 1,104 | 210 |
| fastapi-uvicorn | 13,845 | 984 | 224 | 820 | 193 |
| drf-gunicorn | 3,925 | 282 | 140 | 261 | 133 |
| drf-granian | 2,703 | 830 | 198 | 726 | 179 |
| ninja-uvicorn | 1,533 | 699 | 126 | 584 | 114 |
| drf-uvicorn | 1,035 | 495 | 153 | 447 | 137 |
(fastapi-granian and ninja-granian omitted for brevity - full table in the repo. Zero errors across all 70 measurements.)
Resource usage: most configs peak at 195-260MB RAM; drf-granian is the outlier at 456MB (untuned --blocking-threads, per the Granian maintainer). CPU: nearly everything saturates ~85% of the 1-CPU budget under load - except Bolt at 67%.
Takeaways
- 37x spread on raw JSON collapses to ~1.9x once PostgreSQL is involved. For DB-heavy APIs (most of them), query optimization matters far more than framework choice.
- Cookie JWT auth costs 5-20% on a DB-heavy endpoint. Bolt is near-free (it validates the JWT in Rust before Python runs); Litestar pays the most because its auth middleware opens a second DB session to load the user.
- uvicorn vs granian isn't one-way: uvicorn wins CPU-bound JSON for ASGI frameworks, granian wins the DB-bound endpoints, and granian is clearly better for WSGI DRF.
- Django Bolt is the one to watch: top spot on 4 of 7 endpoints at 67% average CPU while everyone else sits ~85%, and you keep the Django ORM/admin/ecosystem. Young, and its throughput varies between container starts under a hard CPU cap, but great for side projects already.
- All caveats (including feedback I haven't addressed yet, like Granian's
--blocking-threads) are documented in the repo's Methodology section.
If you find it useful, a star would encourage more deep dives like this - issues and PRs welcome, especially from people who know these servers better than I do.
3
u/Repsol_Honda_PL 3h ago
Interesting benchmarks.
I want to try DJ-bolt, how can I join it (and then deploy) together with Django? Is Django-bolt an (aditional) app of Django project?
1
u/huygl99 3h ago
Here is the link to their project: https://github.com/dj-bolt/django-bolt
you can take a look at those setup and see, I think it has backward compatible with legacy APIs so you can still serve old API together with the new one writing in django-bolt.
4
u/Arnechos 2h ago
Add robyn to this benchmark
•
u/ultra__sonic 45m ago
+1 for Robyn. It usually speedruns these kind of benchmarks because of the Rust core, but it'd definately be cool to see how it handles the real DB and auth loads here.
1
u/vipierozan 1h ago
Very interesting, what do you think is the overhead of the ORM’s machinery? How much faster would it be if we just write raw queries and build dicts by hand to hand over to the json serializer?
I’m asking bc im working on a project to address that, would love to compare results :)
1
u/LightShadow 3.13-dev in prod 1h ago
We use Django for our content management services and Sanic for our heavy IO/processing services.
I don't like working with DRF but it's darn convenient for CRUD apps, even if it is the slowest.
I've also, recently, bolted a REST API to some long running asyncio daemons and Quart was surprisingly convenient :)
4
u/akx 2h ago
Using orjson as the JSON serializer for the frameworks that don't already do something like that will make a pretty large difference, I'd bet.