By Ryan Calloway. Updated May 2026.
Three frameworks. Three correct answers. Django REST still wins on “ship a full app with admin and auth in a weekend”; Flask still wins on “tiny service, three routes, do not get in my way”; FastAPI wins on “typed JSON API, async, OpenAPI for free”. The JetBrains Python Developers Survey 2024 (published February 2025, the most recent edition with framework breakdowns) puts FastAPI at 38%, Django at 35%, Flask at 34%, and Django REST Framework at 20% — FastAPI’s climb from 21% in 2021 is the fastest rise in any web-framework category they track. The recurring r/Python “FastAPI vs Django vs Flask” threads land on the same three-way verdict in dozens of replies. This guide is the side-by-side from one CRUD spec, the five questions that actually decide it, and which team shape fits each.
Verdict at a glance
- Best for: FastAPI 0.115+ for new typed JSON APIs — Pydantic v2 native, async-first, OpenAPI auto-generated.
- Not best for: Django REST 3.15+ on a tiny three-route service. Cold start and boilerplate make it a poor fit; reach for Flask 3.0+ or FastAPI instead.
- Watch out for: Async footguns in FastAPI (a blocking call inside
async defstalls the event loop), and DRF’s serializer cost at high RPS. - Use Django REST for: backends where the admin UI, ORM, auth, and migrations are part of the deliverable — marketplaces, internal SaaS, anything ops staff will operate.
Quick answer
Pick FastAPI for a new typed JSON API where Pydantic schemas, async I/O, and OpenAPI auto-generation matter. Pick Django REST Framework when the admin UI, ORM, and auth are part of the product. Pick Flask for tiny services and prototypes where every dependency is a deliberate choice. The five questions below tell you which one you are actually building, and the same CRUD spec implemented in each framework shows what the code looks like in practice.
The short comparison
| Dimension | FastAPI 0.115+ | Flask 3.0+ | Django 5.2 / 6.0 + DRF 3.15+ |
|---|---|---|---|
| Runtime model | ASGI, async-first | WSGI by default; ASGI via adapter | WSGI primary, ASGI supported; async ORM still partial |
| Validation | Pydantic v2 (native) | Marshmallow or Pydantic (BYO) | DRF Serializers (native) |
| OpenAPI / Swagger | Auto-generated | flask-smorest or flasgger |
drf-spectacular |
| Admin UI / auth / ORM | None bundled | None bundled | All bundled, mature |
| Cold start (typical) | 0.5–1.0 s | 0.2–0.4 s | 2–3 s |
| Lines for the same CRUD spec | ~25 | ~30 + auth scaffold | ~15 + free admin and auth |
| JetBrains 2024 adoption | 38% | 34% | 35% Django / 20% DRF |
| TechEmpower Round 23 tier | High async | Mid sync | Mid sync |
Throughput tier comes from TechEmpower Framework Benchmarks Round 23, the final edition before the repository was archived in March 2026. Real apps with database round-trips spend most of their time on I/O; framework overhead is single-digit milliseconds and rarely the bottleneck.
5 questions that pick one
1. Do you need an admin UI, auth, and ORM out of the box?
If the answer is yes — ops staff will operate the data, the data model is relational, the deliverable includes a CRUD interface for non-developers — Django REST Framework is the boring, productive answer. The admin alone is a meaningful share of the actual delivered value on internal tools and marketplaces. The recurring “the admin is Django’s killer feature” threads on r/django are not nostalgic. They describe how teams ship the operations interface in two days that would take two weeks elsewhere. If the answer is no, skip to question 2.
2. Is your workload genuinely I/O-bound?
Async helps when the request path waits on slow downstream calls: 3 to 5 internal services per request, an LLM streaming response, a database query that fans out, a WebSocket. Async hurts when a CPU-bound task blocks the event loop, or when a junior dev writes requests.get(...) inside an async def handler. The most common FastAPI production failure I see is exactly that: the team picked async for a workload that does one Postgres query per request, and the blocking driver inside the async handler stalled latency under load. If your workload is one DB query and a JSON response, sync Django or Flask is fine; if it is fan-out to multiple services or streaming, FastAPI’s async-first runtime is worth the discipline cost.
3. How much does typed clients and OpenAPI matter?
FastAPI generates OpenAPI from Pydantic models for free; the frontend team plugs straight into a generated TypeScript client. DRF gets there with drf-spectacular as an add-on; Flask gets there with flask-smorest. If you are shipping a typed mobile or web client and the API contract is the integration boundary, FastAPI’s zero-config OpenAPI is hard to beat. If your API is internal and consumed by Python services that import the same Pydantic models, the difference is smaller.
4. What does your team know, and who will you hire?
The 2024 JetBrains numbers put Django and Flask at the top of the candidate pool by raw count — both have a decade-plus of accumulated answers on Stack Overflow. FastAPI hiring depth has caught up since 2023; the candidate pool is real and growing. Litestar’s pool is small enough that I would not bet a regulated production stack on it yet. If your team has deep Django investment and no async pain, switching to FastAPI for “modernity” is a project, not an upgrade. If your team is two TypeScript developers learning Python, FastAPI’s type-driven model fits how they already think.
5. How long will this service live, and what is its blast radius?
A three-month internal tool can pick whatever the team likes. A five-year payment service inherits a maintenance horizon. Django’s “boring” reputation is the feature: 20 years of security patches, an admin a junior can extend, an ORM with predictable query plans. Flask is also boring, but every non-trivial feature is a library you added and now own. FastAPI is boring on its second-generation surface (Pydantic v2, Python 3.12+) but still moves faster than Django on breaking changes. Match the framework’s release cadence to your service’s expected lifespan.
The same CRUD spec, three implementations
An /items endpoint that lists items, a POST /items with validation, and an authenticated read on /me. Same Postgres database, same JWT auth, same response shape. This is the realistic benchmark — the one that shows how much code you actually write per endpoint.
FastAPI 0.115+ with Pydantic v2
from fastapi import FastAPI, Depends
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
app = FastAPI()
class Item(BaseModel):
id: int
name: str = Field(min_length=1, max_length=100)
price_cents: int = Field(ge=0)
class ItemCreate(BaseModel):
name: str = Field(min_length=1, max_length=100)
price_cents: int = Field(ge=0)
@app.get("/items", response_model=list[Item])
async def list_items(db: Session = Depends(get_db)):
return db.query(ItemORM).all()
@app.post("/items", response_model=Item, status_code=201)
async def create_item(payload: ItemCreate, db: Session = Depends(get_db)):
obj = ItemORM(**payload.model_dump())
db.add(obj); db.commit(); db.refresh(obj)
return obj
@app.get("/me", response_model=User)
async def me(user: User = Depends(current_user)):
return user
About 25 lines. The Pydantic v2 models double as request validation, response serialization, and OpenAPI schema. Switch to asyncpg through SQLAlchemy 2.0’s async API and the database round-trip becomes non-blocking too.
Flask 3.0+
from flask import Flask, request, jsonify
from marshmallow import Schema, fields, validate, ValidationError
app = Flask(__name__)
class ItemSchema(Schema):
id = fields.Int(dump_only=True)
name = fields.Str(required=True, validate=validate.Length(min=1, max=100))
price_cents = fields.Int(required=True, validate=validate.Range(min=0))
@app.get("/items")
def list_items():
items = ItemORM.query.all()
return jsonify(ItemSchema(many=True).dump(items))
@app.post("/items")
def create_item():
try:
data = ItemSchema().load(request.json)
except ValidationError as e:
return jsonify(e.messages), 400
obj = ItemORM(**data); db.session.add(obj); db.session.commit()
return ItemSchema().dump(obj), 201
@app.get("/me")
@auth_required
def me(user):
return UserSchema().dump(user)
About 30 lines, with explicit serialization and error handling. Validation lives in Marshmallow rather than the framework. Auth is a decorator you wrote. Every framework feature here is a deliberate choice you made.
Django REST Framework 3.15+ on Django 5.2 LTS or 6.0
from rest_framework.viewsets import ModelViewSet
from rest_framework.serializers import ModelSerializer
from rest_framework.generics import RetrieveAPIView
from rest_framework.permissions import IsAuthenticated
class ItemSerializer(ModelSerializer):
class Meta:
model = Item
fields = ["id", "name", "price_cents"]
class ItemViewSet(ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
# urls.py
router = DefaultRouter()
router.register(r"items", ItemViewSet)
class MeView(RetrieveAPIView):
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
def get_object(self): return self.request.user
About 15 lines if you only count the API code. You also get an admin UI, ORM migrations, auth, sessions, and CSRF for free. Django 6.0 (released December 3, 2025; see the official release post) added more native async views and broader async ORM coverage; the async docs still flag that some ORM operations need sync_to_async(). Cold start sits around 2 to 3 seconds on a warm container; that matters for serverless and not at all for long-running services.
Where each one actually wins (and loses)
FastAPI 0.115+: typed teams shipping new APIs
Wins on auto-generated OpenAPI, native Pydantic v2 validation, async I/O, WebSockets and Server-Sent Events without a separate library. The official tutorial takes about 20 minutes for a typed CRUD endpoint. Loses on no bundled admin, no bundled ORM, no bundled auth — you assemble. For a four-person team that knows what they want, that is fine. For a solo dev shipping a marketplace, it is more decisions than you have time for.
Django REST Framework 3.15+ on Django 5.2 / 6.0: full-stack teams
Wins on the admin, the ORM, auth, migrations, sessions, CSRF, file uploads — you start with all of it. DRF’s permission classes scale from “anyone” to object-level rules without leaving the framework’s mental model. Loses on cold start, partial async (Django 6.0 closed some of the gap on December 3, 2025, but full async ORM is still flagged as in-progress in the Django 6.0 async docs), and request throughput on simple endpoints. End-to-end against the same database, the gap closes; on synthetic charts, Django looks slower than it is.
Flask 3.0+: small services and prototypes
Wins on a tiny surface (the framework fits in your head; reading the source is a Sunday afternoon), composability (pick the ORM, validation, auth, migrations — every piece is your call), the smallest cold start of the three, and 14 years of accumulated patterns. Loses on every framework feature being a decision and an integration. For a team of one or two, that is fine. For a team of five or more, you spend more time gluing than building.
Migrating between them
Flask to FastAPI is the cleanest migration: route handlers map one-to-one, swap Marshmallow for Pydantic v2, decide which handlers go async. The recurring r/Python “I migrated from Flask to FastAPI” threads put a 50-endpoint app at one to two weeks of focused work, with the validation-layer rewrite as the long pole.
Flask or FastAPI to Django is a rewrite, not a migration. The data model migration is the easy part; replacing your ad-hoc admin and auth with Django’s is the work. Budget months, not weeks.
Django to anything else is a multi-month project. The admin and the ORM lock-in are real, and the longer the service has lived, the deeper the dependency. I have rarely seen a successful “Django to FastAPI” rewrite that did not also redefine the product. Usually the right move is to keep Django REST for the admin-heavy core and add a FastAPI service for the typed-API edge.
FAQ
Is FastAPI faster than Django in 2026?
On a synthetic JSON benchmark, yes — FastAPI sits in the high-async tier on TechEmpower Round 23, Django in the mid-sync tier. On a real app with a database round-trip, both spend most of their time waiting for I/O; framework overhead is single-digit milliseconds. Async I/O matters more than the framework label, and async only matters if your workload is genuinely I/O-bound across multiple downstream calls.
Does Django support async in 2026?
Yes, partially. Django 3.1 added async views in 2020. Django 5.x stabilised async ORM basics; Django 6.0 (December 2025) extended async ORM coverage further. The official async docs still note that “we’re still working on async support for the ORM” and recommend sync_to_async() for unsupported operations. For pure async-throughout-the-stack, FastAPI is still ahead.
Can I use SQLAlchemy with Django?
Yes, but you lose the Django admin and migrations for those models, which is most of the reason to use Django in the first place. Most teams stick to one ORM per project. If you want SQLAlchemy as the ORM, FastAPI or Flask is a more honest fit.
What about Litestar (formerly Starlite)?
Litestar is the strongest technical challenger to FastAPI in 2026 — msgspec-based serialization is faster than Pydantic v2 in the synthetic charts, the dependency injection is cleaner, the typing is stricter. The trade-off is ecosystem size and hiring pool. For a fashion-driven side project, look at it. For a default new production project, FastAPI’s adoption and tutorials still win.
Should I learn Flask first in 2026?
If you are learning Python web development today, FastAPI is the more useful starting point. Flask was the right answer in 2015; in 2026 it is the answer for niche cases. Start with FastAPI, learn Django when you join a team using it. Add Flask later if you maintain a service that already runs on it.
Which one for AI and LLM backends?
FastAPI. Server-Sent Events, WebSockets, async streaming, and Pydantic v2 validation match how LLM wrapper services are built. Almost every serious LLM backend tutorial published since 2024 uses FastAPI; that is not coincidence, it is a fit.
Sources and further reading
- JetBrains Python Developers Survey 2024 — framework adoption percentages.
- TechEmpower Framework Benchmarks Round 23 — final authoritative edition (archived March 2026).
- FastAPI 0.115+ docs and release notes.
- Django 6.0 release announcement (December 3, 2025) and Django async support docs.
- Django REST Framework 3.15+ docs.
- Flask 3.x docs.
- “Which Is the Best Python Web Framework” — PyCharm blog, updated February 2026.
Once you have picked the framework, the database choice is next; the PostgreSQL vs MySQL guide covers the call. For the validation layer specifically (Pydantic or dataclasses), the dataclasses vs Pydantic guide walks through the trade-offs. If you are wiring CI for whichever stack you pick, the GitHub Actions CI/CD tutorial has the patterns.