Skip to main content

A strict asyncio rate limiter with serialized execution and sliding-window guarantees

Project description

async-rate-limiter

This library provides a RateLimiter with serialized, strict sliding-window execution guarantees.

Overview

async-rate-limiter is a strict, completion-based asyncio rate limiter with serialized execution and sliding-window guarantees.

It is designed for correctness, determinism, and safety rather than raw throughput. The limiter executes tasks sequentially and guarantees that no more than a fixed number of task completions occur within a given time window, making it suitable for protecting external APIs, databases, and other fragile systems.

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 this asyncio rate limiter 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.

Guarantees

  • Serialized execution (no concurrent callbacks)
  • Completion-based rate limiting
  • No more than rate task completions occur in any per interval(e.g. at most 1000 completions per 2 seconds)
  • FIFO ordering

API

  • RateLimiter(rate: int, per_ns: int) — allow rate task completions per per interval.
    • rate : Maximum number of task completions allowed per interval
    • per_ns : Unit time interval in nano seconds in which at max 'rate' are allowed to complete
  • await push(task: Callable) -> bool — execute the task, if bandwidth permits, else queue it for future execution
    • task: Callable[[], None | Awaitable[None]]
      • May be a synchronous function or an async coroutine
      • Takes no arguments
      • Returns nothing

Example

import asyncio
from async_rate_limiter import RateLimiter

async def work():
    print('did work')

async def main():
    rate_limiter = RateLimiter(10, 1000_000_000)
    for _ in range(50):
        await rate_limiter.push(work)

asyncio.run(main())

Important semantic note: completion-based rate limiting

This is not a token-bucket or admission-based limiter.

The RateLimiter enforces:

At most rate task completions per per interval, with serialized execution.

This guarantee holds regardless of task duration or scheduling delays. This means:

  • Tasks are awaited
  • Long-running 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

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 Python asyncio rate limiters

Library Model Bursts Concurrency
async-rate-limiter Serialized, completion-based No No
aiolimiter Token bucket Yes Yes

Design Philosophy

  • Prefer explicit semantics over implicit behavior
  • Favor determinism over throughput
  • Make edge cases testable, not accidental

When to use this asyncio rate limiter

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:


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

Alternatively, this repository includes build_package.py; you can run it if you prefer (it wraps standard build steps):

python build_package.py

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

Platform-specific helper scripts are provided:

  • Windows: run_tests.bat
  • Unix/macOS: run_tests.sh

Notes & Troubleshooting

  • The utilities depend only on Python's standard library (asyncio, datetime, etc.). Tests use unittest.IsolatedAsyncioTestCase which 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/oversubscribed CI runners.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

async_serialized_rate_limiter-0.2.0.tar.gz (9.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file async_serialized_rate_limiter-0.2.0.tar.gz.

File metadata

File hashes

Hashes for async_serialized_rate_limiter-0.2.0.tar.gz
Algorithm Hash digest
SHA256 8b35e87fe3b27b57a08094eeb00eba6083224990c05adf941704cc6bdcf9c6b7
MD5 0ca8db1f5774c05ec0f844dfa3bb1e65
BLAKE2b-256 b56f6f8dd5e94d072859ccdcdffe84aa1da743d034d5c1c67cf86d1c7bb5626c

See more details on using hashes here.

File details

Details for the file async_serialized_rate_limiter-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for async_serialized_rate_limiter-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f98938e81db46d72f662e844b5b45bd5efce53125f285aa3338644be95c870a8
MD5 dbccae2aadc0be9ff4018d616f560b34
BLAKE2b-256 092fbdca8691a38fc2e96ce5e126693edbbf67ad0e77bdc3cb66ddd25414ab36

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page