Under load, the pool and timeout settings decide whether a service degrades gracefully or stalls.
httpx.Timeout has four parts:
- connect: establishing the connection,
- read: waiting for a chunk of data from the server,
- write: sending a chunk of the request body,
- pool: waiting to acquire a connection from the pool.
httpx.Timeout(10.0, connect=2.0) sets 10 seconds everywhere except connect. You can pass a Timeout to a client and override it per request. As with most clients, read is per chunk, not a total deadline; for a total limit wrap the call with asyncio.timeout() in async code.
httpx.Limits controls the pool:
max_connections(default 100): total concurrent connections,max_keepalive_connections(default 20): idle connections kept for reuse,keepalive_expiry(default 5 seconds): how long idle connections are kept.
When all connections are busy, requests wait for one and raise httpx.PoolTimeout if the pool timeout expires. A PoolTimeout under load means your concurrency exceeds max_connections; either raise the limit or reduce concurrency.
HTTPX has no built-in retry for HTTP statuses. httpx.HTTPTransport(retries=3) (and AsyncHTTPTransport) only retries connection failures (ConnectError, ConnectTimeout), which are always safe. For retrying 503s or 429s, write a small loop, a custom transport, or use a library such as tenacity or stamina.
Example
import httpx
timeout = httpx.Timeout(10.0, connect=2.0, pool=1.0)
print(timeout) # Timeout(connect=2.0, read=10.0, write=10.0, pool=1.0)
limits = httpx.Limits(max_connections=1, max_keepalive_connections=1)
transport = httpx.HTTPTransport(retries=2, limits=limits) # connect retries only
with httpx.Client(transport=transport, timeout=timeout,
base_url="https://httpbin.org") as client:
with client.stream("GET", "/bytes/10"): # holds the only connection
try:
client.get("/get") # must wait for the pool
except httpx.PoolTimeout:
print("pool exhausted") # pool exhausted
print(client.get("/get").status_code) # 200 (connection free again)Remember
- Timeout has connect, read, write and pool parts
- Limits sets max_connections and keep-alive; PoolTimeout signals exhaustion
- HTTPTransport(retries=n) only retries connection failures
Watch out for
- Expecting HTTPTransport(retries=3) to retry 503 responses.