Skip to main content

rate-sync

rate-sync

PyPI version Python 3.12+ License: MIT Tests

Distributed rate limiting for Python with Redis, PostgreSQL, or in-memory backends.


What It Solves

Brute force and credential stuffing on your auth endpoints

Attackers try thousands of password combinations. A sliding window limiter on your login endpoint stops them — and composite limiting blocks both single-IP attacks and distributed botnets hitting the same account.

[limiters.auth_credential]
store = "prod-cache"
algorithm = "sliding_window"
limit = 5                # 5 attempts
window_seconds = 300     # per 5 minutes

Deep dive: Authentication Protection · Abuse Prevention

Different API quotas for free, pro, and enterprise customers

Your free tier gets 100 requests/hour. Pro gets 1,000. Enterprise gets 10,000. Define each tier as a limiter and key it per user. rate-sync handles the rest.

limiter = await get_or_clone_limiter(f"api_{tier}", api_key)

Deep dive: API Tiering

One tenant consuming all your platform's resources

In multi-tenant systems, a single noisy tenant can starve everyone else. Per-tenant rate limiting enforces fair usage — each tenant gets their share, and no one can monopolize your infrastructure.

# Each tenant gets their own enforced limit, on one shared connection
limiter = await get_keyed_limiter("platform_api", tenant_id)
await limiter.acquire()

Deep dive: Multi-Tenant Fairness

Webhook floods taking down your customers' endpoints

Your platform sends webhooks to customer URLs. A bulk import triggers 10,000 events, and suddenly you're DDoS-ing your own customers. Per-endpoint rate limiting with automatic retries keeps delivery smooth without overwhelming anyone.

limiter = await get_or_clone_limiter("webhook_endpoint", endpoint_url)
async with limiter.acquire_context(timeout=30.0):
    await http_client.post(endpoint_url, json=payload)

Deep dive: Webhook Delivery

File uploads and heavy operations eating all your resources

Five concurrent 1GB uploads can exhaust server memory. PDF generation can peg every CPU core. Concurrency limiting caps how many heavy operations run simultaneously — completely separate from request rate.

[limiters.upload]
store = "prod-cache"
max_concurrent = 10          # max 10 uploads at once
timeout = 300.0

Deep dive: File Uploads & Heavy Resources

Background workers overwhelming third-party APIs

You have 20 Celery workers calling the Stripe API, which allows 100 req/s. Without coordination, your workers exceed the limit and get throttled. rate-sync coordinates across all workers through a shared Redis backend.

[limiters.stripe_api]
store = "prod-cache"
rate_per_second = 90.0   # stay under Stripe's 100/s limit
max_concurrent = 10       # max 10 in-flight calls

Deep dive: Background Jobs

Rate limits that actually work across multiple servers

In-memory counters reset when a process restarts and can't coordinate across instances. rate-sync uses Redis or PostgreSQL as a shared backend, so limits are enforced consistently across your entire fleet.

[stores.prod-cache]
engine = "redis"
url = "${REDIS_URL}"

[limiters.api]
store = "prod-cache"     # all instances share this
rate_per_second = 100.0

Deep dive: Production Deployment

Knowing what's being blocked and why

Rate limiting without observability is flying blind. rate-sync exposes built-in metrics (acquisitions, wait times, timeouts) and integrates with Prometheus for dashboards and alerting.

state = await limiter.get_state()
# → LimiterState(allowed=True, remaining=42, reset_at=1706367600)

Deep dive: Observability · Monitoring Patterns


Features

  • Declarative configuration - Define limits in TOML, use anywhere
  • Multiple backends - Redis (recommended), PostgreSQL, or memory
  • Dual limiting - Rate limiting (req/sec) + concurrency limiting (max parallel)
  • Two algorithms - Token bucket for throughput, sliding window for quotas
  • FastAPI integration - Dependencies, middleware, exception handlers
  • Async-first - Built on asyncio with full type hints

Installation

pip install rate-sync             # Memory backend only
pip install rate-sync[redis]      # + Redis support
pip install rate-sync[postgres]   # + PostgreSQL support
pip install rate-sync[fastapi]    # + FastAPI integration
pip install rate-sync[all]        # All backends + integrations

Quick Start

1. Create rate-sync.toml:

[stores.main]
engine = "memory"  # or "redis", "postgres"

[limiters.api]
store = "main"
rate_per_second = 10.0

2. Use it:

from ratesync import acquire

await acquire("api")  # Blocks until rate limit allows

That's it. Configuration auto-loads on import.

Usage Patterns

async with acquire("api"):
    response = await client.get(url)

Decorator

from ratesync import rate_limited

