Skip to main content

PyCurb – A Framework‑Agnostic Rate Limiter for Python

CI License: Apache 2.0 Documentation Status

PyCurb is a flexible, and easy‑to‑use rate‑limiting library for Python. It is desgined to be framework‑agnostic, i.e, used with FastAPI, Flask, Django, or even in plain scripts and CLI tools. It supports multiple algorithms, redis & in-memory storage backends, and has advanced features like composite (multi‑tier) limits, and dynamic rule updates.

Algorithms

PyCurb implements five industry‑standard rate‑limiting algorithms, each suited for different traffic patterns. Choose the one that best fits your use case.

1. Sliding Window

The sliding window algorithm maintains a queue of request timestamps for each key. The window slides continuously; old timestamps (older than now - window) are discarded. At any moment, the number of requests in the window is the length of the queue.

  • Use case: APIs where you need strict, real‑time limiting without the burstiness of fixed windows.
  • Parameters: limit (max requests), window (duration in seconds).
  • Behaviour: The window moves with every request; no abrupt resets.

Example
Limit: 100 requests per minute.
At t=10s, a request arrives; its timestamp is stored. At t=75s, the window covers [15s, 75s]; any request older than 15s is dropped. The count is the number of timestamps in that range.

count = |{ t ∈ timestamps : t > now - window }|
Allowed if count < limit.

2. Fixed Window

Fixed window divides time into consecutive, non‑overlapping windows of length window. All requests within the same window are counted together. The counter resets at the start of each new window.

  • Use case: Simple, memory‑efficient limiting for non‑critical APIs.
  • Parameters: limit (max requests per window), window (duration in seconds).
  • Behaviour: The counter resets abruptly at window boundaries.

Example
Limit: 100 requests per minute, windows start at 0s, 60s, 120s, …
At t=10s, the counter is in window [0,60). At t=75s, a new window [60,120) begins and the counter resets to 0.

window_start = floor(now / window) * window
counter is stored for the current window_start.
Allowed if counter < limit.

3. Token Bucket

The token bucket algorithm maintains a bucket of tokens that refills at a constant rate. Each request consumes one token. If the bucket has at least one token, the request is allowed; otherwise, it is denied.

  • Use case: APIs that require a steady average rate with the ability to handle short bursts.
  • Parameters: capacity (burst size), refill_rate (tokens per second).
  • Behaviour: Allows bursts up to capacity; long‑term average is refill_rate.

Example
Capacity = 10, refill rate = 2 tokens/sec.
Initially, the bucket has 10 tokens. A client can send 10 requests immediately (burst). After that, the bucket refills at 2 tokens/sec, so the client can sustain 2 requests per second over time.

tokens = min(capacity, tokens + (now - last_refill) * refill_rate)
Allowed if tokens >= 1; then tokens -= 1.

4. Leaky Bucket

The leaky bucket algorithm models a queue that holds pending requests. Requests leak out of the queue at a constant rate (leak_rate). New requests are accepted only if the queue has free capacity.

  • Use case: Smoothing bursty traffic to a constant output rate (e.g., for database writes or external API calls).
  • Parameters: capacity (max queue size), leak_rate (requests per second).
  • Behaviour: Smooths bursts; the output rate is constant.

Example
Capacity = 5, leak rate = 1 request/sec.
If 5 requests arrive at once, they fill the queue. They are then processed at 1 request/sec. A new request arriving while the queue is full is dropped.

leaked = floor((now - last_leak) * leak_rate)
queue = max(0, queue - leaked)
Allowed if queue < capacity; then queue += 1.

5. GCRA – Generic Cell Rate Algorithm

GCRA (also known as the leaky bucket with burst control) uses a single state variable – the Theoretical Arrival Time (TAT) – to enforce both a rate and a burst size. It does not require a queue or background processes.

  • Use case: High‑precision rate limiting where both burst and average rate must be controlled.
  • Parameters: capacity (burst size), refill_rate (requests per second).
  • Behaviour: Similar to token bucket but implemented with a single timestamp, making it extremely efficient.

Example
Capacity = 10, rate = 5 req/sec → interval T = 0.2s, burst interval τ = capacity * T = 2s.
The algorithm allows a request if TAT ≤ now + τ. After an allowed request, TAT = max(TAT, now) + T. This ensures that the average rate is 5 req/sec and the burst is at most 10 requests.

