sliding window rate limiter for FastAPI
Project description
,---. ,--. ,--.
/ .-' ,--,--. ,---.,-' '-. ,--,--. ,---. `--'
| `-,' ,-. |( .-''-. .-'' ,-. || .-. |,--.
| .-'\ '-' |.-' `) | | \ '-' || '-' '| |
`--' `--`--'`----' `--' `--`--'| |-' `--'
`--'
,--. ,--.,--. ,--. ,--.
,--.--. ,--,--.,-' '-. ,---. | |`--',--,--,--.`--',-' '-.
| .--'' ,-. |'-. .-'| .-. : | |,--.| |,--.'-. .-'
| | \ '-' | | | \ --. | || || | | || | | |
`--' `--`--' `--' `----' `--'`--'`--`--`--'`--' `--'
📦 installation
pip install fastapi-sliding-window
📑 quick start
from fastapi import FastAPI, Depends
from fastapi_sliding_window import RateLimit
app = FastAPI()
@app.get("/login", dependencies=[Depends(RateLimit(requests=5, window_seconds=60))])
async def login():
return {"status": "ok"}
🧩 features
- 🎯 3 algorithms — Sliding Window Log (exact), Sliding Window Counter (O(1) memory), Fixed Window
- 💾 in-memory — no Redis required, zero external dependencies
- 🔧 two usage styles —
Depends(RateLimit(...))per-route orRateLimitMiddlewareglobally - 📝 standard headers —
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset,Retry-After - ✅ fully typed —
py.typedmarker included - 🚀 async-native — built on
asyncio.Lockfor thread safety
📖 usage
Depends (per-route)
from fastapi import Depends
from fastapi_sliding_window import RateLimit, Algorithm
# Sliding Window Log (default, most accurate)
@app.get("/api/data", dependencies=[
Depends(RateLimit(requests=100, window_seconds=60))
])
async def get_data():
return {"data": "..."}
# Fixed Window (fastest, O(1) memory)
@app.post("/api/upload", dependencies=[
Depends(RateLimit(requests=10, window_seconds=60, algorithm=Algorithm.FIXED_WINDOW))
])
async def upload():
return {"status": "uploaded"}
# custom key function (per-user instead of per-IP)
async def user_key(request):
return request.headers.get("X-User-ID", "anonymous")
@app.get("/api/profile", dependencies=[
Depends(RateLimit(requests=50, window_seconds=60, key_func=user_key))
])
async def profile():
return {"user": "..."}
Middleware (global)
from fastapi_sliding_window import RateLimitMiddleware, Algorithm
app.add_middleware(
RateLimitMiddleware,
requests=100,
window_seconds=60.0,
algorithm=Algorithm.SLIDING_WINDOW_LOG,
exclude_paths=["/health"], # skip rate limiting for these paths
)
📊 algorithms
| Algorithm | Accuracy | Memory | Speed | Best for |
|---|---|---|---|---|
| Sliding Window Log | 100% | O(requests/window) | O(1) amortized | accuracy-critical endpoints |
| Sliding Window Counter | ~99% | O(1) per key | O(1) | high-traffic APIs |
| Fixed Window | ~50% (boundary burst) | O(1) per key | O(1) | simple quotas |
Sliding Window Log
Stores a timestamp for every request. Prunes entries outside the window on each check. Perfectly accurate, memory grows linearly with request volume within the window.
Sliding Window Counter
Maintains two counters (current and previous window) and computes a weighted average. Near-accurate with constant memory. Best balance of accuracy and performance.
Fixed Window
Simple counter per time bucket. Suffers from boundary burst (clients can send 2x the limit across a window boundary).
📝 HTTP headers
successful responses include:
| Header | Description |
|---|---|
X-RateLimit-Limit |
maximum requests allowed |
X-RateLimit-Remaining |
requests remaining in window |
X-RateLimit-Reset |
unix timestamp when window resets |
Retry-After |
seconds until next request is allowed (429, middleware only) |
🔗 API Reference
RateLimit(requests, window_seconds, algorithm, key_func, include_headers)
FastAPI dependency for per-route rate limiting.
| Parameter | Type | Default | Description |
|---|---|---|---|
requests |
int |
required | max requests per window |
window_seconds |
float |
required | window duration in seconds |
algorithm |
Algorithm |
SLIDING_WINDOW_LOG |
rate limiting algorithm |
key_func |
KeyFunc |
IP-based | function to extract client key |
include_headers |
bool |
True |
add rate limit headers |
RateLimitMiddleware(requests, window_seconds, algorithm, key_func, include_headers, exclude_paths)
Starlette middleware for global rate limiting.
| Parameter | Type | Default | Description |
|---|---|---|---|
requests |
int |
100 |
max requests per window |
window_seconds |
float |
60.0 |
window duration in seconds |
algorithm |
Algorithm |
SLIDING_WINDOW_LOG |
rate limiting algorithm |
key_func |
KeyFunc |
IP-based | function to extract client key |
include_headers |
bool |
True |
add rate limit headers |
exclude_paths |
list[str] |
None |
paths to skip rate limiting |
📜 license
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_sliding_window-0.1.0.tar.gz.
File metadata
- Download URL: fastapi_sliding_window-0.1.0.tar.gz
- Upload date:
- Size: 13.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8edff10de493c9bf4c942a66f2f6ba5b0d87d6253c62b05d09fec1360bd42d6
|
|
| MD5 |
cd4ea679342148789eb4dde0d3be08cc
|
|
| BLAKE2b-256 |
32c52cd336370c73c027a085efc1d14c94d379bb0a71d14706d66b2f8541f774
|
File details
Details for the file fastapi_sliding_window-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_sliding_window-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cb2a8bdbd9da26e1b88b495a14354c50277a2ce395c7b3e65c1108da64873ff
|
|
| MD5 |
b1d609bb899c635b9d0b8d50c07791ca
|
|
| BLAKE2b-256 |
1a692fd99d411d591c0c1f4ef7fa0121167d99e3f5d1cee04d73d077da250485
|