Async rate limiter for FastAPI with Redis or native in-memory backend
Project description
fastapi‑easylimiter
An ASGI async rate-limiting middleware for FastAPI with Redis or in-memory caching, designed to handle auto-generated routes (e.g., FastAPI-Users) without decorators, for simplicity and performance.
Features
- Async rate limiting
- Optional temporary IP bans
- Configurable threshold (default 10 violations)
- Sliding 30-min offense window
- 5-min ban on repeat abuse
- Cache Backends
- Redis (recommended for multi-instance deployments)
- In-Memory (single worker dev)
- Path-based rules
- Supports multi-rule prefix matching
- Global and per-route limits
- Standard rate-limit headers
X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-ResetRetry-Afteron429responses
- Proxy Aware
- Uses
'X-Forwarded-For'only when the sender is trusted - Rejects spoofed XFF headers
- Supports
'CF-Connecting-IP'from Cloudflare, verified against CF CIDRs - Fallback to ASGI
scope["client"]if no trusted headers exist
- Uses
- Zero dependencies beyond Redis client
- Starlette-style ASGI middleware
- Custom responses
HTMLResponsefor browser clientsJSONResponsefor API clients
Installation
pip install fastapi-easylimiter
Usage
from fastapi import FastAPI
from fastapi_easylimiter import AsyncRedisBackend, InMemoryBackend, RateLimiterMiddleware
import redis.asyncio as redis_async
app = FastAPI()
REDIS_URL = "redis://localhost:6379/0"
# Redis backend (recommended for multi-instance deployments)
redis_client = redis_async.from_url(REDIS_URL, decode_responses=True)
backend = AsyncRedisBackend(redis_client)
# Or for single-instance/local development:
# backend = InMemoryBackend()
rules = {
"/": {"limit": 600, "period": 60}, # GLOBAL: 600 req/min per IP
"/api/": {"limit": 10, "period": 1},
"/api/users": {"limit": 1, "period": 2},
}
app.add_middleware(
RateLimiterMiddleware,
rules=rules,
backend=backend,
trusted_proxies=[""], # OPTIONAL: your proxy IPs
cloudflare=False, # OPTIONAL: enable CF-Connecting-IP
enable_bans=True, # OPTIONAL: enable temporary bans
ban_threshold=15, # Violations before ban
ban_duration=300, # Ban length in seconds
offenses_ttl=900, # Offense counting window
ban_page="<p>Your IP has been temporarily banned.</p>", # OPTIONAL custom HTML ban page
rate_page="<p>Too many requests. Please try again later.</p>", # OPTIONAL custom HTML rate-limit page
)
Example:
/api/users/mematches/api/usersand/api. If any rule is exceeded →429returned.
Redis Lua Script
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local count = redis.call("INCR", key)
local ttl = redis.call("TTL", key)
if count == 1 or ttl < 0 then
redis.call("EXPIRE", key, window)
ttl = window
end
local reset = now + ttl
local exceeded = count > limit and 1 or 0
return {count, reset, exceeded}
Redis Key Patterns
| Key Pattern | Example | Purpose |
|---|---|---|
rl:{client_ip}:{prefix} |
rl:203.0.113.5:/api |
Rate-limit counter per IP+prefix |
ban:{client_ip} |
ban:203.0.113.5 |
Temporary ban |
offenses:{client_ip} |
offenses:203.0.113.5 |
Offense counter for ban tracking |
Middleware Parameters
| Parameter | Type | Description |
|---|---|---|
app |
ASGIApp | FastAPI/ASGI app |
rules |
dict | { prefix: {"limit": int, "period": int} } |
backend |
Redis or InMemory backend | Rate-limit storage |
trusted_proxies |
list[str] | Proxies allowed to trust XFF headers |
cloudflare |
bool | Enable Cloudflare IP extraction |
enable_bans |
bool | Enable temporary IP bans |
ban_threshold |
int | Violations before ban |
ban_duration |
int | Ban length in seconds |
offense_ttl |
int | Offense counting window in seconds |
ban_page |
str | Custom HTML ban page |
rate_page |
str | Custom HTML rate-limit page |
Screenshot
Contributing
Contributions and forks are always welcome! Adapt, improve, or extend for your own needs.
Support
Parts of this code were generated/assisted by AI (Grok).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_easylimiter-0.3.6.tar.gz.
File metadata
- Download URL: fastapi_easylimiter-0.3.6.tar.gz
- Upload date:
- Size: 8.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f580f9f5abfa8a414f7a4f2b9cdc3bd1aeb3a63f1966830cbf0f87715bb19e5b
|
|
| MD5 |
bfa4e862a53c518dff538a425c5854a5
|
|
| BLAKE2b-256 |
44b993075acf676d7623f1a325295fc77a29d027abfa818b1abdf64be146dd49
|
File details
Details for the file fastapi_easylimiter-0.3.6-py3-none-any.whl.
File metadata
- Download URL: fastapi_easylimiter-0.3.6-py3-none-any.whl
- Upload date:
- Size: 10.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d733b9edbdb5053a423707defbfedb5dc8dd2982f683c339a988ffe46dae795
|
|
| MD5 |
8ece9cd2e061c0f566062bfdb5cbb1e2
|
|
| BLAKE2b-256 |
29b7ee79b3d85aae9a33030661be54c9d49b21f09c758512e20755caa9f8fe3c
|