smartratelimit
Your API's rate limits, read from its own headers and respected automatically โ no 429s, no hand-rolled sleep loops
What is smartratelimit?¶
Every API tells you its rate limit on every response โ X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset โ and almost no client reads them. So you either guess a time.sleep(), or you find out the hard way with a 429.
smartratelimit reads those headers for you. Swap requests.get(url) for limiter.request('GET', url) and the limiter learns the limit from the first response, refills a token bucket at exactly that rate, and blocks the calling thread just long enough when you're about to run out. Nothing to configure โ most APIs are handled by the headers they already send.
When you need more than one process to share those limits โ Gunicorn workers, Celery tasks, a fleet of scrapers โ point it at SQLite or Redis and the token bucket becomes shared state.
Start here: 60-second quickstart ยท How it works ยท Which storage backend?
What you get¶
X-RateLimit-*, RateLimit-* and Retry-After headers, with built-in profiles for GitHub, Stripe, Twitter, OpenAI and RapidAPI. Non-standard header names take a three-line map.limit / window per second. Requests are spread across the window instead of sprinting into the wall and stalling.storage= string. Restart your app and it still knows it has 12 requests left on this hour's quota.AsyncRateLimiter paces httpx and aiohttp calls with asyncio.sleep, so waiting on one endpoint never blocks the loop.smartratelimit probe shows what an API advertises, status reads the stored quota, clear resets it โ useful against a shared SQLite or Redis backend.Sixty seconds¶
from smartratelimit import RateLimiter
limiter = RateLimiter()
# The first response teaches the limiter GitHub's limit; every call after
# that is paced to fit inside it.
for user in ["octocat", "torvalds", "gvanrossum"]:
response = limiter.request("GET", f"https://api.github.com/users/{user}")
print(response.json()["name"])
status = limiter.get_status("api.github.com")
print(f"{status.remaining}/{status.limit} left, resets in {status.reset_in:.0f}s")
Nothing above names a rate limit. GitHub's headers did.
When you don't need it¶
If you call an API a handful of times a day, a bare requests.get is fine. smartratelimit earns its place when you're looping over an API โ batch enrichment, scraping, fan-out jobs, sync workers โ where the limit is a real constraint and the failure mode is a 429 partway through a run.
It also assumes the limit belongs to you calling someone else. It is a client-side limiter, not a server-side one; it won't throttle inbound traffic to your own service.
Installation¶
pip install smartratelimit # core, requests only
pip install smartratelimit[redis] # + Redis storage backend
pip install smartratelimit[httpx] # + httpx async client
pip install smartratelimit[aiohttp] # + aiohttp async client
pip install smartratelimit[all] # everything
Python 3.8+. The only hard dependency is requests.