When you need a Python web framework that can serve thousands of requests per second without sacrificing developer friendliness, Sanic is often the first name that pops up. Designed from the ground up as an asynchronous, high‑performance microframework, Sanic lets you build a high‑performance web app that rivals Node.js or Go in raw speed while keeping the elegance of Python. In this guide we’ll explore what makes Sanic special, walk through a complete setup, dive into code examples, and share best‑practice tips that help you squeeze every ounce of performance out of your Python stack.
What Is Sanic? A Quick Overview
Sanic is an open‑source, asynchronous web framework built on top of uvloop and asyncio. It was created by Andrew Svetlov in 2016 with a single goal: to provide a Python framework that could handle high concurrency and low latency out of the box. Unlike traditional WSGI frameworks such as Flask or Django, Sanic uses the ASGI (Asynchronous Server Gateway Interface) model, allowing non‑blocking I/O and true parallel request handling.
Core Features That Boost Performance
- Native async/await support – Write asynchronous code with clean syntax.
- uvloop integration – A drop‑in replacement for the default event loop that can be up to 2× faster.
- Zero‑copy response streaming – Send large files or JSON payloads without extra memory overhead.
- Built‑in HTTP/2 and WebSocket support – Future‑proof your API without extra libraries.
- Simple routing syntax – Fast route matching using a trie‑based algorithm.
- Extensible middleware – Add logging, authentication, or compression with minimal impact.
Setting Up a Sanic Project
Getting started with Sanic is straightforward. Below is a step‑by‑step checklist that will have you running a basic app in under five minutes.
1. Install Sanic and Optional Dependencies
python3 -m venv venv
source venv/bin/activate
pip install "sanic[uvloop]" # uvloop gives you the performance boost
2. Create the Project Structure
.
├── app.py # Main entry point
├── routes/
│ └── hello.py # Example route module
└── requirements.txt
3. Write a Minimal Application
# app.py
from sanic import Sanic
from sanic.response import json
app = Sanic("high_perf_app")
@app.get("/ping")
async def ping(request):
return json({"message": "pong"})
if __name__ == "__main__":
# Use uvloop automatically if installed
app.run(host="0.0.0.0", port=8000, access_log=False)
Run the app with python app.py and visit http://localhost:8000/ping. You should see {"message":"pong"} instantly.
Building a High‑Performance API with Sanic
Now that the skeleton is ready, let’s explore how Sanic’s asynchronous nature translates into real‑world speed for API endpoints.
Asynchronous Request Handling
Every route handler in Sanic can be an async def function, allowing you to perform non‑blocking I/O such as database queries, external HTTP calls, or file reads. Here’s an example that fetches data from a mock async function:
# routes/hello.py
from sanic import Blueprint
from sanic.response import json
import asyncio
hello_bp = Blueprint("hello")
async def fetch_user(user_id):
# Simulate a slow I/O operation (e.g., DB call)
await asyncio.sleep(0.05)
return {"id": user_id, "name": "User "+str(user_id)}
@hello_bp.get("/user/<user_id:int>")
async def get_user(request, user_id):
user = await fetch_user(user_id)
return json(user)
Because await asyncio.sleep(0.05) doesn’t block the event loop, Sanic can serve other requests while waiting, dramatically increasing throughput.
Routing and Middleware Best Practices
- Group related routes with Blueprints – Keeps the codebase modular and improves import times.
- Leverage built‑in middlewares – Use
@app.middleware('request')for logging or authentication without adding extra layers. - Prefer JSON over HTML for APIs – JSON serialization in Sanic is highly optimized.
- Enable HTTP/2 when possible – Faster multiplexing of requests over a single connection.
Real‑World Performance Benchmarks
Benchmarks consistently place Sanic among the top Python frameworks for raw request per second (RPS) numbers. Below are representative results from TechEmpower Framework Benchmarks (2024 edition) for a simple “JSON‑Hello‑World” test:
- Sanic (uvloop) – ≈ 95 k RPS
- FastAPI (uvicorn + uvloop) – ≈ 85 k RPS
- Flask (Gunicorn) – ≈ 30 k RPS
- Django (ASGI) – ≈ 28 k RPS
These numbers demonstrate that, when paired with uvloop and a proper ASGI server, Sanic can handle nearly three times the traffic of traditional synchronous frameworks.
Scaling Sanic for Production
Running a single Sanic instance is fine for development, but production environments demand process management, load balancing, and containerization. Below are the most common strategies.
Using Gunicorn with Sanic Workers
Gunicorn’s uvicorn.workers.UvicornWorker works seamlessly with Sanic, providing graceful restarts and multi‑process scaling.
# Install the required extras
pip install "gunicorn[uvicorn]" sanic
# Run 4 worker processes, each listening on the same port
gunicorn app:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
Each worker runs its own event loop, so you can fully utilize multi‑core CPUs.
Deploying with Docker
Containerizing Sanic simplifies deployment on Kubernetes, ECS, or any cloud platform. A minimal Dockerfile looks like this:
# Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt "sanic[uvloop]"
COPY . .
EXPOSE 8000
CMD ["gunicorn", "app:app", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000"]
Build and run:
docker build -t my-sanic-app .
docker run -p 8000:8000 my-sanic-app
Common Pitfalls and Best Practices
- Don’t mix sync and async code unnecessarily – Blocking calls (e.g.,
time.sleep) will stall the entire event loop. - Use connection pools for databases – Libraries like
asyncpg(PostgreSQL) oraiomysqlprovide async pooling that prevents bottlenecks. - Enable response compression – Sanic’s
Compressmiddleware reduces payload size without a noticeable CPU hit. - Monitor with built‑in metrics – Sanic can expose
/metricsfor Prometheus, giving insight into request latency and error rates.