The Most Popular Python Web Frameworks in 2021

Web frameworks are powerful tools. They abstract the common aspects of building web sites and APIs and allow us to build richer, more stable applications with less effort.

A broad range of web frameworks is available to us in Python. Some are proven favorites with large ecosystems and communities. Others excel in niche use cases or for specific kinds of development. Still, others are up-and-comers with compelling new reasons to be considered. Read on to explore the options and find what will work best for you.

If you already know what interests you, use the links below to skip ahead:

If you want help deciding, you can also skip straight to our recommendations overview.

How to Choose the Right Framework for Your Needs

Frameworks are designed to solve different problems and make tradeoffs to better serve their intended audiences. If everyone had the same goals we would only need one framework! As you evaluate frameworks some important considerations include:

In addition, consider the quality of available documentation for potential choices and the size of the community around a project - this affects both the range of existing plugins or integrations you can leverage to make development quicker and the likelihood of being able to get help as you get stuck.

Keep these aspects in mind as you explore your options - there are a lot of them! Depending on the longevity of your project, also consider whether a framework is likely to grow with you. Will it be a good fit for your application both now and in the future?

Full-Stack Frameworks vs. Microframeworks vs. Asynchronous Frameworks

Python frameworks can be roughly divided into three camps, full-stack frameworks, microframeworks, and asynchronous frameworks. 

Full-stack frameworks are generally focused on building larger, full-featured applications and offer a lot of common functionality out of the box. If you are looking to build something complex quickly or want reasonable defaults for how to put an application together without making all the choices yourself, a full-stack framework is a good choice. Full-stack frameworks generally provide you with sensible defaults for communicating with databases, templating your views, managing queues, background jobs and other common aspects of larger applications.

Microframeworks are generally focused on providing a small core of functionality and invite the developer to make their own choices about which libraries and technologies to add in for other functionality. This has the advantage of allowing for much more control over application design and can result in better application performance. They typically require the developer to pick their own database abstraction layer and other libraries. Microframeworks can be a great choice for smaller, more narrowly focused applications, API development, or applications where performance is more important.

Asynchronous frameworks are focused on delivering high levels of performance by allowing a very large number of concurrent connections. While you can boost the concurrency of most synchronous frameworks significantly by pairing them with async-capable servers like gevent, natively asynchronous frameworks go one step further with a completely asynchronous stack. Generally, asynchronous frameworks require more rigor in programming style and have a more limited set of plugins. Asynchronous frameworks are great when you need to provide a specific functionality at a very high volume.

Full-Stack Python Frameworks



Django is the most popular full-stack framework for Python. It has a well-deserved reputation for being highly productive when building complex web apps. Dubbed "the web framework for perfectionists with deadlines", its focus is rapid development with well-documented options for common cases.

Django has been around for more than a decade (first release in 2006) and it is mature, comprehensive, well-documented and has a very large community. It is an opinionated framework, which means it makes a lot of decisions for the developer. Positives of this approach are making it quicker to start development, "blessed" integrations that just work, and more headspace to focus on the custom needs of your project instead of which libraries to use. In addition, Django projects tend to look pretty similar, which makes it easier for developers to ramp up quickly on projects that are new for them and for teams to organize their efforts consistently.

Django offers a lot out of the box, including its own object-relational mapper (ORM) for working with databases, a standard approach to authentication and authorization, an automatically generated admin interface (helpful for rapid prototyping), integrated caching and more.

Good for projects small and large, Django can be scaled well for reasonable loads and has been used by many high traffic sites including Instagram, Mozilla, and the Washington Post. Django has asynchronous features in version 3.0, with async views and middleware forthcoming in version 3.1.

While traditionally focused on full-stack web applications, Django is also well suited to developing API-only backend applications. Mature integrations exist for quickly building both REST and GraphQL APIs with Django.

Bottom line: The de-facto standard for good reason, Django is the dominant full-stack framework for Python. Excellent for getting started quickly and with a proven track record for scaling, Django is a great fit for many projects. If you prefer to customize more than Django allows, consider Pyramid and the microframeworks. If you need very high concurrency, explore the asynchronous frameworks.



Pyramid is another popular full-stack framework. With roots in the Pylons project, it has been in development as long as Django and is also a very mature option. 

