FastAPI
Limitra has no FastAPI-specific code. Use @rate_limit directly on your routes — write a key extractor and an on_exceeded handler using FastAPI's own types.
Basic example
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from limitra import LimitraConfig, rate_limit, RateLimitExceeded
app = FastAPI()
LimitraConfig(redis_url="redis://localhost:6379", project="api")
def on_exceeded(exc: RateLimitExceeded) -> JSONResponse:
return JSONResponse(
{"error": "Too Many Requests", "retry_after": exc.retry_after},
status_code=429,
)
def extract_ip(request: Request) -> str:
return request.client.host if request.client else "unknown"
@app.post("/login")
@rate_limit(requests=5, window=60, key=extract_ip, on_exceeded=on_exceeded)
async def login(request: Request):
...
@app.get("/items")
@rate_limit(requests=100, window=60, key=extract_ip, on_exceeded=on_exceeded)
async def list_items(request: Request):
...
Per-user limit via header
def extract_user(request: Request) -> str:
return request.headers.get("X-User-Id", "anonymous")
@app.get("/items")
@rate_limit(requests=100, window=60, key=extract_user, on_exceeded=on_exceeded)
async def list_items(request: Request):
...
Note
FastAPI injects route parameters by name. The key callable receives the same keyword arguments as the endpoint — declare request: Request in the endpoint signature so it is available to the key extractor.
Sync routes
@app.get("/items")
@rate_limit(requests=100, window=60, key=extract_ip, on_exceeded=on_exceeded)
def list_items(request: Request):
...
Both sync and async routes work identically.