FastAPI rate limiting,per customer and per key.
One middleware checks the caller’s API key, charges their credits, and applies their plan’s rate limit before your route runs — across every Uvicorn worker, with no Redis to operate.
- Keys issued per customer
- Credits that refill
- Limits per plan, not per process
Official SDKs with drop-in middleware for the stack you already run
- Python
- Node.js
- Go
- Rust
- PHP
- .NET
- Java
- API requests validated
- 100M+
- Average key validation
- <5ms
- Average analytics ingest
- <5ms
- Check and log, end to end
- <10ms
The usual FastAPI rate limiter counts requests. It doesn’t know your customers.
Most FastAPI projects start with slowapi, a port of Flask-Limiter: a decorator per route and a key function that picks what to count by — usually the client IP.
# main.py — with slowapifrom fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.get("/search")
@limiter.limit("10/minute")
async def search(request: Request):
return {"results": []}- A decorator on every route — miss one and it’s unlimited
- Keyed by IP address by default, not by the customer who is paying
- In-memory by default, so four Uvicorn workers allow four times the limit
- No API keys: issuing, hashing, scoping, and revoking them is still yours to build
- No credit balance: a request can’t cost 5 on one route and 1 on another, or refill each month
- One limit for everyone — no per-plan limits for Free, Pro, and Enterprise customers
- API keys issued per customer, prefixed, hashed, and revocable from the dashboard
- A credit balance per customer — price each route, refill every hour, day, week, or month
- Rate limits set per plan, shared by all of a customer’s keys, from 1 second to 24 hours
- The same count on every worker, instance, and region — no Redis to run
- Over the limit? A 429 with Retry-After, and no credits charged
- Every request logged per customer — status, latency, endpoint — in under 5ms
FastAPI in three steps, one of them code.
ReqKey runs inside your API, not in front of it. Your server asks one question per request and gets an answer in under 5ms.
- 1
Install the SDK
pip install "reqkey[fastapi]" - 2
Set your project key
Copy it from the dashboard into
REQKEY_PROJECT_KEY. It stays on your server. - 3
Add the FastAPI middleware
Every request is checked, charged, and logged before your handler runs.
import os from fastapi import FastAPI, Requestfrom reqkey.fastapi import ReqKeyMiddleware app = FastAPI() app.add_middleware( ReqKeyMiddleware, project_key=os.environ["REQKEY_PROJECT_KEY"], api_id="api_payments", mode="both", # validate keys AND record analytics key_name="X-MyStartup-Key", # where consumers send their key exclude_paths=("/health", "/docs"),) @app.post("/payments")async def create_payment(request: Request): decision = request.state.reqkey return {"created": True, "credits_remaining": decision.credits_remaining}Every request gets one of these answers
- 200Valid key, credits charged — your handler runs
- 402Out of credits
- 403Key disabled, or not allowed on this API
- 429Over the rate limit — no credits charged
Where ReqKey sits
Responses go straight back to your customer. ReqKey sees the key check and the log line, nothing else.
Credits
Charge each route what it costs you.
A lookup can cost 1 credit and a render 5, from the same balance. Excluded paths are never validated, charged, or recorded. In Python:
# Exact paths or trailing-* prefixes: never validated, charged, or recordedexclude_paths=("/health", "/openapi.json", "/docs/*", "/cron/*") # Or decide per request with a sync or async resolvershould_protect=lambda request: request.url.path.startswith("/api/") # Charge different endpoints differentlydef credits_for(request): if request.method == "POST" and request.url.path == "/images": return 5 return 1 app.add_middleware(ReqKeyMiddleware, ..., credits=credits_for)Rate limits
Set limits on plans, not in code.
Your FastAPI code never hard-codes a number. Each plan carries its credits, refill, and rate limit; moving a customer to Pro changes all three with no deploy.
Example plans. You name them and pick the numbers.
Notes for FastAPI teams hit in production.
One middleware, every route
ReqKeyMiddleware wraps the whole app, so a new route is protected the moment you add it. Keep public paths like /health and /docs open with exclude_paths.
Workers share one count
Counts and balances are kept by ReqKey, not in the worker. Run one Uvicorn worker or forty behind a load balancer — a customer on 100 requests a minute gets 100.
Starlette works the same way
The FastAPI adapter is Starlette middleware, so plain Starlette apps use the same class. Any other ASGI 3 app can use the generic ASGI adapter.
FastAPI rate limiting: the questions teams ask.
Something else? Ask the team or read the docs.
slowapi counts requests per key function, usually per IP, and stores counts in memory or Redis you run. ReqKey issues the API keys your customers call with, keeps a credit balance per customer, applies the rate limit on their plan, and logs every request — without a decorator per route or a Redis instance.
Yes. The middleware runs before FastAPI dispatches to your path operation, so def and async def routes are treated the same.
On their plan or on the customer (consumer) in ReqKey — a number of requests per window from 1 second to 24 hours, shared by all of that customer’s keys. Your FastAPI code never hard-codes a limit, so upgrading a customer is a dashboard change, not a deploy.
A check averages under 5ms. ReqKey runs inside your app as middleware, not as a gateway in front of it, so responses go straight back to your customer.
You choose: fail closed and answer 503, or fail open and let requests through. Invalid keys are denied either way, and a validation is never retried, so no one is charged twice.
More Python guides.
Other languages
Ship API keys in FastAPIin five minutes.
Free for your first 5 million requests every month. No card, no gateway, no rewrite.