Rate limiting for FastAPI with dual (IP/user) buckets.
Project description
fastlimit
Rate limiting for FastAPI with clean route signatures, IP/user-aware buckets, pluggable backends, and automatic rate limit headers.
from fastapi import FastAPI
from fastlimit import FastLimit, rate_limit
app = FastAPI()
limiter = FastLimit(
redis_url="redis://localhost:6379", # omit for the in-memory backend
user_id_func=lambda request: getattr(request.state, "user_id", None),
)
limiter.init_app(app)
@app.post("/upload", dependencies=[rate_limit(ip="10/min", user="50/min")])
async def upload():
return {"ok": True}
No Request parameter is required in your route handler, and your handler can
keep returning normal FastAPI values such as dictionaries, Pydantic models, or
responses.
Why fastlimit?
Many FastAPI rate limiters make rate limiting visible in every route signature or only give you one simple bucket per endpoint. fastlimit is built for APIs that need anonymous and authenticated traffic to be treated differently without leaking that plumbing into application code.
With rate_limit(ip="10/min", user="50/min"), anonymous requests are limited by
client IP and authenticated requests are limited by the user ID returned from
user_id_func.
Features
- FastAPI dependency API:
dependencies=[rate_limit("10/min")] - Decorator API:
@limit("10/min") - No forced
Requestargument in route functions - IP and authenticated-user buckets
- In-memory backend for development and tests
- Redis backend for multi-worker production deployments
- Sliding window, fixed window, and token bucket algorithms
- Automatic
X-RateLimit-*andRetry-Afterheaders - Custom header names and custom 429 responses
- Request cost support for expensive endpoints
- Dry-run mode for observing limits before enforcement
- Exempt IPs and trusted proxy support
- Typed package (
py.typed)
Installation
pip install fastlimit
For Redis-backed limits:
pip install "fastlimit[redis]"
With uv:
uv add fastlimit
uv add "fastlimit[redis]"
Quickstart
Create and register one limiter for your FastAPI app:
from fastapi import FastAPI
from fastlimit import FastLimit
app = FastAPI()
limiter = FastLimit(
# Use Redis in production. Omit redis_url for the in-memory backend.
redis_url="redis://localhost:6379",
# Return a stable user ID for authenticated requests, or None for anonymous.
user_id_func=lambda request: getattr(request.state, "user_id", None),
)
limiter.init_app(app)
Add limits to routes:
from fastapi import APIRouter
from fastlimit import rate_limit
router = APIRouter()
@router.post("/register", dependencies=[rate_limit("10/min")])
async def register():
return {"ok": True}
@router.post("/upload", dependencies=[rate_limit(ip="5/min", user="50/min")])
async def upload():
return {"ok": True}
The shorthand rate_limit("10/min") is equivalent to
rate_limit(ip="10/min").
Decorator style
If you prefer decorators:
from fastlimit import limit
@router.get("/feed")
@limit("100/min")
async def feed():
return {"items": []}
The decorator injects a hidden FastAPI dependency, so the route function still
does not need a Request parameter.
Rate strings
Rate strings use the format limit/window:
rate_limit("5/s")
rate_limit("10/min")
rate_limit("100/hour")
rate_limit("1000/day")
rate_limit("3/5min")
Supported units include seconds, minutes, hours, and days, with common aliases
such as s, sec, min, hour, and day.
Backends
fastlimit ships with two backends:
MemoryBackend, used by default, stores counters in the current Python process. It is useful for development, tests, and single-process apps.RedisBackend, enabled withredis_url, shares counters across workers and should be used for multi-process or production deployments.
from fastlimit import FastLimit
limiter = FastLimit() # in-memory
limiter = FastLimit(redis_url="redis://localhost:6379") # Redis
Custom backends can be passed with FastLimit(backend=...) by implementing the
fastlimit.backends.Backend protocol.
Algorithms
Choose the algorithm when creating the limiter:
from fastlimit import Algorithm, FastLimit
FastLimit(algorithm=Algorithm.SLIDING_WINDOW) # default
FastLimit(algorithm=Algorithm.FIXED_WINDOW)
FastLimit(algorithm=Algorithm.TOKEN_BUCKET)
SLIDING_WINDOW: best default for most APIs; smooth rolling windows.FIXED_WINDOW: simple counters with fixed reset boundaries.TOKEN_BUCKET: allows short bursts while maintaining a long-term rate.
Response headers
Allowed requests include:
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1781736212
Blocked requests return HTTP 429 and include:
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1781736212
Retry-After: 41
Header names can be customized with HeaderConfig, or disabled entirely.
Advanced options
from fastlimit import FastLimit, HeaderConfig
limiter = FastLimit(
redis_url="redis://localhost:6379",
key_prefix="my_api",
headers=HeaderConfig(
limit="RateLimit-Limit",
remaining="RateLimit-Remaining",
reset="RateLimit-Reset",
),
exempt_ips={"127.0.0.1"},
trusted_proxies=1,
dry_run=False,
)
Individual routes can also set a request cost:
@router.post("/export", dependencies=[rate_limit("100/hour", cost=10)])
async def export():
...
Documentation
- Documentation: https://fastlimit.muizzyranking.me
- Repository: https://github.com/muizzyranking/fastlimit
- Issues: https://github.com/muizzyranking/fastlimit/issues
Development
This project uses Python 3.11+ and uv.
uv sync --all-extras --dev
uv run pytest
uv run ruff check src tests
uv run mypy
Build the package:
uv build
Serve the docs locally:
uv run mkdocs serve
License
MIT
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 fastlimit-0.1.1.tar.gz.
File metadata
- Download URL: fastlimit-0.1.1.tar.gz
- Upload date:
- Size: 15.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55c92defc0d271dba5be5b8892ca7409cb4de43e4670b7dab99f90c026e395ac
|
|
| MD5 |
2983bddaec3a39b4bb1cd410da9cf599
|
|
| BLAKE2b-256 |
f121d45865d12d154c8ce9dd021e0d3b84d42e7ff3a0427b57dc01e24719d584
|
Provenance
The following attestation bundles were made for fastlimit-0.1.1.tar.gz:
Publisher:
publish.yml on Muizzyranking/fastlimit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastlimit-0.1.1.tar.gz -
Subject digest:
55c92defc0d271dba5be5b8892ca7409cb4de43e4670b7dab99f90c026e395ac - Sigstore transparency entry: 1858096364
- Sigstore integration time:
-
Permalink:
Muizzyranking/fastlimit@f04c13f1ed8bb9d4992967134a2506722dc27ff3 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Muizzyranking
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f04c13f1ed8bb9d4992967134a2506722dc27ff3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastlimit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: fastlimit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 21.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa71b5463413a96c0391b1279402e47b04a0a7536d879b8eb1d915a9309d7311
|
|
| MD5 |
4cb9a41016a0e085950c45a27f5f8b82
|
|
| BLAKE2b-256 |
8d7e12ed7b64ec195816e97ab6142b198e7567cf2378e4ee9dc5182f16062bd7
|
Provenance
The following attestation bundles were made for fastlimit-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on Muizzyranking/fastlimit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastlimit-0.1.1-py3-none-any.whl -
Subject digest:
fa71b5463413a96c0391b1279402e47b04a0a7536d879b8eb1d915a9309d7311 - Sigstore transparency entry: 1858096440
- Sigstore integration time:
-
Permalink:
Muizzyranking/fastlimit@f04c13f1ed8bb9d4992967134a2506722dc27ff3 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Muizzyranking
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f04c13f1ed8bb9d4992967134a2506722dc27ff3 -
Trigger Event:
push
-
Statement type: