# Python : Web : HTTPX
\[ [PyPI](https://pypi.org/project/httpx/) | [Source](https://github.com/encode/httpx/) | [Docs](https://www.python-httpx.org/) | [API](https://www.python-httpx.org/api/) ]
## Overview
HTTPX builds on [requests](https://pypi.org/project/requests/) and adds...
- HTTP/2.0 support
- async API
- strict timeouts
- make requests directly on WSGI and ASGI apps (useful as test client)
- gzip and deflate HTTP response encodings are automatically decoded
#### Packages
```bash
$ pip install httpx
$ pip install "httpx[ ...extras...]"
```
**Extras**
- cli -> CLI
- https -> HTTP/2 support
#### Imports
```python
from httpx import (
# classes
ASGITransport, AsyncClient, Client, Cookies, DigestAuth, Request, WSGITransport
# errors
HTTPError, HTTPStatusError, RequestError
# enums
codes
)
```
## Usage
#### Requests
```python
r = httpx.delete|get|head|options|post( "URL", ... )
follow_redirects = True
auth = ( "USERNAME", "PASSWORD" ) # Basic Auth
auth = DigestAuth( "USERNAME", "PASSWORD" )
cookies = { "NAME": "COOKIE" }
headers = { "user-agent": "...", ... }
params = { "k1": "v1", "k2": [ "v2a", ... ] }
# for post()
data = { "k1": "v1", "k2": [ "v2a", ... ] }
files = {
"f1": <fileobj>
"f2": ( "FILENAME" | None, <fileobj>, "MIME Type" | None )
}
json = ...
# use Cookies for more control
cookies = Cookies()
cookies.set( "NAME", "COOKIE", domain="DOMAIN" )
httpx.get( ..., cookies=cookies )
```
#### Responses
```python
r.status_code # 200 aka codes.OK
r.content # b"..."
r.text # "..."
r.json() # ...
r.encoding # "UTF-8"
r.url # URL( "..." )
r.headers # dict-like, case-insensitive
r.cookies # Cookies instance (dict-like + methods to access cookies by domain or path)
r.raise_for_status() # raise exception if 4xx/5xx code
# returns self if 2xx code to facilitate idiom:
# r = httpx.get( ... ).raise_for_status()
```
**Redirects**
```python
# when not following redirects (default)
r.status_code -> 301
r.history -> []
r.next_request -> Request
# when following redirects
r.history -> [ Response, ... ]
```
**Streaming Responses**
```python
with httpx.stream( "GET", "https://example.com" ) as r:
for data in r.iter_bytes():
for text in r.iter_text():
for line in r.iter_lines():
...
```
> [!NOTE] Create Image from Response:
> ```python
> from PIL import Image
> from io import BytesIO
> i = Image.open( BytesIO( r.content ) )
> ```
#### Clients
**Why use Client?**
- uses connection pooling
- re-uses connections to hosts
- cookie persistence across requests
- applying custom config to all requests
- sending through proxies
- using HTTP/2
```python
with Client( ... ) as client: # takes most of the same options as post()
r = client.get( ... ) # client has mostly the same methods as httpx
# Client-Only Options
with Client( base_url="https://example.com" ) as client:
r = client.geT( "/subpath" )
```
When a configuration option is provided at both the client-level and request-level, one of two things can happen:
- For headers, query parameters and cookies, the values are combined together.
- For all other parameters, the request-level value takes priority.
**Async**
```python
async with AsyncClient() as client:
r = await client.get( "https://example.com/" )
async with client.stream( "GET", "https://example.com/" ) as response:
async for chunk in response.aiter_bytes():
...
```
When sending a streaming request body with an `AsyncClient` instance, you should use an async bytes generator:
```python
async def upload_bytes():
... # yield byte content
await client.post( url, content=upload_bytes() )
```
#### Errors
```python
HTTPError
RequestError
.request
HTTPStatusError # raised by raise_for_status()
.request
.response
```