fastapi‑easylimiter
An ASGI async rate-limiting middleware for FastAPI with Redis, designed to handle auto-generated routes (e.g., FastAPI-Users) without decorators, for simplicity and ease of use.
Features
- Path based rules (
/api/*,/auth/*,/api/users/me, etc) - Fixed & Moving window algorithms (Lua)
RateLimit,RateLimit-Policy,Retry-Afterheaders- ASGI async middleware for FastAPI/Starlette
- Asyncio Redis support
- Easy to configure
- No decorators needed
- HTML/JSON error responses
- Site-wide or per-endpoint bans, with configurable durations
TODO
- In-memory option
- X-Forwarded-For and X-Real-IP handling
- Better websocket support
- User specific banning
Rule Matching
Single Rule
Use these when you want a rule to apply to one specific endpoint only.
"/api/users/me": (20, 60, "fixed")
This applies only to requests where the normalized path is exactly:
/api/users/me
Nothing else matches.
Not /api/users/me/profile, not /api/users/me/123, not /api/users.
Prefix Wildcards
A rule ending with /* applies to all sub-paths under a given prefix, as one shared rate-limit bucket.
"/api/*": (100, 60, "moving")
This matches:
/api
/api/
/api/users
/api/users/123
/api/anything/here/nested
How Rule applies
Rules are normalized and sorted so that:
- Exact matches come before wildcard matches.
- Longer prefixes take priority over shorter prefixes (so
/api/users/*overrides/api/*) - A request may match multiple rules, if so, ALL matching rules run, and the strictest one determines whether the request is allowed.
- Bans will double with each offense, up to the configured maximum ban length.
Installation
pip install fastapi-easylimiter
Usage
from fastapi import FastAPI
import redis.asyncio as redis
from middleware.rate import RateLimitMiddleware
app = FastAPI()
redis_client = redis.from_url("redis://localhost:6379/0")
app.add_middleware(
RateLimitMiddleware,
redis=redis,
rules={
"/*": (200, 60, "moving"),
"/api/*": (10, 1, "moving"),
"/api/auth/*": (3, 1, "fixed"),
"/api/users/me": (1, 5, "fixed"),
},
exempt=[],
ban_offenses=15,
ban_length="3m",
ban_max_length="30m",
ban_counter_ttl="1h",
site_ban=True,
)
Example:
/api/auth/loginmatches/api/authand/api. If any rule is exceeded →429returned. If banned →403returned.
Redis Key Patterns
| Key Pattern | Example | Used For |
|---|---|---|
rl:fixe:{hash}:{limit}:{window} |
rl:fixe:a1b2c3d4e5f6a7b8:100:60 |
Fixed-window counter |
rl:movi:{hash}:{limit}:{window}:{window_id} |
rl:movi:a1b2c3d4e5f6a7b8:100:60:12345 |
Moving window per-subwindow counter |
{rl_key}:meta |
rl:fixe:a1b2c3d4e5f6a7b8:100:60:meta |
Stores both: offenses & ban_count for doubling |
ban:{hash} |
ban:a1b2c3d4e5f6a7b8 |
Active ban flag |
Middleware Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
redis |
redis.asyncio.Redis |
Yes | Redis async client |
rules |
Dict[str, Tuple[int, int, str]] |
Yes | Path → (limit, period, strategy) |
exempt |
List[str] |
No | Paths that bypass rate limits |
ban_offenses |
int |
No | Offenses before ban triggers |
ban_length |
str |
No | Initial ban length |
ban_max_length |
str |
No | Maximum exponential ban ceiling |
ban_counter_ttl |
int |
No | TTL for ban metadata (default 3600s) |
site_ban |
bool |
No | Enable site-wide bans or per-endpoint |
Tests
Used Ratelimit Tester for testing rate-limit atomicity. Tested with 10 concurrent connections calling 10k requests each, no sleep timer. More testing in heavier environments is needed.
===== FLOOD TEST RESULTS =====
URL: http://localhost:8000/
Workers: 10
Requests per worker: 10000
Total Requests: 100000
Delay per request: 0.0 sec
--- IP 244.35.63.217 ---
200: 200
403: 9786
429: 14
Other: 0
ERR: 0
Latency avg: 7.29 ms
Latency min: 2 ms
Latency max: 3152 ms
--- IP 26.72.199.16 ---
200: 200
403: 9786
429: 14
Other: 0
ERR: 0
Latency avg: 7.20 ms
Latency min: 2 ms
Latency max: 2842 ms
--- IP 103.19.7.208 ---
200: 200
403: 9786
429: 14
Other: 0
ERR: 0
Latency avg: 7.11 ms
Latency min: 3 ms
Latency max: 2515 ms
--- IP 219.61.231.164 ---
200: 200
403: 9786
429: 14
Other: 0
ERR: 0
Latency avg: 7.19 ms
Latency min: 2 ms
Latency max: 2246 ms
--- IP 67.190.167.172 ---
200: 200
403: 9786
429: 14
Other: 0
ERR: 0
Latency avg: 7.16 ms
Latency min: 2 ms
Latency max: 1905 ms
--- IP 92.47.52.135 ---
200: 200
403: 9786
429: 14
Other: 0
ERR: 0
Latency avg: 7.08 ms
Latency min: 2 ms
Latency max: 1635 ms
--- IP 86.33.165.103 ---
200: 200
403: 9786
429: 14
Other: 0
ERR: 0
Latency avg: 7.07 ms
Latency min: 2 ms
Latency max: 1316 ms
--- IP 201.252.232.237 ---
200: 200
403: 9786
429: 14
Other: 0
ERR: 0
Latency avg: 7.05 ms
Latency min: 2 ms
Latency max: 947 ms
--- IP 153.64.165.188 ---
200: 200
403: 9786
429: 14
Other: 0
ERR: 0
Latency avg: 7.01 ms
Latency min: 2 ms
Latency max: 653 ms
--- IP 109.49.11.6 ---
200: 200
403: 9786
429: 14
Other: 0
ERR: 0
Latency avg: 6.95 ms
Latency min: 2 ms
Latency max: 401 ms
Limitations
- Requires Redis; in-memory backend not yet implemented.
- Limited WebSocket support.
- No built-in handling for X-Forwarded-For and X-Real-IP headers.
- Tested in light environments; may need optimization for very high traffic.
- Bans are IP-based; no user-specific banning yet.
Screenshot
Contributing
Contributions and forks are always welcome! Adapt, improve, or extend for your own needs.
Metadata
Release files for fastapi-easylimiter 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastapi_easylimiter-0.5.0.tar.gz | 13.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_easylimiter-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 25.4 kB
Release files / fastapi_easylimiter-0.5.0.tar.gz
| Download URL | fastapi_easylimiter-0.5.0.tar.gz |
|---|---|
| Size | 13.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6b999a1cfd4c5ccfb1e2e617796eb91f3617bb87824629a02e3a33ae8b66edec
|
|
BLAKE2b-256 checksum How to use checksums |
fce9e35475df2e6f41d01f73c7baa25612331daef44e4361b820ba09ba174ade
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.1
|
Release files / fastapi_easylimiter-0.5.0-py3-none-any.whl
| Download URL | fastapi_easylimiter-0.5.0-py3-none-any.whl |
|---|---|
| Size | 11.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a6d254ff57b67f3b88b81df06262520af72ab5b5bf11bee81ec8a9a55c91bfcf
|
|
BLAKE2b-256 checksum How to use checksums |
a8ddeba855beb0dd720e8c36598cff4b4612d4b9119a2925daa283f18756579f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.1
|