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
Metadata
Release files for limitkit 0.1.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 | |
|---|---|---|---|---|
| limitkit-0.1.0.tar.gz | 10.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| limitkit-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 19.3 kB
Release files / limitkit-0.1.0.tar.gz
| Download URL | limitkit-0.1.0.tar.gz |
|---|---|
| Size | 10.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
95fb9c7b371ca75817c8fc87603fdcbbfe6a0527e167ebd34b60591d4023e7d8
|
|
BLAKE2b-256 checksum How to use checksums |
ba73aaaeb84863d2091440b4b57d74dbb0185d0a1131a981029e3ee1f5367aa8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / limitkit-0.1.0-py3-none-any.whl
| Download URL | limitkit-0.1.0-py3-none-any.whl |
|---|---|
| Size | 9.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c5efa3d373e46bb2b6436ddd660c6922afbb82a29b0b3bb20b333a1fd9efc4bf
|
|
BLAKE2b-256 checksum How to use checksums |
1bef41e834a724d9c60ca0831913fc6d6b492c2d9a205a1de0aa2748df3ca844
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Security for limitkit 0.1.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.
To learn more about protecting yourself from malicious actors, visit our package security documentation.