In contrast to Django, Pyramid is less opinionated. It provides routing, renderers, and command-line tools for bootstrapping a project, but offers you the ability to choose your own database layer, templating system, and more via an extensive set of plugins

With its fundamental flexibility, Pyramid is a nice middle ground if you are trying to decide between a full-stack framework or a microframework. Pyramid allows you to start smaller than Django and grow the complexity of your codebase as needed. This flexibility in library support can be important when you have specialized requirements or are interfacing heavily with systems that Django may not integrate with well (legacy databases are a common example).

Pyramid has a dedicated fan base and an active community who appreciate its grow-as-you-go nature and fundamental flexibility. If you go with Pyramid, expect extra work to choose components upfront. However, this can be time well spent in the long run if it allows you to permanently accelerate aspects of development that are critical for you.

Bottom line: A powerful mix of flexibility and control makes Pyramid a compelling alternative to Django for some projects.



web2py  is a full-stack framework that focuses on ease of development, with its own web-based IDE, debugger, and deployment controls. It was inspired by Ruby on Rails and Django and follows an MVC (Model View Controller) design.

The project started as a teaching tool and has an emphasis on common functionality with sensible defaults. It has a much easier learning curve than most frameworks and is extremely easy to install and get started with. Documentation is great and loads of functionality comes built-in, including a scheduler, 2FA helpers, and a nice ticketing system that gets automatically populated by defects in production.

web2py has a smaller community than Django and some other frameworks, but a very friendly one. Lots of tutorials and resources are available. 

Bottom line: Best suited to newer programmers or developers experimenting with web development. Not a great fit for new larger-scale commercial projects.



Billed as the "framework that scales with you", TurboGears allows you to start your application as simple as a single file (like a microframework) or scale all the way up to a full-stack app with command-line tools to support management. In this sense, it is similar to Pyramid, with the benefit of more control/customization at the expense of requiring more work upfront to determine how you want to structure your app and which libraries you want to integrate.

The default ORM for TurboGears is the excellent SQLAlchemy. TurboGears has interesting differences in how it handles routing and its default templating solution. Unlike most full-stack frameworks, routing is handled through an object hierarchy instead of mapping regular expressions to controllers (mapping is available as an alternative option). The default template system for TurboGears is Kajiki, an XSLT-inspired language.

Bottom line: TurboGears scales well with a lot of control from small projects to larger ones. However, Pyramid offers a similar range of flexibility and is probably a better choice for most people.



Masonite is a relatively new (2017) framework that has a similar design philosophy to Django but aims to improve on some common pain points. It offers enhanced code scaffolding, routing middleware, and built-in support for email sending, S3 upload, and queuing.

Masonite's architecture is highly extensible and its integrated capabilities are impressive. Documentation is good and there is an active Slack channel for support. It uses its own ORM, Orator, based on ActiveRecord.

As a newer framework, Masonite's community is small but growing. It is actively improved and has a lot to like. Given its smaller mindshare, it is harder to find developers familiar with Masonite but if the additional out-of-the-box capabilities are a good fit for your needs it may speed up your development.

Bottom line: A newer contender, Masonite makes common tasks like email management, uploading files to the cloud, and payment processing easy out of the box.

Microframeworks for Python



Flask is an incredibly popular solution for both web apps and microservices. Originally inspired by the Ruby framework Sinatra, Flask focuses on providing a core set of functionality (request handling, routing, WSGI compliance, templating) and offers a modular design for the addition of anything else you need.

As a result, starting an application is incredibly simple. You can build a working web application in just a few lines:

from flask import Flask, escape, request

app = Flask(__name__)

def hello():
    name = request.args.get("name", "World")
    return f'Hello, {escape(name)}!'

Flask has a wide range of available extensions, allowing you to integrate your own choices for storage, database interaction, authentication and authorization, security and more. It will take time to integrate and set up your choices, but apps can be built incrementally and won't include libraries and code for things your application doesn't use.

Flask apps typically start in a single file, but can scale to be very large. There are common patterns for arranging flask apps and flask also offers blueprints as a way to make larger applications more modular and manageable. 

Bottom line: Extremely flexible, Flask is great for user-facing web apps, APIs and microservices alike. Flask is the most popular microframework for Python. 



