Clients

HTTP requests are sent by using a Client instance. Client instances are thread safe interfaces that maintain a pool of HTTP connections.

httpx
>>> cli = httpx.Client()
>>> cli
<Client [0 active]>

The client representation provides an indication of how many connections are currently in the pool.

httpx
>>> r = cli.get("https://www.example.com")
>>> r = cli.get("https://www.wikipedia.com")
>>> r = cli.get("https://www.theguardian.com/uk")
>>> cli
<Client [0 active, 3 idle]>

The connections in the pool can be explicitly closed, using the close() method...

httpx
>>> cli.close()
>>> cli
<Client [0 active]>

Client instances support being used in a context managed scope. You can use this style to enforce properly scoped resources, ensuring that the connection pool is cleanly closed when no longer required.

httpx
>>> with httpx.Client() as cli:
...     r = cli.get("https://www.example.com")

It is important to scope the use of client instances as widely as possible.

Typically you should have a single client instance that is used throughout the lifespan of your application. This ensures that connection pooling is maximised, and minmises unneccessary reloading of SSL certificate stores.

The recommened usage is to either a have single global instance created at import time, or a single context scoped instance that is passed around wherever it is required.

Setting a base URL

Client instances can be configured with a base URL that is used when constructing requests...

httpx
>>> with httpx.Client(url="https://www.httpbin.org") as cli:
>>>     r = cli.get("/json")
>>>     print(r)
<Response [200 OK]>

Setting client headers

Client instances include a set of headers that are used on every outgoing request.

The default headers are:

You can override this behavior by explicitly specifying the default headers...

httpx
>>> headers = {"User-Agent": "dev", "Accept-Encoding": "gzip"}
>>> with httpx.Client(headers=headers) as cli:
>>>     r = cli.get("https://www.example.com/")

Configuring the connection pool

The connection pool used by the client can be configured in order to customise the SSL context, the maximum number of concurrent connections, or the network backend.

httpx
>>> # Setup an SSL context to allow connecting to improperly configured SSL.
>>> no_verify = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> no_verify.check_hostname = False
>>> no_verify.verify_mode = ssl.CERT_NONE
>>> # Instantiate a client with our custom SSL context.
>>> pool = httpx.ConnectionPool(ssl_context=no_verify)
>>> with httpx.Client(transport=pool) as cli:
>>>     ...

Sending requests

Shortcut methods...


Transports

By default requests are sent using the ConnectionPool class. Alternative implementations for sending requests can be created by subclassing the Transport interface.

For example, a mock transport class that doesn't make any network requests and instead always returns a fixed response.

httpx
class MockTransport(httpx.Transport):
    def __init__(self, response):
        self._response = response

    @contextlib.contextmanager
    def send(self, request):
        yield response

    def close(self):
        pass

response = httpx.Response(200, content=httpx.Text('Hello, world'))
transport = MockTransport(response=response)
with httpx.Client(transport=transport) as cli:
    r = cli.get('https://www.example.com')
    print(r)

Middleware

In addition to maintaining an HTTP connection pool, client instances are responsible for two other pieces of functionality...

RedirectMiddleware

Wraps a transport class, adding support for HTTP redirect handling.

CookieMiddleware

Wraps a transport class, adding support for HTTP cookie persistence.


Custom client implementations

The Client implementation in httpx is intentionally lightweight.

If you're working with a large codebase you might want to create a custom client implementation in order to constrain the types of request that are sent.

The following example demonstrates a custom API client that only exposes GET and POST requests, and always uses JSON payloads.

httpx
class APIClient:
    def __init__(self):
        self.url = httpx.URL('https://www.example.com')
        self.headers = httpx.Headers({
            'Accept-Encoding': 'gzip',
            'Connection': 'keep-alive',
            'User-Agent': 'dev'
        })
        self.via = httpx.RedirectMiddleware(httpx.ConnectionPool())

    def get(self, path: str) -> Response:
        request = httpx.Request(
            method="GET",
            url=self.url.join(path),
            headers=self.headers,
        )
        with self.via.send(request) as response:
            response.read()
        return response

    def post(self, path: str, payload: Any) -> httpx.Response:
        request = httpx.Request(
            method="POST",
            url=self.url.join(path),
            headers=self.headers,
            content=httpx.JSON(payload),
        )
        with self.via.send(request) as response:
            response.read()
        return response

You can expand on this pattern to provide behavior such as request or response schema validation, consistent timeouts, or standardised logging and exception handling.


Quickstart Servers