๐Ÿฆ€ Rust Core ๐Ÿ Python API v0.1.5 ๐Ÿ“ฆ PyPI

Rate limiting that
never leaves the process

Drop-in rate limiter for FastAPI, Django, Flask and any Python backend.
A couple of atomic operations per request โ€” no Redis, no network hop. The hot path runs in Rust and releases the GIL.

$ pip install rust-py-rate-limit
python
>>> from rust_py_rate_limit import RateLimiter
>>> limiter = RateLimiter(limit=10, window_seconds=60)
>>> limiter.allow("user:123")
True
>>> limiter.stats()
{'allowed': 1, 'blocked': 0, 'total_checks': 1, 'active_keys': 1}
>>> โ–ˆ
38Tests passing
Fixed WindowAlgorithm
GIL-freeHot path
0Runtime deps

Everything a rate limiter needs

Rust handles the counting and concurrency. Python stays simple.

โšก

Fixed Window

Up to limit requests per window_seconds per key. Predictable, cheap, and easy to reason about โ€” the counter resets when the window rolls over.

๐Ÿ”‘

Any Key You Want

Rate-limit by IP, user id, API key, route โ€” anything you can express as a string. allow("ip:1.2.3.4") and you're done.

๐Ÿงต

Thread-safe & Lock-free

State lives in a sharded DashMap; stats use AtomicU64. No global lock on the critical path โ€” different keys never contend.

๐Ÿš€

FastAPI Middleware

Add RateLimitMiddleware in one line. Sets X-RateLimit-* and Retry-After headers, returns 429 when over the limit.

๐ŸŽธ

Django Middleware

Drop RateLimitMiddleware into MIDDLEWARE and configure it from settings.py. Limit by IP or authenticated user.

๐ŸŽ€

Decorator

Wrap any callable with @limiter.limit("login"). Raises RateLimitExceeded โ€” with a dynamic key option for per-argument budgets.

๐Ÿ“Š

Built-in Stats

stats() returns allowed, blocked, total_checks and active_keys โ€” ready for dashboards and alerts.

๐Ÿฆ€

Rust Core via PyO3

The counting logic compiles to a native .so extension. No Rust toolchain needed to use it โ€” just pip install.

๐Ÿงน

Lazy Cleanup

cleanup_expired() reclaims memory from keys whose window has passed โ€” call it on a timer or leave it to grow with your key space.

Works with your stack

One middleware line, a decorator, or call the core API directly.

Python
from rust_py_rate_limit import RateLimiter

limiter = RateLimiter(limit=3, window_seconds=60)

limiter.allow("ip:127.0.0.1")   # True
limiter.allow("ip:127.0.0.1")   # True
limiter.allow("ip:127.0.0.1")   # True
limiter.allow("ip:127.0.0.1")   # False โ€” limit reached

limiter.check("ip:127.0.0.1")
# {"allowed": False, "limit": 3, "remaining": 0,
#  "reset_after_seconds": 42, "retry_after_seconds": 42}

limiter.remaining("ip:127.0.0.1")  # 0
limiter.reset("ip:127.0.0.1")      # True
Python
from fastapi import FastAPI
from rust_py_rate_limit.fastapi import RateLimitMiddleware

app = FastAPI()

# 429 + X-RateLimit-* headers, automatically
app.add_middleware(
    RateLimitMiddleware,
    limit=100,
    window_seconds=60,
    key_func=lambda request: request.client.host,
)

@app.get("/api/users")
def list_users():
    return {"users": []}
Python
# settings.py
MIDDLEWARE = [
    "rust_py_rate_limit.django.RateLimitMiddleware",
    # ... rest of your middleware
]

RUST_PY_RATE_LIMIT = {
    "LIMIT": 100,
    "WINDOW_SECONDS": 60,
    "KEY": "ip",   # "ip" or "user"
}
Python
from rust_py_rate_limit import RateLimiter, RateLimitExceeded

limiter = RateLimiter(limit=5, window_seconds=60)

@limiter.limit("login")
def login():
    return "ok"

# Dynamic key derived from the function arguments
@limiter.limit(lambda user_id: f"user:{user_id}")
def fetch(user_id):
    return user_id

# Raises RateLimitExceeded once the limit is exceeded

How Fixed Window works

Each key gets a counter and a window. Cross the limit and you're blocked until it rolls over.

request 1โ€“3
count: 1 โ†’ 3
allowed โœ“
โ†’
request 4 (over limit)
count: 3 / 3
blocked โœ•
โ†’
after 60s
window reset
allowed โœ“

The API

  • allow(key) consume โ†’ bool
  • check(key) consume โ†’ dict
  • remaining(key) peek, no consume
  • reset(key) drop one key
  • clear() drop everything
  • cleanup_expired() reclaim memory

Under the hood

  • DashMap sharded, per-key locks
  • AtomicU64 lock-free stats
  • allow_threads releases the GIL
check() โ€” allowed
{
  "allowed": True,
  "limit": 100,
  "remaining": 99,
  "reset_after_seconds": 60,
  "retry_after_seconds": 0
}
check() โ€” blocked
{
  "allowed": False,
  "limit": 100,
  "remaining": 0,
  "reset_after_seconds": 42,
  "retry_after_seconds": 42
}

How it fits together

Rust counts and stores. Python integrates. You stay fast.

Your App
FastAPI Django Flask Worker
โ†’
Middleware / API
RateLimitMiddleware @limiter.limit() limiter.allow()
โ†’
๐Ÿฆ€ Rust Core
fixed_window.rs rate_limiter.rs stats.rs (AtomicU64) DashMap
โ†’
Output
allow โ†’ bool check โ†’ dict stats โ†’ dict

The native .so extension is compiled once at publish time via maturin + PyO3.
Your users just pip install โ€” no Rust toolchain required. State is per-process: behind multiple workers each worker keeps its own counters.

Ready in seconds

Install from PyPI. No Rust, no compilers, no configuration.

Basic

pip install rust-py-rate-limit

Quick check

python -c "import rust_py_rate_limit"

From source

maturin develop

Run the tests

cargo test && pytest
Requires Python 3.10+ ยท Linux ยท macOS ยท Windows ยท MIT License
Copied!