Bottle has a similar syntax to Flask (it actually predates it by a year), but is distributed as a single file with no dependencies outside of the python standard library. This makes it easy to run in any environment, including places where installing libraries are difficult. It also means dependency management can be trivial, which can be great for smaller projects.

from bottle import route, run, template

def index(name):
      return template('<b>Hello {{name}}</b>!', name=name)

run(host='localhost', port=8080)

The feature set is quite similar to Flask, but the active community is much smaller. The available plugins are also more limited. In addition, there are fewer tutorials and it can be harder to find code examples or get help. 

Bottle is mostly geared towards apps with very small codebases and doesn't have a clear path for scaling code organization as things get complex. Simplicity is the focus. Having fewer dependencies can simplify deployment a lot (just stick in your project directory) and get you from a prototype to a production app more quickly.

Bottom line: Great for personal projects, small apps, or deployment scenarios where managing complex dependencies is difficult.

H3: CherryPy


CherryPy is another mature microframework (around since 2002) with its own fans. One major difference from Flask and Bottle is that CherryPy is object-oriented and focuses on being as "pythonic" as possible. Put another way, CherryPy aims to make writing a web app as similar to writing general python code as possible. Let's look at an example:

import cherrypy

class HelloWorld(object):

    def index(self):
        return "Hello World!"


You can see that the app is defined as a class, as opposed to the function-based approach of Flask. Also the routing itself is object-based; the `@cherrypy` decorator marks which object methods should be turned into routes, where in Flask the decorators define the routes themselves. Some developers prefer this form of implicit routing while others find it challenging.

One of CherryPy's strengths is its web server, which is bundled into the framework. It is fast, production-ready, HTTP/1.1-compliant, thread-pooled and can be used with any Python WSGI application. In fact some developers use CherryPy's web server to run other (non-CherryPy) WSGI apps because it is so easy to set up and use.

CherryPy also includes a lot of built-in functionality, including session management, authentication, static content handlers, caching, profiling and more. Plugins are available which tap into a rich set of extension points.

CherryPy's community is much smaller than Flask's, which means a smaller community of plugins, fewer tutorials, etc.

Bottom line: Worth a look if you prefer an object-oriented development style.



Falcon is a performance-focused framework for building REST APIs and microservices. Given its focus on speed, Falcon provides a limited feature set: routing, middleware, hooks, strong error/exception handling, and WSGI helpers to make unit testing easy.

Falcon sheds any interest in user-facing apps and focuses on serving JSON through REST endpoints. Note the REST verb (GET) in this code example:

import falcon

class QuoteResource:
       def on_get(self, req, resp):
        """Handles GET requests"""
        quote = {
            'quote': (
                "I've always been more interested in "
                "the future than in the past."
            'author': 'Grace Hopper'
        } = quote

api = falcon.API()

api.add_route('/quote', QuoteResource())

Given its tuning and singular focus, Falcon is radically faster (20-75x!) than Django and Flask in benchmarks of very basic requests. Other strong aspects of Falcon are idiomatic HTTP error responses (a common pain point when constructing APIs) and straightforward exception handling. It runs on PyPy and supports Cython on CPython, two options to consider for maximum performance.

If you like the idea of Falcon but want a more full-featured solution, take a look at Hug, a framework built on top of Falcon which adds version management, automatic documentation and type-driven automatic validation.

Bottom line: If you want to build highly-performant REST/JSON APIs, Falcon may be for you.

Asynchronous frameworks for python



Sanic is a relatively new (first release in 2016) asynchronous web framework and server, "written to go fast".

While most of the full-stack and microframeworks on this list have been around for a decade or more, the addition of asyncio in Python 3.5+ has opened the doors to a whole new generation of highly performant asynchronous frameworks. Sanic is one of the most established options in this new generation.

Sanic's syntax is fairly similar to Flask, with the addition of end-to-end async support:

from sanic import Sanic

from sanic.response import json

app = Sanic()

async def test(request):
    return json({"hello": "world"})

if __name__ == "__main__":"", port=8000)

It features strong routing capabilities, middleware, streaming, WebSocket support, cookie management, route versioning, static file serving and more. Sanic is a natural fit if you need to handle long-lived connections like WebSockets or need a high level of concurrency out of your API.

