A small, flexible rate-limiting library for Python: token bucket, fixed/sliding window, leaky bucket. Sync + async. In-memory + Redis.
Project description
limitkit
A small, flexible rate-limiting library for Python.
- 4 algorithms: token bucket, fixed window, sliding window, leaky bucket
- 2 storage backends: in-memory (default) and Redis (for cross-process / distributed limiting)
- Per-key limiting: one limiter instance handles per-user / per-IP / per-API-key buckets
- Three API styles: direct check, context manager, decorator
- Sync and async: every API works in both worlds
Install
pip install limitkit # in-memory only
pip install limitkit[redis] # adds the Redis backend
60-second tour
from limitkit import TokenBucket
# 5 req/sec sustained, burst tolerance of 5
limiter = TokenBucket(capacity=5, refill_rate=5)
# direct check
if limiter.allow("user:alice"):
serve_request()
# context manager (raises RateLimitExceeded when over the limit)
with limiter:
serve_request()
# decorator
@limiter
def search(query):
...
Algorithms
All four classes share the same surface (.allow(), with, @, async with, async @). Pick by the behavior you want:
| Class | Behavior | When to use |
|---|---|---|
TokenBucket(capacity, refill_rate) |
Allows bursts up to capacity; refills at refill_rate per second. |
Most APIs. "Up to 100 req with sustained 10 req/sec." |
FixedWindow(limit, window) |
limit requests allowed per window seconds; resets at window boundaries. |
Simple counters: "max 1000 calls per hour." Cheap. |
SlidingWindow(limit, window) |
Same limit but no boundary burst — the window slides with the request log. |
When fixed-window boundary bursts matter. |
LeakyBucket(capacity, leak_rate) |
Smooths output to a constant rate; queues up to capacity units. |
Enforcing a maximum sustained outflow. |
Per-key limiting
Pass a key to .allow() and one instance handles many independent buckets:
limiter = TokenBucket(capacity=10, refill_rate=2)
limiter.allow("user:alice") # alice's bucket
limiter.allow("user:bob") # bob's bucket — independent
limiter.allow("ip:192.0.2.1") # any string works
limiter.allow() # uses key="default"
Async support
Every algorithm works in async code:
import asyncio
from limitkit import TokenBucket, RateLimitExceeded
limiter = TokenBucket(capacity=5, refill_rate=5)
@limiter
async def fetch(url):
...
async def main():
async with limiter:
await fetch("...")
try:
await fetch("...")
except RateLimitExceeded:
...
asyncio.run(main())
Redis backend (cross-process / distributed)
In-memory limiters protect a single process. With multiple workers behind a load balancer you need shared state — pass a redis_url:
from limitkit import TokenBucket
limiter = TokenBucket(
capacity=100,
refill_rate=10,
redis_url="redis://localhost:6379",
)
limiter.allow("user:alice") # state lives in Redis; all workers see the same counter
Under the hood every check is a single Lua script call, so updates are atomic — no races between workers. Keys auto-expire after the bucket is fully refilled so abandoned keys don't accumulate.
Heavy / weighted requests
Use n= to mark a call as costing more than 1:
limiter.allow("user:alice", n=5) # consumes 5 tokens instead of 1
Catching the limit
The decorator and context manager raise RateLimitExceeded:
from limitkit import RateLimitExceeded
try:
with limiter:
...
except RateLimitExceeded:
return 429
.allow() returns bool instead — pick whichever style fits your code.
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 limitkit-0.1.0.tar.gz.
File metadata
- Download URL: limitkit-0.1.0.tar.gz
- Upload date:
- Size: 10.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95fb9c7b371ca75817c8fc87603fdcbbfe6a0527e167ebd34b60591d4023e7d8
|
|
| MD5 |
dfe99f08e08e0c2a1628104ab425fb54
|
|
| BLAKE2b-256 |
ba73aaaeb84863d2091440b4b57d74dbb0185d0a1131a981029e3ee1f5367aa8
|
File details
Details for the file limitkit-0.1.0-py3-none-any.whl.
File metadata
- Download URL: limitkit-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c5efa3d373e46bb2b6436ddd660c6922afbb82a29b0b3bb20b333a1fd9efc4bf
|
|
| MD5 |
557efbf84f663e28ccbd4677ccb826a4
|
|
| BLAKE2b-256 |
1bef41e834a724d9c60ca0831913fc6d6b492c2d9a205a1de0aa2748df3ca844
|