Skip to main content

llm-governor

Client-side rate limiting for the Claude API. Stop before the server has to say no.

This library never sees your API key. It wraps calls you make with your own client, and reads only anthropic-ratelimit-* response headers. No credentials are required, requested, logged, or stored.

pip install llm-governor

Why

Claude's Messages API enforces three independent limits per model: requests per minute (RPM), input tokens per minute (ITPM), and output tokens per minute (OTPM). Blow any one of them and you get a 429.

Most retry-on-429 code reacts after the failure. llm-governor mirrors the server's own token bucket locally, corrects itself from the rate-limit headers on every response, and paces your requests so the 429 never happens.

It also understands cached input. On most Claude models, cache_read_input_tokens do not count toward ITPM. A naive limiter that counts every input token will throttle you to a fraction of your real throughput; this one tracks your rolling cache-hit rate and reserves only what actually counts.

Quick start

import anthropic
from llm_governor import Governor

client = anthropic.Anthropic()          # your key, your client
gov = Governor(model="claude-sonnet-5") # knows nothing about keys

with gov.slot(messages=messages, max_tokens=1024) as slot:
    resp = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        messages=messages,
    )
    slot.observe(resp)   # feeds headers + usage back in

The first call runs against conservative defaults. From the second call onward, the governor knows your real limits because the API told it.

Async

gov = Governor(model="claude-sonnet-5", max_concurrency=8)

async def worker(messages):
    async with gov.aslot(messages=messages, max_tokens=1024) as slot:
        resp = await client.messages.create(...)
        slot.observe(resp)
        return resp

await asyncio.gather(*(worker(m) for m in batch))

Live dashboard

pip install "llm-governor[dashboard]"
llm-governor demo          # simulated workload, no API key needed
from llm_governor.dashboard import Dashboard

with Dashboard(gov):
    run_my_batch()
╭─ llm-governor · claude-sonnet-5 ─────────────────────╮
│  RPM   ████████████████░░░░   812 / 1000             │
│  ITPM  ███████░░░░░░░░░░░░░   1.4M / 2.0M            │
│  OTPM  ██████████████████░░   361K / 400K            │
│                                                      │
│  cache  78.0%                        94 req/min      │
│  flight     7                        231 done        │
│  429s       0                        3.2s waited     │
╰────────────────────────────── live ──────────────────╯

Bars turn amber below 25% headroom and red below 10%.

Dry run

See whether a workload would throttle, without waiting for it:

gov = Governor(model="claude-sonnet-5", dry_run=True)
# ... replay your workload ...
print(gov.snapshot()["throttled_seconds"])

Options

Option Default Meaning
model "claude-sonnet-5" Limits are per-model; use one governor per model
rpm / itpm / otpm conservative Starting guesses, overridden by headers
headroom 0.90 Fraction of the real limit to use; leaves margin for bursts
max_concurrency None Cap simultaneous in-flight requests
dry_run False Compute waits but never sleep

On credentials

The header allowlist in llm_governor.headers.ALLOWED_HEADERS is explicit and tested. scrub() is the only path by which headers enter this library, and a test asserts that x-api-key and authorization never survive it. If you extend this library, do not replace that with dict(response.headers).

Notes and limits

  • State is per-process. For multiple workers sharing one org limit, divide headroom by your worker count, or wait for the planned Redis backend.
  • Input estimation is character-based and approximate. Pass estimated_input explicitly if you have a real count from the token-counting endpoint.
  • Batch API requests have separate limits and are not covered.
  • max_tokens is reserved pessimistically and refunded once the real output size is known.

License

MIT

Metadata

Release files for llm-rate-governor 0.1.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 llm-rate-governor 0.1.0
File Size Uploaded
llm_rate_governor-0.1.0.tar.gz 14.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for llm-rate-governor 0.1.0
File Interpreter ABI Platform
llm_rate_governor-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.6 kB

Release files / llm_rate_governor-0.1.0.tar.gz

Download URL llm_rate_governor-0.1.0.tar.gz
Size 14.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b937583b51b0a78b01278f939ccb2b09a8f3620a4889bc2fa6e4189b93238574
BLAKE2b-256 checksum
How to use checksums
bf3b56d06d24b6e5f3948c7922c83f9fced4066891bb648824f75e695186030a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / llm_rate_governor-0.1.0-py3-none-any.whl

Download URL llm_rate_governor-0.1.0-py3-none-any.whl
Size 16.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c5749bca8216fb09b78accdcb13a9ff85ef3f3c7fd5e03fa142c6e9fdac0a4b5
BLAKE2b-256 checksum
How to use checksums
30d6c6942a67995438d5e48418b27cd39e5ab18f43fc95f554baf432b3c5ddec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

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