With an asynchronous framework, you will need to wrap your head around asynchronous programming in Python, with its related caveats, complexity, and debugging challenges. It is worth taking the time to assess whether you really need the performance of a fully async API, but if you do, Sanic is worth a look!

Bottom line: A mature, established option for developing highly performant asynchronous APIs



FastAPI is newer than Sanic (first release in early 2019) but gaining momentum fast. It excels at building REST or GraphQL APIs, and can handle synchronous requests, asynchronous requests, streaming and websockets. 

It also has built-in support for authentication and authorization, data validation, JSON serialization and features automatic API documentation following the OpenAPI standard.

from fastapi import FastAPI

app = FastAPI()

def read_root():
    return {"Hello": "World"}

def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

The feature set of FastAPI is really impressive and it hits a sweet spot with its combination of flexibility and ease of development. It is very thoughtfully designed and leverages type hinting and dependency injection broadly to reduce bugs in development. In addition, FastAPI's documentation and editor support are excellent.

FastAPI's syntax is quite similar to Flask, which makes it a good choice if you are looking to migrate existing Flask codebases to a fully async solution.

Bottom line: A framework on the rise, FastAPI is worth exploring for your next async project.



Starlette is a lightweight ASGI framework and toolkit, providing primitives and modular integration to let you build your application with any degree of control that you want.

ASGI is a successor to WSGI, providing a standard interface between async-capable web servers, frameworks, and applications. Note that ASGI supports both synchronous and asynchronous operations and ASGI includes a WSGI backward-compatibility implementation.

As a framework, Starlette ties together its various functions for you, including WebSocket support, GraphQL support, in-process background tasks, session and cookie support, CORS, GZip, static files, and more. You can also use each piece independently, picking and choosing specific pieces of the toolkit.

Since Starlette is a toolkit first, use as a framework can feel more compositional, with concerns exposed separately:

from starlette.applications import Starlette

from starlette.responses import JSONResponse

from starlette.routing import Route

async def homepage(request):
    return JSONResponse({'hello': 'world'})

app = Starlette(debug=True, routes=[
    Route('/', homepage),

FastAPI is actually built on top of Starlette, adding syntax convenience and additional features. For most teams FastAPI is probably a better place to start, but Starlette offers maximum control and a powerful set of primitives.

Bottom line: If you want to work close to the metal on your own async tools, Starlette is an awesome place to start.


Tornado is an older async web framework, created well before asyncio capabilities were baked into Python. Originally created by FriendFeed and first released in 2009, Tornado is a proven solution for scaling into tens of thousands of open connections in Python.

The core of Tornado is a highly customizable application model with strong underlying network libraries. It includes routing, templating, session and cookie management, native WebSocket support, security features and has a mature range of options for different datastores. It is less full-featured than something like Django but has a lot more features built-in than a typical microframework. Tornado uses verb-style methods on request handler classes, so it lends itself to a more object-oriented development style:

import tornado.ioloop

import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),

if __name__ == "__main__":
    app = make_app()

Tornado continues to be actively improved and maintained, with a robust community. It is used by Facebook, Quora, and others in their production architecture. 

Tornado 6+ uses asyncio under the hood and requires Python 3.5+, but Tornado 5 can be used with Python  2.7 and newer. Running on Python 3.5+, Tornado asynchronous coroutines use the native `async` / `await` syntax. For earlier versions, Tornado offers a generator syntax which has similar capabilities. Notably, Tornado has a mature bridge for Twisted, which allows you to run both Twisted apps and libraries on top of Tornado.

Bottom line: A proven solution for scaling into massive numbers of concurrent requests, Tornado is worth exploring if you want an established option with a strong community, are tied to older codebases or have to use older versions of Python.

Which Python web framework is best for you?

As should be clear from this list, there are a lot of good options. In fact, there are many other frameworks available for Python - we've intentionally limited this article to just the frameworks we believe are most worth considering in 2020.

A good place to start is the criteria we mentioned at the beginning of this article. What are your goals? Are you looking to experiment with something new, build something quickly with proven technology, or learn new skills? How many people will work on your codebase and how long will it be around? These are all helpful clues as to the right choice.

There is no one framework that is perfect for everyone, but here are some general suggestions:

Did you find this article helpful or see something you disagree with or think should be improved. Please let us know, we value your feedback!