Skip to main content

Production-grade rate limiting, retry, and circuit breaking library

Project description

APIGuard

Production-grade rate limiting, retry, and circuit breaking library for Python.

Features

  • Token Bucket Rate Limiting: Thread-safe rate limiting with configurable tokens and refill rate
  • Retry with Backoff: Exponential backoff with jitter and Retry-After header support
  • Circuit Breaker: Protects against cascading failures with CLOSED/OPEN/HALF_OPEN states
  • Async Support: First-class async/await support via httpx adapter
  • Registry: Per-user/per-resource rate limit tracking
  • Structured Logging: JSON-structured log events for observability

Installation

pip install GRID-APIGUARD

Quick Start

Basic Rate Limiting

from apiguard import TokenBucket

bucket = TokenBucket(capacity=100, refill_rate=10.0)

if bucket.acquire(tokens=5):
    # Make API call
    pass
else:
    # Wait or reject request
    pass

Circuit Breaker

from apiguard import CircuitBreaker, CircuitOpenError

breaker = CircuitBreaker(
    failure_threshold=5,
    recovery_timeout=60.0,
)

try:
    with breaker:
        result = make_api_call()
except CircuitOpenError:
    # Circuit is open, fail fast
    handle_failure()

Retry Handler

from apiguard import RetryHandler, RetryExhaustedError
import httpx

retry = RetryHandler(
    max_retries=3,
    base_delay=1.0,
    max_delay=60.0,
    retryable_status_codes={429, 500, 502, 503, 504},
)

async def fetch_with_retry(url: str) -> httpx.Response:
    async for attempt in retry.attempts():
        response = await httpx.get(url)
        if response.status_code in retry.retryable_status_codes:
            retry.apply_backoff(attempt, response)
            continue
        return response
    raise RetryExhaustedError("All retries exhausted")

RateLimitedClient (Composition)

from apiguard import RateLimitedClient, TokenBucket, RetryHandler, CircuitBreaker

client = RateLimitedClient(
    bucket=TokenBucket(capacity=100, refill_rate=10.0),
    retry=RetryHandler(max_retries=3),
    breaker=CircuitBreaker(failure_threshold=5),
)

# Use as context manager
with client:
    response = client.request("GET", "https://api.example.com/data")

Per-User Rate Limiting with Registry

from apiguard import BucketRegistry

registry = BucketRegistry(default_capacity=100, default_refill_rate=10.0)

# Get or create bucket for specific user
user_bucket = registry.get_bucket("user-123")

Async HTTP Client

from apiguard.adapters.httpx import AsyncRateLimitedClient

async with AsyncRateLimitedClient(
    bucket=TokenBucket(capacity=100, refill_rate=10.0),
) as client:
    response = await client.get("https://api.example.com/data")

API Reference

TokenBucket

  • TokenBucket(capacity: int, refill_rate: float) - Create a bucket with capacity tokens, refilling at refill_rate tokens/second
  • acquire(tokens: int = 1) -> bool - Try to acquire tokens, returns True if successful
  • available() -> float - Current token count
  • __enter__ / __exit__ - Context manager support (no-op, for composition)

CircuitBreaker

  • CircuitBreaker(failure_threshold: int, recovery_timeout: float, success_threshold: int = 1) - Configure breaker
  • States: CLOSED, OPEN, HALF_OPEN
  • __enter__ - Raises CircuitOpenError if circuit is OPEN
  • __exit__(exc_type, exc_val, exc_tb) - Records success/failure and updates state

RetryHandler

  • RetryHandler(max_retries: int, base_delay: float = 1.0, max_delay: float = 60.0, jitter: float = 0.1, retryable_status_codes: set[int] | None = None)
  • attempts() -> AsyncIterator[int] - Async generator yielding attempt numbers
  • apply_backoff(attempt: int, response: httpx.Response | None = None) -> float - Apply backoff with optional Retry-After header

RateLimitedClient

  • RateLimitedClient(bucket: TokenBucket, retry: RetryHandler | None = None, breaker: CircuitBreaker | None = None)
  • Composes rate limiting, retry, and circuit breaking
  • Thread-safe and reusable

BucketRegistry

  • BucketRegistry(default_capacity: int, default_refill_rate: float)
  • get_bucket(key: str) -> TokenBucket - Get or create bucket for key
  • remove_bucket(key: str) -> bool - Remove bucket for key

Exceptions

  • CircuitOpenError - Raised when circuit is OPEN
  • RetryExhaustedError - Raised when all retries are exhausted

Development

Setup

python3 -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"

Testing

pytest                       # run all 60 tests
pytest --cov=apiguard        # with coverage
pytest tests/test_bucket.py  # single module

Linting & Type Checking

ruff check .                 # lint
ruff format .                # format
mypy apiguard                # strict type checking

Project Layout

apiguard/
  bucket.py       Token bucket rate limiter (thread-safe)
  circuit.py      Circuit breaker (CLOSED → OPEN → HALF_OPEN)
  client.py       Composed client (bucket + retry + breaker)
  retry.py        Exponential backoff with jitter & Retry-After
  registry.py     Per-key bucket registry
  exceptions.py   CircuitOpenError, RetryExhaustedError
  logging.py      Structured JSON log events
  adapters/
    httpx.py      Async httpx integration
tests/            Mirrors apiguard/ — one test file per module

Requires Python ≥ 3.11. Build system: Hatchling.

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

grid_apiguard-0.1.0.tar.gz (17.3 kB view details)

Uploaded Source

Built Distribution

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

grid_apiguard-0.1.0-py3-none-any.whl (14.1 kB view details)

Uploaded Python 3

File details

Details for the file grid_apiguard-0.1.0.tar.gz.

File metadata

  • Download URL: grid_apiguard-0.1.0.tar.gz
  • Upload date:
  • Size: 17.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for grid_apiguard-0.1.0.tar.gz
Algorithm Hash digest
SHA256 618c46a7c6672916c61bd1dd23c7fe3596d4b346893db6b2c71cb2a6c4572967
MD5 7b17e0c6124d0d095abb1bae3b61ff92
BLAKE2b-256 0552bc7088b657b8160af8dd333ee749895188398af2fcfba4ee69cc36f5e25e

See more details on using hashes here.

File details

Details for the file grid_apiguard-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: grid_apiguard-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for grid_apiguard-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 49f8e3a35796ce82edb6eaa307ffca9e0b6e27958987c1d7c41a892e32bd0d71
MD5 4f82ec53c44d4edee36c71e8a89bae3b
BLAKE2b-256 99db7f0857f328c212216b906339f7b144105f7fef88e8022ab53deec8d31719

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