httpx: Modern Async HTTP Client for Python – Complete Guide
Learn httpx, the modern async HTTP client for Python.
20+ years shipping production Python across data and backend systems. Drawn from code that ran under real load.
- ✓Python 3.7+ installed
- ✓Basic understanding of HTTP concepts
- ✓Familiarity with
asyncio(for async examples)
- httpx is a modern HTTP client for Python supporting both sync and async APIs.
- It offers HTTP/2, connection pooling, timeouts, retries, and type annotations.
- Use
httpx.AsyncClientfor async code andhttpx.Clientfor sync code. - It's a drop-in replacement for
requestswith additional features. - Ideal for production APIs, web scraping, and microservices.
Imagine you're ordering food from different restaurants. The old way (requests) is like calling each restaurant one by one and waiting on hold. httpx is like having a smart assistant that can call multiple restaurants at once, handle busy signals, and even remember your favorite orders. It's faster and more efficient.
In the world of Python HTTP clients, requests has been the go-to library for years. But as applications grow and demand for performance increases, you need a client that can handle modern requirements: async/await, HTTP/2, connection pooling, and robust error handling. Enter httpx – a fully featured HTTP client for Python 3.7+ that provides both synchronous and asynchronous APIs.
Why httpx? Imagine you're building a web scraper that fetches data from 100 APIs. With requests, you'd either block on each request or juggle threads. With httpx's async client, you can fire all 100 requests concurrently with minimal overhead. Or perhaps you're building a microservice that needs to make outbound calls without blocking the event loop – httpx integrates seamlessly with asyncio.
httpx is not just an async clone of requests. It brings native HTTP/2 support (faster multiplexing), automatic content decompression, cookie persistence, and a transport layer that you can customize for proxies or mock testing. It also supports type hints, making it a joy to use in modern Python codebases.
In this tutorial, you'll learn how to install httpx, make GET and POST requests, handle timeouts and retries, work with async clients, and debug production issues. We'll cover real-world patterns like connection pooling, streaming responses, and error handling. By the end, you'll be ready to replace requests with httpx in your next project.
Installing httpx
To get started with httpx, install it via pip. It's recommended to install the full package with HTTP/2 support. If you're working in an async environment, ensure you have anyio or asyncio available.
``bash pip install httpx ``
For HTTP/2 support, install the optional h2 dependency:
``bash pip install httpx[http2] ``
That's it! You're ready to make your first request.
pip install httpx[http2] for full features.Making GET and POST Requests
httpx provides a simple API similar to requests. Use for synchronous GET requests and httpx.get() for POST requests. You can pass query parameters, headers, and JSON data easily.httpx.post()
For POST requests with JSON, use the json parameter – httpx automatically sets the Content-Type header. You can also send form data with data.
Let's see examples:
response.raise_for_status() to catch HTTP errors early.params for query strings, json for JSON payloads, and data for form data.Using the Client for Connection Pooling
For production applications, you should use httpx.Client (sync) or httpx.AsyncClient (async) instead of the top-level functions. The client maintains a connection pool, reuses TCP connections, and allows you to configure defaults like base URL, headers, and timeouts.
Using a client as a context manager ensures connections are properly closed. You can also set a base URL to avoid repeating the hostname.
Async Requests with AsyncClient
One of httpx's standout features is native async support. Use httpx.AsyncClient with async/await to perform non-blocking HTTP requests. This is ideal for I/O-bound tasks like web scraping, API aggregation, or microservices.
You can run multiple requests concurrently using asyncio.gather() or asyncio.create_task(). The async client also supports context managers.
Timeouts and Retries
Production systems must handle network failures gracefully. httpx allows you to set timeouts for the entire request or individual phases (connect, read, write). You can also configure retries using a custom transport.
Timeouts can be set per-request or on the client. For retries, use httpx.HTTPTransport with the retries parameter. Note that retries only apply to certain exceptions (like connection errors) by default.
Handling Errors and Exceptions
httpx raises exceptions for network errors, timeouts, and HTTP status codes (if you call ). Common exceptions include raise_for_status()httpx.ConnectError, httpx.TimeoutException, and httpx.HTTPStatusError. Always wrap your requests in try-except blocks to handle failures gracefully.
You can also inspect the response for details even when an error occurs.
raise_for_status() and catch specific exceptions for robust error handling.Streaming Responses and File Downloads
For large responses, you can stream the content to avoid loading everything into memory. Use for sync or client.stream()async with for async. This is useful for downloading large files or processing real-time data.client.stream()
You can iterate over the response bytes or read chunks.
Custom Headers and Authentication
You can set custom headers on the client or per request. httpx supports various authentication methods: Basic, Digest, Bearer token, and custom auth classes. Use the auth parameter.
For bearer tokens, you can pass a tuple ('token', '') or use httpx.BearerAuth.
httpx.Auth subclass to automatically refresh tokens.auth parameter for authentication; set default headers on the client.Testing with Mock Transports
httpx provides a mock transport for testing. You can simulate responses without making real HTTP calls. This is perfect for unit tests. Use httpx.MockTransport and pass a handler function that returns a response.
You can also use respx library for more advanced mocking.
The Silent Timeout That Took Down Our API Gateway
- Always set explicit timeouts on HTTP clients.
- Monitor connection pool usage to detect leaks.
- Use retries with jitter to avoid thundering herd.
- Test failure scenarios in staging before production.
- Log request durations to identify slow endpoints.
client.get(..., timeout=5).limits=httpx.Limits(max_connections=100); ensure clients are reused.http2=False; check server compatibility.client.get(..., timeout=httpx.Timeout(5.0, read=2.0)); profile network.follow_redirects=False; inspect response history.response = client.get(url, timeout=5)print(response.elapsed)client = httpx.Client(timeout=10)| File | Command / Code | Purpose |
|---|---|---|
| install.py | response = httpx.get('https://httpbin.org/get') | Installing httpx |
| basic_requests.py | response = httpx.get('https://httpbin.org/get', params={'key': 'value'}) | Making GET and POST Requests |
| client_example.py | with httpx.Client(base_url='https://httpbin.org') as client: | Using the Client for Connection Pooling |
| async_example.py | async def fetch(url): | Async Requests with AsyncClient |
| timeout_retry.py | response = httpx.get('https://httpbin.org/delay/3', timeout=5.0) | Timeouts and Retries |
| error_handling.py | url = 'https://httpbin.org/status/404' | Handling Errors and Exceptions |
| streaming.py | with httpx.Client() as client: | Streaming Responses and File Downloads |
| auth.py | headers = {'User-Agent': 'MyApp/1.0'} | Custom Headers and Authentication |
| mock_test.py | def handler(request): | Testing with Mock Transports |
Key takeaways
raise_for_status() to catch HTTP errors.Interview Questions on This Topic
How does httpx handle connection pooling?
httpx.Client or httpx.AsyncClient. The pool reuses TCP connections for requests to the same host, reducing latency and resource usage. You can configure pool limits with Limits(max_connections=...).Frequently Asked Questions
20+ years shipping production Python across data and backend systems. Drawn from code that ran under real load.
That's Python Libraries. Mark it forged?
3 min read · try the examples if you haven't