httpx streaming response: stream(), iter_bytes, ResponseNotRead (tested)

Use with client.stream("GET", url) as r: and iterate r.iter_bytes() / r.iter_lines() inside the block (async: aiter_bytes()). Accessing r.text there raises httpx.ResponseNotRead unless you call r.read() first.

Streaming in httpx means not loading the whole body into memory: httpx.stream() / client.stream() return the response as soon as the headers arrive, and you iterate over the body. Measured with httpx 0.28.1 on Python 3.13.5 against a local http.server that returns three lines (21 bytes).

1. ResponseNotRead: .text/.json() inside a stream

import httpx
from srv import start
base = start()   # returns "line 1\nline 2\nline 3\n"

with httpx.stream("GET", base) as r:
    try:
        print(r.text)
    except httpx.ResponseNotRead as e:
        print(type(e).__name__ + ":", e)

Output:

ResponseNotRead: Attempted to access streaming response content, without having called `read()`.

Inside a stream() block the body has not been read, so .text, .content and .json() raise httpx.ResponseNotRead. Either iterate (below) or call r.read() first; if you always need the whole body, use a normal client.get() instead.

2. iter_lines, iter_bytes(chunk_size) and read()

import httpx
from srv import start
base = start()

with httpx.Client() as client:
    with client.stream("GET", base) as r:
        print(r.status_code, r.headers["content-length"])
        for line in r.iter_lines():
            print(repr(line))

    with client.stream("GET", base) as r:
        print([len(c) for c in r.iter_bytes(chunk_size=8)])

    with client.stream("GET", base) as r:
        r.read()                     # load the body, then .text works
        print(repr(r.text[:6]))

Output:

200 21
'line 1'
'line 2'
'line 3'
[8, 8, 5]
'line 1'

Status and headers are available before the body. iter_lines() yields decoded lines without the newline; iter_bytes(chunk_size=8) yields chunks of that size (the last one shorter); iter_text() and iter_raw() (no gzip/deflate decoding) also exist. To download a file, write each iter_bytes() chunk to an open file.

3. StreamConsumed and StreamClosed

import httpx
from srv import start
base = start()

with httpx.stream("GET", base) as r:
    list(r.iter_bytes())
    try:
        list(r.iter_bytes())
    except httpx.StreamConsumed as e:
        print(type(e).__name__ + ":", str(e)[:80])

with httpx.stream("GET", base) as r:
    pass
try:
    r.read()
except httpx.StreamClosed as e:
    print(type(e).__name__ + ":", str(e)[:80])

Output:

StreamConsumed: Attempted to read or stream some content, but the content has already been strea
StreamClosed: Attempted to read or stream content, but the stream has been closed.

A stream can be iterated once: a second pass raises StreamConsumed. After the with block exits, the connection is released and reading raises StreamClosed, so do all reading inside the block.

4. Async: client.stream with aiter_lines / aiter_bytes

import asyncio, httpx
from srv import start
base = start()

async def main():
    async with httpx.AsyncClient() as client:
        async with client.stream("GET", base) as r:
            async for line in r.aiter_lines():
                print(line)

asyncio.run(main())

Output:

line 1
line 2
line 3

With AsyncClient, use async with client.stream(...) and the aiter_* methods (aiter_bytes, aiter_text, aiter_lines, aread()).

Related: httpx timeout, httpx RemoteProtocolError, httpx vs requests.

Frequently asked questions

Why does httpx raise ResponseNotRead?

You accessed .text, .content or .json() on a streaming response without calling read() first.

How do I stream a download with httpx?

Open client.stream('GET', url) in a with block and write each chunk from r.iter_bytes() to a file.

Can I iterate an httpx stream twice?

No. A second iteration raised StreamConsumed; reading after the with block raised StreamClosed.

Written with AI assistance. Measured on 2026-10-23 with httpx 0.28.1 on Python 3.13.5 against a local http.server. Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; httpx is free.