philiprehberger-rate-limiter
In-memory rate limiter with sliding window, token bucket, and leaky bucket algorithms.
Installation
pip install philiprehberger-rate-limiter
Usage
from philiprehberger_rate_limiter import RateLimiter, Algorithm
limiter = RateLimiter(
requests=100,
window_seconds=60,
algorithm=Algorithm.SLIDING_WINDOW,
)
if limiter.allow("user-123"):
handle_request()
Check Status
from philiprehberger_rate_limiter import RateLimiter
limiter = RateLimiter(100, 60)
status = limiter.status("user-123")
print(f"Allowed: {status.allowed}")
print(f"Remaining: {status.remaining}/{status.limit}")
print(f"Resets at: {status.reset_at}")
Usage Statistics
from philiprehberger_rate_limiter import RateLimiter
limiter = RateLimiter(100, 60)
limiter.allow("user-123")
stats = limiter.get_stats("user-123")
print(f"Current usage: {stats.current_usage}")
print(f"Remaining: {stats.remaining}")
print(f"Reset at: {stats.reset_at}")
Querying budget without consuming
from philiprehberger_rate_limiter import RateLimiter, Algorithm
import time
limiter = RateLimiter(100, 60, Algorithm.SLIDING_WINDOW)
for _ in range(15):
limiter.allow("user-123")
# How many requests still allowed (does not consume a request)
print(limiter.remaining("user-123")) # 85
# Monotonic timestamp when capacity for one more request is available
reset = limiter.reset_at("user-123")
print(f"Available in {max(0.0, reset - time.monotonic()):.1f}s")
Format status
from philiprehberger_rate_limiter import RateLimiter
limiter = RateLimiter(100, 60)
for _ in range(15):
limiter.allow("user:123")
print(limiter.format_status("user:123"))
# "15/100 requests used (85 remaining); resets in 23.5s"
Blocking Wait
from philiprehberger_rate_limiter import RateLimiter
limiter = RateLimiter(100, 60)
# Synchronous — blocks until quota is available or timeout expires
status = limiter.wait("user-123", timeout=5.0)
# Async — awaits until quota is available
status = await limiter.async_acquire("user-123")
Async Context Manager
from philiprehberger_rate_limiter import RateLimiter
limiter = RateLimiter(100, 60)
async with limiter:
status = await limiter.async_acquire("user-123")
if status.allowed:
await handle_request()
Decorator
from philiprehberger_rate_limiter import RateLimiter, rate_limit
# Standalone decorator
@rate_limit(calls=10, period=60)
def api_endpoint():
return {"data": "ok"}
# Instance-based decorator
limiter = RateLimiter(10, 60)
@limiter.limit("10/minute")
def another_endpoint():
return {"data": "ok"}
# Async function decorator
@rate_limit(calls=10, period=60)
async def async_endpoint():
return {"data": "ok"}
Rate Limiter Groups
from philiprehberger_rate_limiter import RateLimiter, RateLimiterGroup
limiter = RateLimiter(100, 60)
group = RateLimiterGroup(limiter, ["api-key-1", "api-key-2", "api-key-3"])
# All keys in the group share one rate limit pool
group.allow("api-key-1") # consumes from shared pool
group.allow("api-key-2") # consumes from same pool
stats = group.get_stats()
print(f"Group usage: {stats.current_usage}/{stats.limit}")
Algorithms
from philiprehberger_rate_limiter import RateLimiter, Algorithm
# Fixed window — resets at interval boundaries
RateLimiter(100, 60, Algorithm.FIXED_WINDOW)
# Sliding window (default) — rolling time window
RateLimiter(100, 60, Algorithm.SLIDING_WINDOW)
# Token bucket — smooth rate with burst capacity
RateLimiter(100, 60, Algorithm.TOKEN_BUCKET)
# Leaky bucket — constant drain rate, smooths bursts
RateLimiter(100, 60, Algorithm.LEAKY_BUCKET)
API
| Function / Class | Description |
|---|---|
RateLimiter(requests, window_seconds, algorithm) |
Create a rate limiter |
limiter.allow(key) |
Check if request is allowed |
limiter.status(key) |
Get detailed LimitStatus |
limiter.get_stats(key) |
Get RateLimiterStats without consuming a request |
limiter.remaining(key) |
Get the number of requests still allowed for key without consuming |
limiter.reset_at(key) |
Monotonic timestamp when capacity for one more request is available |
limiter.format_status(key) |
Get a human-readable single-line summary (e.g. "15/100 requests used (85 remaining); resets in 23.5s") |
limiter.wait(key, timeout) |
Block until quota available or raise RateLimitExceeded |
limiter.async_acquire(key) |
Async wait until quota is available |
limiter.reset(key) |
Reset state for a key |
limiter.reset_all() |
Reset state for all keys |
limiter.active_keys() |
List all keys with active state |
limiter.limit(rate) |
Decorator with rate string (e.g., "10/minute") |
RateLimiterGroup(limiter, keys) |
Create a shared rate limit group |
group.allow(key) |
Check if request is allowed against shared pool |
group.status(key) |
Get shared LimitStatus |
group.get_stats() |
Get shared RateLimiterStats |
group.reset() |
Reset shared group state |
rate_limit(calls, period, algorithm) |
Standalone decorator for rate limiting |
format_status(status) |
Format a LimitStatus as a human-readable summary string |
Algorithm |
Enum: FIXED_WINDOW, SLIDING_WINDOW, TOKEN_BUCKET, LEAKY_BUCKET |
LimitStatus |
Dataclass: allowed, remaining, reset_at, limit |
RateLimiterStats |
Dataclass: current_usage, remaining, reset_at, limit |
RateLimitExceeded |
Exception raised when rate limit is exceeded |
Development
pip install -e .
python -m pytest tests/ -v
Support
If you find this project useful:
License
Metadata
Release files for philiprehberger-rate-limiter 0.6.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 | Provenance | |
|---|---|---|---|---|
| philiprehberger_rate_limiter-0.6.0.tar.gz | 195.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| philiprehberger_rate_limiter-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 204.8 kB
Release files / philiprehberger_rate_limiter-0.6.0.tar.gz
| Download URL | philiprehberger_rate_limiter-0.6.0.tar.gz |
|---|---|
| Size | 195.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6f43e1af13abc8db9a037bff0002c62ce589576fb3b9c29bd495a63a5dcf1088
|
|
BLAKE2b-256 checksum How to use checksums |
a6dad4d7492cea2d16d41dd62ba772b85b9bab36522f8973a72f4c2e26483b52
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / philiprehberger_rate_limiter-0.6.0-py3-none-any.whl
| Download URL | philiprehberger_rate_limiter-0.6.0-py3-none-any.whl |
|---|---|
| Size | 9.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2c709229b994bfe0c8c74006894a0dbba9e659b87441257e527b8c458b719317
|
|
BLAKE2b-256 checksum How to use checksums |
5af3fd211e985e258b6cb3db0ae010c7301bc26287913b157ab6a2946a1dcd73
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Security for philiprehberger-rate-limiter 0.6.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.