T = 1 / refill_rate
τ = (capacity - 1) * T (using capacity-1 to match the burst size exactly)
Allowed if TAT ≤ now + τ.
If allowed: TAT = max(TAT, now) + T.

Comparison Table

Algorithm Burst Tolerance Memory Usage Best For
Sliding Window Up to limit Medium (timestamps) Precise window limits
Fixed Window Up to limit Low (counter) Simple, low‑overhead limiting
Token Bucket Configurable Low (2 floats) Burst‑tolerant APIs
Leaky Bucket None (smooths) Low (2 floats) Constant‑rate processing
GCRA Configurable Low (1 float) High‑precision, efficient limiting

Choose the algorithm that aligns with your traffic pattern and resource constraints. All algorithms are fully configurable via the LimitRule model.


Get Started

Installation

Install the core package:

pip install pycurb

For redis as a storage backend, install:

pip install pycurb[redis]

For framework adapters (i.e Flask, Django, FastAPI), install the extras:

pip install pycurb[fastapi]

To install everything:

pip install pycurb[all]

Basic Usage (Async)

import asyncio
import time
from pycurb.core import AsyncRateLimiter, AsyncMemoryStorage, LimitRule
  1. Define a rule: 5 requests per 10 seconds
rule = LimitRule(
    name="api",
    algorithm="sliding_window",
    limit=5,
    window=10,
)
  1. Set up storage and limiter
storage = AsyncMemoryStorage()
limiter = AsyncRateLimiter(storage, [rule])
  1. Use it in your async function
async def fetch_data(user_id: str):
result = await limiter.check(user_id, "api")
if result.allowed:
    print(f"Allowed – remaining: {result.remaining}")
else:
    print(f"Denied – retry after: {result.retry_after}")

asyncio.run(fetch_data("user-123"))

Basic Usage (Sync)

from pycurb.core import RateLimiter, MemoryStorage, LimitRule

storage = MemoryStorage()
limiter = RateLimiter(storage, [rule])

def handle_request(user_id: str):
    result = limiter.check(user_id, "api")
    if result.allowed:
        print("Request allowed")
    else:
        print(f"Rate limited. Retry after {result.retry_after} seconds.")

Using the Decorator

The @rate_limit decorator wraps any sync or async function

from pycurb.core import rate_limit

@rate_limit(limiter, limit_str="5/10s", key_extractor=lambda user_id: str(user_id))
async def my_function(user_id: str):
    return "OK"

Framework Adapters

Pycurb provides plug-and-play adapters for popular python frameworks

  • FastAPI: Use the RateLimitMiddleware class or rate_limiter dependency
  • Django: Use the rate_limit decorator or the create_rate_limit_middleware factory
  • Flask: Use the rate_limit decorator or the RateLimit extension

Metadata

Release files for pycurb 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pycurb 0.2.1
File Size Uploaded Provenance Actions
pycurb-0.2.1.tar.gz 32.3 kB Without provenance Details for pycurb-0.2.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pycurb 0.2.1
File Interpreter ABI Platform Actions
pycurb-0.2.1-py3-none-any.whl Python 3 none any Details for pycurb-0.2.1-py3-none-any.whl

Total release size: 79.1 kB

Release files / pycurb-0.2.1.tar.gz

Download URL pycurb-0.2.1.tar.gz
Size 32.3 kB
Tags Source
SHA-256 checksum
How to use checksums
bbde482df3f0bf2ac8424c2a4bf0e832ec51950ca87f1cc9508bc0f117854712
BLAKE2b-256 checksum
How to use checksums
ad3c091c511b81ff47b0865798a27a5dd77dcc4d5fca080a5874b9fc34a1919a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / pycurb-0.2.1-py3-none-any.whl

Download URL pycurb-0.2.1-py3-none-any.whl
Size 46.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e1b718ba68f1fb244f61912fca4ac45ea306650271e8a7472fff1b1ba7b60735
BLAKE2b-256 checksum
How to use checksums
7c5c5c742741f4de2b2d6c2f364a77e0a305003a5165cd873f2225c4e9968fc0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Security for pycurb 0.2.1

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page