SlowAPI: Rate Limiting for Starlette & FastAPI
SlowAPI is a Python library that provides a flexible rate-limiting extension for Starlette and FastAPI applications. It builds upon the 'limits' library to offer various storage backends (in-memory, Redis, Memcached) and granular control over rate limits per route or globally. The current version is 0.1.9, and it is actively maintained with an irregular release cadence.
Warnings
- gotcha Middleware order is critical. If your application uses other middlewares (e.g., authentication, proxy headers) that modify the `Request` object (e.g., setting `request.client.host`), ensure that `slowapi`'s middleware is added *after* them. This guarantees the `key_func` receives the final, correct request state for identification.
- gotcha The `key_func` (e.g., `get_ipaddr`) is essential for identifying unique requests for rate limiting. Misconfiguring or omitting it will lead to all requests sharing the same limit or ineffective rate limiting. Custom `key_func`s must correctly extract a unique identifier from the `Request` object.
- gotcha To return a custom HTTP 429 (Too Many Requests) response when a rate limit is exceeded, you must explicitly register the `_rate_limit_exceeded_handler` (or a custom handler) with your application's exception handlers. Failing to do so will result in a generic server error (e.g., HTTP 500) instead of the expected 429.
- gotcha The rate-limiting storage backend is determined by the `LIMITS_STORAGE_URI` environment variable or the `storage_uri` parameter in `Limiter`. Not setting it or setting it incorrectly (e.g., forgetting `memory://` for in-memory or incorrect Redis/Memcached URIs) will lead to runtime errors or incorrect rate limiting behavior.
Install
-
pip install slowapi -
pip install 'slowapi[redis]' # For Redis backend pip install 'slowapi[memcached]' # For Memcached backend
Imports
- Limiter
from slowapi import Limiter
- get_ipaddr
from slowapi.util import get_ipaddr
- RateLimitExceeded
from slowapi.errors import RateLimitExceeded
- _rate_limit_exceeded_handler
from slowapi import _rate_limit_exceeded_handler
Quickstart
import uvicorn
from fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_ipaddr
from slowapi.errors import RateLimitExceeded
# Initialize Limiter with a key function and default limits
# Using in-memory storage for simplicity, but can be 'redis://localhost:6379' etc.
limiter = Limiter(key_func=get_ipaddr, default_limits=["5/minute", "100/day"])
app = FastAPI()
app.state.limiter = limiter # Essential for decorator-based limits
# Register the exception handler to return a 429 response
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.get("/unlimited")
async def read_unlimited():
return {"message": "This endpoint is not rate limited."}
@app.get("/")
@limiter.limit("10/minute") # Route-specific limit
async def read_root(request: Request):
# The 'request' argument is required by the key_func (get_ipaddr)
return {"message": "Hello, rate-limited world!"}
@app.get("/fast/")
@limiter.limit("2/second", "/fast/") # Another route with a custom scope
async def read_fast(request: Request):
return {"message": "Too fast, too furious!"}
# To run this application:
# 1. Save the code as 'main.py'
# 2. Run from your terminal: uvicorn main:app --reload --port 8000