Async transport for httpx to implement various rate limiting (using a centralized redis as backend)
Project description
httpx-rate-limiter-transport
What is it?
This project provides an async transport for httpx to implement various rate limiting (using a centralized redis as backend).
Features
- Global semaphore to limit the number of concurrent requests to all hosts
- Optional second level of semaphore to limit the number of concurrent requests (you can provide your own logic)
- for example: you can limit the number of concurrent requests by host, by HTTP method or only for some given hosts...
- TTL to avoid blocking the semaphore forever (in some special cases like computer crash or network issues at the very wrong moment)
- Can wrap another transport (if you already use one)
Roadmap
- Add a "request per minute" rate limiting
Installation
pip install httpx-rate-limiter-transport
Quickstart
import asyncio
import httpx
from httpx_rate_limiter_transport.backend.adapters.redis import (
RedisRateLimiterBackendAdapter,
)
from httpx_rate_limiter_transport.transport import ConcurrencyRateLimiterTransport
def get_httpx_client() -> httpx.AsyncClient:
transport = ConcurrencyRateLimiterTransport(
global_concurrency=2,
backend_adapter=RedisRateLimiterBackendAdapter(
redis_url="redis://localhost:6379", ttl=300
),
)
return httpx.AsyncClient(transport=transport, timeout=300)
async def request(n: int):
client = get_httpx_client()
async with client:
futures = [client.get("https://www.google.com/") for _ in range(n)]
res = await asyncio.gather(*futures)
for r in res:
print(r.status_code)
if __name__ == "__main__":
asyncio.run(request(10))
How-to
How to get a concurrency limit by host?
To get a "concurrency limit by host", you can provide 2 hooks to define a custom/second level of concurrency limit.
import httpx
from httpx_rate_limiter_transport.backend.adapters.redis import (
RedisRateLimiterBackendAdapter,
)
from httpx_rate_limiter_transport.transport import ConcurrencyRateLimiterTransport
def get_httpx_client() -> httpx.AsyncClient:
transport = ConcurrencyRateLimiterTransport(
global_concurrency=100, # global concurrency limit (for all requests)
backend_adapter=RedisRateLimiterBackendAdapter(
redis_url="redis://localhost:6379", ttl=300
),
get_concurrency_hook=lambda request: 10, # set a second level of concurrency limit of 10
get_key_hook=lambda request: request.url.host, # use the host as key for the second level of concurrency limit
)
return httpx.AsyncClient(transport=transport, timeout=300)
How to get a concurrency limit for only one given host?
To get a concurrency limit only for a given host, you can return None from your custom hooks to deactivate the
concurrency control for this specific request.
import httpx
from httpx_rate_limiter_transport.backend.adapters.redis import (
RedisRateLimiterBackendAdapter,
)
from httpx_rate_limiter_transport.transport import ConcurrencyRateLimiterTransport
def get_key_cb(request: httpx.Request) -> str | None:
host = request.url.host
if host == "www.google.com":
# For google, no concurrency limit
return None
return host
def get_concurrency_cb(request: httpx.Request) -> int | None:
# Let's return a constant concurrency limit of 10
# (but of course, you can build your own logic here)
return 10
def get_httpx_client() -> httpx.AsyncClient:
transport = ConcurrencyRateLimiterTransport(
global_concurrency=None, # No global concurrency limit
backend_adapter=RedisRateLimiterBackendAdapter(
redis_url="redis://localhost:6379", ttl=300
),
get_concurrency_hook=get_concurrency_cb,
get_key_hook=get_key_cb,
)
return httpx.AsyncClient(transport=transport, timeout=300)
How to wrap another httpx transport?
If you already use a specific httpx transport, you can wrap it inside this one.
import httpx
from httpx_rate_limiter_transport.backend.adapters.redis import (
RedisRateLimiterBackendAdapter,
)
from httpx_rate_limiter_transport.transport import ConcurrencyRateLimiterTransport
def get_httpx_client() -> httpx.AsyncClient:
original_transport = httpx.AsyncHTTPTransport(retries=3)
transport = ConcurrencyRateLimiterTransport(
inner_transport=original_transport, # let's wrap the original transport
global_concurrency=10,
backend_adapter=RedisRateLimiterBackendAdapter(
redis_url="redis://localhost:6379", ttl=300
),
)
return httpx.AsyncClient(transport=transport, timeout=300)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file httpx_rate_limiter_transport-0.1.1.tar.gz.
File metadata
- Download URL: httpx_rate_limiter_transport-0.1.1.tar.gz
- Upload date:
- Size: 8.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05e85f7a307b20f742fa20c9004c2e750364c563fbb25e3898f416b7aa307b67
|
|
| MD5 |
5c8c90125ac2b3a343a6755f52bf7b71
|
|
| BLAKE2b-256 |
e3468b8ec3719e37d096d68dd71cc490d45dd329897f44724438d9abed1072d4
|
File details
Details for the file httpx_rate_limiter_transport-0.1.1-py3-none-any.whl.
File metadata
- Download URL: httpx_rate_limiter_transport-0.1.1-py3-none-any.whl
- Upload date:
- Size: 8.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b5a63f035c885275d1f0e0b686f9924b79f7858e1768e687fdff13155762a9d
|
|
| MD5 |
c8ab03a132dcb2c9a9c12d80caf6e4e8
|
|
| BLAKE2b-256 |
be8362bad579e6e504e3a53366798a4769748c66a2bdb0532dbab450c65847ae
|