Skip to main content

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 Request argument 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-* and Retry-After headers
  • 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 with redis_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

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastlimit-0.1.1.tar.gz (15.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastlimit-0.1.1-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

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

Hashes for fastlimit-0.1.1.tar.gz
Algorithm Hash digest
SHA256 55c92defc0d271dba5be5b8892ca7409cb4de43e4670b7dab99f90c026e395ac
MD5 2983bddaec3a39b4bb1cd410da9cf599
BLAKE2b-256 f121d45865d12d154c8ce9dd021e0d3b84d42e7ff3a0427b57dc01e24719d584

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastlimit-0.1.1.tar.gz:

Publisher: publish.yml on Muizzyranking/fastlimit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for fastlimit-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fa71b5463413a96c0391b1279402e47b04a0a7536d879b8eb1d915a9309d7311
MD5 4cb9a41016a0e085950c45a27f5f8b82
BLAKE2b-256 8d7e12ed7b64ec195816e97ab6142b198e7567cf2378e4ee9dc5182f16062bd7

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastlimit-0.1.1-py3-none-any.whl:

Publisher: publish.yml on Muizzyranking/fastlimit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page