httpx β Modern HTTP Client#
What it is#
httpx is a fully-featured HTTP client that supports both synchronous and asynchronous usage. Itβs a drop-in replacement for requests in synchronous code and the go-to choice when you need async, HTTP/2, or type-annotated APIs.
Install#
pip install httpx
# HTTP/2 support (optional)
pip install "httpx[http2]"
Quick example β synchronous#
import httpx
resp = httpx.get("https://httpbin.org/get", params={"tool": "httpx"})
resp.raise_for_status()
print(resp.status_code)
print(resp.json()["args"])
Output:
200
{'tool': 'httpx'}
Async example#
import asyncio
import httpx
async def fetch_all(urls: list[str]) -> list[dict]:
async with httpx.AsyncClient(timeout=10) as client:
tasks = [client.get(url) for url in urls]
responses = await asyncio.gather(*tasks)
return [r.json() for r in responses]
urls = [
"https://httpbin.org/get?n=1",
"https://httpbin.org/get?n=2",
"https://httpbin.org/get?n=3",
]
results = asyncio.run(fetch_all(urls))
print([r["args"] for r in results])
Output:
[{'n': '1'}, {'n': '2'}, {'n': '3'}]
When / why to use it over requests#
| Situation | Use |
|---|---|
You need async/await | httpx |
| You want HTTP/2 | httpx |
| Type annotations matter to you | httpx (fully typed) |
| Simple sync-only scripts | Either (requests has wider ecosystem) |
Existing requests codebase | Keep requests unless you have a reason to migrate |
Common pitfalls#
[!WARNING]
AsyncClientmust be used as a context manager β creatinghttpx.AsyncClient()withoutasync withleaks the connection pool. Always useasync with httpx.AsyncClient() as client:.
[!WARNING] Default timeout is 5 seconds β unlike
requests,httpxhas a default timeout. You can increase or disable it:timeout=httpx.Timeout(30.0)ortimeout=None.
[!TIP]
httpxraiseshttpx.HTTPStatusError(notrequests.HTTPError) when you callraise_for_status(). Handle imports accordingly if porting fromrequests.
Richer example β POST with auth and retry#
import httpx
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=0.5))
def post_event(payload: dict) -> dict:
with httpx.Client(timeout=10) as client:
resp = client.post(
"https://httpbin.org/post",
json=payload,
headers={"Authorization": "Bearer my-token"},
)
resp.raise_for_status()
return resp.json()["json"]
result = post_event({"event": "page_view", "user": "alice"})
print(result)
Output:
{'event': 'page_view', 'user': 'alice'}
[!NOTE]
tenacityis a separate retry library (pip install tenacity).httpxdoes not include built-in retry logic the wayrequests+HTTPAdapterdoes.
Quick reference#
# Sync client (recommended: reuse across calls)
with httpx.Client(base_url="https://api.example.com", timeout=10) as client:
r = client.get("/users", params={"page": 1})
r = client.post("/users", json={"name": "Alice"})
r = client.put("/users/1", json={"name": "Alice B."})
r = client.delete("/users/1")
# One-off sync calls
httpx.get(url)
httpx.post(url, json={...})
# Async
async with httpx.AsyncClient() as client:
r = await client.get(url)