async-rate-limiter
This library provides a RateLimiter with serialized, strict sliding-window execution guarantees.
Overview
This library provides an interface to serially execute tasks, in accordance with strict sliding-window semantics. It is intentionally conservative and explicit, making it suitable for protecting external systems (APIs, databases, hardware) where contract violation and ambiguity are unacceptable.
async-rate-limiter takes a different approach:
Behavior is explicit, testable, and deterministic --- even if that costs some throughput.
Installation
pip install async_rate_limiter
What it does
Enforces a maximum number of task completions per time window. Tasks
pushed to the limiter are executed as bandwidth becomes available. Tasks
may be synchronous functions or async coroutines. Internally uses a
small ring buffer of recent task completion timestamps and a pending
queue. When the buffer is full, execution is deferred until the earliest
timestamp ages out by per.
In addition to task-count-based limiting, the library also provides a WeightedRateLimiter, which enforces a maximum total weight per time window instead of a maximum number of tasks.
This is useful when tasks represent different amounts of work or resource usage (for example, network bytes sent, database rows written, or request cost units), and throttling must be based on total cost rather than task count.
Guarantees
- Serialized execution (no concurrent callbacks)
- Completion-based rate limiting
- No more than
ratetask completions occur in anyperinterval (e.g. at most 1000 completions per 2 seconds) - FIFO ordering
For WeightedRateLimiter, the same guarantees apply, except:
- Rate limiting is based on total weight instead of number of tasks
- No more than
allowed_weighttotal weight is consumed in anyperinterval - Tasks are still executed strictly in FIFO order
API
RateLimiter(rate: int, per_ns: int)--- allowratetask completions perperinterval.- rate : Maximum number of task completions allowed per interval
- per_ns : Unit time interval in nanoseconds in which at max
rateare allowed to complete
await push(task: Callable) -> bool- Executes the task immediately if bandwidth permits, otherwise queues it.
Returns True if the task was executed immediately, False if it was queued - Params
- task:
Callable[[], None | Awaitable[None]]- May be a synchronous function or an async coroutine
- Takes no arguments
- Returns nothing
- task:
- Executes the task immediately if bandwidth permits, otherwise queues it.
Weighted API
WeightedRateLimiter(allowed_weight: int, per_ns: int)--- allow total weightallowed_weightperperinterval.- allowed_weight : Maximum total weight allowed per interval
- per_ns : Unit time interval in nanoseconds
await push(task: Callable, estimated_weight: int) -> bool- Executes the task immediately if bandwidth permits, otherwise queues it.
Returns True if the task was executed immediately, False if it was queued - Params
- task:
Callable[[], None | Awaitable[None]]- May be a synchronous function or an async coroutine
- Takes no arguments
- Returns nothing
- estimated_weight:
- Must be
> 0and<= allowed_weight - Represents the estimated resource cost of the task
- Must be
- task:
- Executes the task immediately if bandwidth permits, otherwise queues it.
Example
import asyncio
from async_rate_limiter import RateLimiter
async def work():
print('did work')
async def main():
rate_limiter = RateLimiter(10, 1_000_000_000)
for _ in range(50):
await rate_limiter.push(work)
asyncio.run(main())
Weighted Example
import asyncio
from async_rate_limiter import WeightedRateLimiter
async def send_packet(size):
print(f"sending {size} bytes")
def make_task(size):
async def task():
await send_packet(size)
return task
async def main():
# Allow 1000 bytes per second
limiter = WeightedRateLimiter(allowed_weight=1000, per_ns=1_000_000_000)
packets = [200, 400, 700, 300]
for size in packets:
await limiter.push(make_task(size), estimated_weight=size)
asyncio.run(main())
In this example, execution is throttled based on total bytes sent per second rather than number of tasks.
Important Semantic Note
This is not a token-bucket or admission-based limiter.
The RateLimiter enforces:
At most
ratetask completions perperinterval, with serialized execution.
The same completion-based semantics apply to WeightedRateLimiter:
At most
allowed_weighttotal weight of task completions perperinterval, with serialized execution.
This means: - Tasks are awaited - Weight is charged only after task completion - Long-running or heavy tasks reduce throughput - No task executes concurrently with another
This design favors correctness and predictability over raw throughput.
Suitable Use Cases
- Protecting external APIs
- Throttling database writes
- IO-bound pipelines
- Any system where bursts are harmful
Additional use cases for WeightedRateLimiter:
- Network bandwidth throttling (bytes sent per second)
- API cost-unit quotas
- Database write amplification control
- Any system where tasks have non-uniform cost
What this RateLimiter is NOT
- Not a token bucket
- Not burst-tolerant
- Not admission-based
- Not suitable for CPU-bound parallel work
If you need those semantics, consider aiolimiter.
Comparison with Other Libraries
Library Model Bursts Concurrency Cost-Based
async-rate-limiter Sliding window, No No No (RateLimiter) completion-based
async-rate-limiter Sliding window, No No Yes (WeightedRateLimiter) completion-based
aiolimiter Token bucket Yes Yes No
Design Philosophy
- Prefer explicit semantics over implicit behavior
- Favor determinism over throughput
- Make edge cases testable, not accidental
When to Use This Library
Use async-rate-limiter if: - You care about correctness and predictability - You want to reason about timing behavior precisely - You are protecting fragile downstream systems
Do not use it if: - You need maximum throughput - You rely on bursts - You want parallel execution
Working Examples
For concrete examples showing actual working code using these classes,
see the example code in the examples/ directory:
examples/BasicRateLimiterExample.py--- basicRateLimiterusageexamples/WeightedRateLimiterExample.py--- basicWeightedRateLimiterusage
Build Package from Source
The project uses pyproject.toml. To build distribution archives
install build and run:
pip install --upgrade build
python -m build
The above produces a dist/ folder with .whl and .tar.gz files. You
can also install locally with:
pip install . # install from source
pip install -e . # editable install for development
Run Tests
The tests are in the tests/ directory and use unittest's async test
support. You can run them with unittest or with pytest.
Run with unittest (cross-platform):
python -m unittest discover -v
Run with pytest (if installed):
pip install pytest
pytest -q
Notes & Troubleshooting
- The utilities depend only on Python's standard library (
asyncio,time, etc.). Tests useunittest.IsolatedAsyncioTestCasewhich requires Python 3.8+. - If you see timing-sensitive failures, they may be due to scheduling resolution on the host system --- increase sleep durations in tests when diagnosing on slow or oversubscribed CI runners.
License
MIT
Metadata
Release files for async-serialized-rate-limiter 0.2.2
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 | |
|---|---|---|---|---|
| async_serialized_rate_limiter-0.2.2.tar.gz | 10.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| async_serialized_rate_limiter-0.2.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 20.5 kB
Release files / async_serialized_rate_limiter-0.2.2.tar.gz
| Download URL | async_serialized_rate_limiter-0.2.2.tar.gz |
|---|---|
| Size | 10.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
93ecbcb1f73d78762432a707d20dcc90124826bf46f3b63a0d08cb1ba1ca2c0d
|
|
BLAKE2b-256 checksum How to use checksums |
6db47f7d438b23c0c4bc4f0330d4efbe8d1a715c5e00e935079797c4482c0f33
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.2
|
Release files / async_serialized_rate_limiter-0.2.2-py3-none-any.whl
| Download URL | async_serialized_rate_limiter-0.2.2-py3-none-any.whl |
|---|---|
| Size | 10.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d6b0f7b5901f6250c90ac424b23b7413d32ebaa391909eefc43350aef43ccf91
|
|
BLAKE2b-256 checksum How to use checksums |
ca0f03a08b64f19dd7ca95f325e292a6760052af47445f56857db0ab8f4e9334
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.2
|
Security for async-serialized-rate-limiter 0.2.2
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.