@rate_limited("api")
async def fetch_data():
    return await client.get(url)

Use template strings — placeholders are resolved at call time:

@rate_limited("api:{user_id}")
async def fetch_user_data(user_id: str):
    return await client.get(url)

@rate_limited("api:{tenant_id}:{user_id}")
async def multi_tenant_api(tenant_id: str, user_id: str):
    return await client.post(url)

Per-User Limits (Manual)

For advanced cases where you need direct limiter control, use get_keyed_limiter. The identifier is only a key on the limiter's single connection, so the number of Redis or PostgreSQL connections does not grow with the number of users, client addresses or tenants:

from ratesync import get_keyed_limiter

limiter = await get_keyed_limiter("api", user_id)
async with limiter.acquire_context():
    response = await client.get(url)

get_or_clone_limiter("api", user_id) returns the same thing for the supported engines. Do not call clone_limiter with a value your callers control: it registers a limiter, and on Redis or PostgreSQL a connection pool, per value, for the life of the process. Keep clone_limiter for a small fixed set of named variants (api and api-premium).

Backends

Memory (development)

[stores.local]
engine = "memory"

Redis (production)

[stores.prod-cache]
engine = "redis"
url = "redis://localhost:6379/0"

PostgreSQL

[stores.db]
engine = "postgres"
url = "postgresql://user:pass@localhost/mydb"

Algorithms

Token Bucket (default)

Controls request throughput with optional concurrency limits:

[limiters.external_api]
store = "prod-cache"
rate_per_second = 100.0  # Max 100 req/sec
max_concurrent = 10      # Max 10 in-flight requests
timeout = 30.0           # Wait up to 30s for a slot

Sliding Window

Counts requests in a time window. Ideal for login protection:

[limiters.login]
store = "prod-cache"
algorithm = "sliding_window"
limit = 5              # Max 5 attempts
window_seconds = 300   # Per 5 minutes

FastAPI Integration

Requires: pip install rate-sync[fastapi]

from fastapi import Depends, FastAPI
from ratesync.contrib.fastapi import (
    RateLimitDependency,
    RateLimitExceededError,
    rate_limit_exception_handler,
)

app = FastAPI()
app.add_exception_handler(RateLimitExceededError, rate_limit_exception_handler)

@app.get("/api/data")
async def get_data(_: None = Depends(RateLimitDependency("api"))):
    return {"status": "ok"}

Programmatic Configuration

Skip the TOML file if you prefer code:

from ratesync import configure_store, configure_limiter, acquire

configure_store("main", strategy="redis", url="redis://localhost:6379/0")
configure_limiter("api", store_id="main", rate_per_second=100.0)

await acquire("api")

Documentation

Topic Link
Configuration Reference docs/configuration.md
API Reference docs/api-reference.md
FastAPI Integration docs/fastapi-integration.md
Redis Setup docs/setup/redis-setup.md
PostgreSQL Setup docs/setup/postgres-setup.md
Observability docs/observability.md

Patterns

Contributing

git clone https://github.com/rate-sync/python.git
cd python
poetry install
poetry run pytest

See CONTRIBUTING.md for guidelines.

License

MIT

Metadata

Release files for rate-sync 0.5.0

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

Source distribution (sdist)

Source distribution for rate-sync 0.5.0
File Size Uploaded Provenance Actions
rate_sync-0.5.0.tar.gz 90.8 kB With provenance Details for rate_sync-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for rate-sync 0.5.0
File Interpreter ABI Platform Actions
rate_sync-0.5.0-py3-none-any.whl Python 3 none any Details for rate_sync-0.5.0-py3-none-any.whl

Total release size: 200.3 kB

Release files / rate_sync-0.5.0.tar.gz

Download URL rate_sync-0.5.0.tar.gz
Size 90.8 kB
Tags Source
SHA-256 checksum
How to use checksums
798dc95c17dd748cb04cd3f15a0bee707955266df2ed6da19dfb3a75ce59b1ab
BLAKE2b-256 checksum
How to use checksums
ab1d08e4c66adc6666f5523f8eba235d6ff5f3277a62095997f5ca92b4b440ce
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / rate_sync-0.5.0-py3-none-any.whl

Download URL rate_sync-0.5.0-py3-none-any.whl
Size 109.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6d028ac5466c1595fbadc3d22903ee85724d4c2f430f598158de20b8ec0bea95
BLAKE2b-256 checksum
How to use checksums
8e371a9e35f640ac4ae3508a2b9ad914a5b6ebc7ad793554b7d1ce329290183d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Security for rate-sync 0.5.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.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for rate_sync-0.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

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