Response¶
The HTTP response object returned by all request methods.
Overview¶
The Response class provides access to all aspects of an HTTP response:
import httpr
response = httpr.get("https://httpbin.org/get")
# Status
print(response.status_code) # 200
# Body
print(response.text) # Decoded text
print(response.content) # Raw bytes
data = response.json() # Parsed JSON
# Headers (case-insensitive)
print(response.headers["content-type"])
print(response.headers["Content-Type"]) # Same result
# Cookies
print(response.cookies) # {"session": "value"}
# Metadata
print(response.url) # Final URL after redirects
print(response.encoding) # Detected encoding
Properties¶
status_code¶
HTTP status code (e.g., 200, 404, 500).
Example:
reason_phrase¶
Canonical reason phrase for the status code (e.g., "OK", "Not Found").
Example:
Status classification¶
A set of boolean properties for checking the status code class without comparing integers manually:
| Property | True when |
|---|---|
is_informational |
1xx status code |
is_success |
2xx status code |
is_redirect |
3xx status code |
is_client_error |
4xx status code |
is_server_error |
5xx status code |
is_error |
4xx or 5xx status code |
has_redirect_location |
3xx response with a Location header (301, 302, 303, 307, 308) |
Example:
response = httpr.get("https://httpbin.org/status/200")
if response.is_success:
print("Success!")
elif response.is_client_error:
print("Client error")
elif response.is_server_error:
print("Server error")
text¶
Response body decoded as text.
Encoding is automatically detected from:
Content-Typeheader charset- HTML meta charset tag
- Falls back to UTF-8
Example:
content¶
Response body as raw bytes.
Example:
response = httpr.get("https://httpbin.org/bytes/100")
print(len(response.content)) # 100
# Save binary file
response = httpr.get("https://httpbin.org/image/png")
with open("image.png", "wb") as f:
f.write(response.content)
headers¶
Response headers as a case-insensitive dict-like object.
Supports:
response.headers["Content-Type"]- get by keyresponse.headers.get("content-type", "default")- get with default"content-type" in response.headers- check existenceresponse.headers.keys()- all header namesresponse.headers.values()- all header valuesresponse.headers.items()- key-value pairs
Example:
response = httpr.get("https://httpbin.org/get")
# Case-insensitive access
print(response.headers["content-type"])
print(response.headers["Content-Type"]) # Same
# Iteration
for name, value in response.headers.items():
print(f"{name}: {value}")
cookies¶
Cookies set by the server via Set-Cookie headers.
Example:
response = httpr.get("https://httpbin.org/cookies/set?name=value")
print(response.cookies) # {"name": "value"}
url¶
Final URL after following any redirects.
Example:
response = httpr.get("https://httpbin.org/redirect/3")
print(response.url) # https://httpbin.org/get
encoding¶
Character encoding detected from response headers or content.
Example:
text_markdown¶
HTML response body converted to Markdown format.
Uses Rust's html2text crate for conversion.
Example:
response = httpr.get("https://example.com")
print(response.text_markdown)
# # Example Domain
#
# This domain is for use in illustrative examples...
text_plain¶
HTML response body converted to plain text (no formatting).
Example:
text_rich¶
HTML response body converted to rich text format.
Methods¶
json¶
Parse response body as JSON.
Returns: Parsed JSON (dict, list, str, int, float, bool, or None)
Raises: Exception if body is not valid JSON
Example:
response = httpr.get("https://httpbin.org/json")
data = response.json()
print(data["slideshow"]["title"])
Note
json() is a method, not a property. Call it with parentheses.
raise_for_status¶
Raise HTTPStatusError if the response has a non-2xx status
code. Returns the response itself on success, so the call can be chained.
Returns: The same Response object (when the status is 2xx)
Raises: HTTPStatusError for any non-2xx status code
Example:
response = httpr.get("https://httpbin.org/status/404")
try:
response.raise_for_status()
except httpr.HTTPStatusError as exc:
print(exc) # Client error '404 Not Found' for url '...'
# Chaining: raise on error, otherwise parse the body
data = httpr.get("https://httpbin.org/json").raise_for_status().json()
Note
Unlike requests, any non-2xx status raises — including 1xx and 3xx
responses — matching httpx behavior. With redirects followed (the
default), a 3xx is only seen when follow_redirects=False.
StreamingResponse¶
For streaming large responses without buffering the entire response in memory, use the Client.stream() method which returns a StreamingResponse.
import httpr
with httpr.Client() as client:
with client.stream("GET", "https://httpbin.org/stream-bytes/1000") as response:
for chunk in response.iter_bytes():
process(chunk)
Properties¶
status_code¶
HTTP status code (e.g., 200, 404, 500).
Example:
Status helpers¶
StreamingResponse exposes the same status API as Response, available
immediately after the headers arrive (before the body is read):
reason_phrase— canonical reason phrase for the status codeis_informational,is_success,is_redirect,is_client_error,is_server_error,is_error— status-class booleanshas_redirect_location— 3xx response with aLocationheaderraise_for_status()— raiseHTTPStatusErrorfor a non-2xx status, otherwise return the streaming response itself
Example:
with client.stream("GET", "https://httpbin.org/stream-bytes/1000") as response:
response.raise_for_status() # bail out early on a non-2xx status
for chunk in response.iter_bytes():
process(chunk)
headers¶
Response headers as a case-insensitive dict-like object.
Example:
with client.stream("GET", "https://httpbin.org/get") as response:
content_type = response.headers["content-type"]
cookies¶
Cookies set by the server via Set-Cookie headers.
url¶
Final URL after following any redirects.
is_closed¶
Whether the stream has been closed.
Example:
with client.stream("GET", "https://httpbin.org/get") as response:
print(response.is_closed) # False
print(response.is_closed) # True (after context manager exits)
is_consumed¶
Whether the stream has been fully consumed.
Example:
with client.stream("GET", "https://httpbin.org/get") as response:
print(response.is_consumed) # False
_ = list(response) # Consume the stream
print(response.is_consumed) # True
Methods¶
iter_bytes¶
Iterate over the response body as bytes chunks.
Returns: Iterator yielding bytes chunks
Example:
with client.stream("GET", "https://httpbin.org/stream-bytes/1000") as response:
for chunk in response.iter_bytes():
print(f"Received {len(chunk)} bytes")
iter_text¶
Iterate over the response body as text chunks, decoded using the response encoding.
Returns: TextIterator yielding string chunks
Example:
with client.stream("GET", "https://httpbin.org/html") as response:
for text in response.iter_text():
print(text, end="")
iter_lines¶
Iterate over the response body line by line.
Returns: LineIterator yielding string lines
Useful for Server-Sent Events (SSE) and line-based protocols.
Example:
with client.stream("GET", "https://httpbin.org/stream/10") as response:
for line in response.iter_lines():
print(line.strip())
read¶
Read the entire remaining response body into memory.
Returns: Response body as bytes
Example:
with client.stream("GET", "https://httpbin.org/get") as response:
if response.status_code == 200:
content = response.read()
close¶
Close the stream and release resources.
Note: When using the context manager, close() is called automatically.
Example:
with client.stream("GET", "https://httpbin.org/get") as response:
# Process headers
if response.status_code != 200:
response.close() # Close early without reading body
return
# Otherwise read body
content = response.read()
Direct Iteration¶
StreamingResponse supports direct iteration, which is equivalent to calling iter_bytes():
with client.stream("GET", "https://httpbin.org/stream-bytes/1000") as response:
for chunk in response: # Same as response.iter_bytes()
process(chunk)
Async Streaming¶
AsyncClient.stream() yields an AsyncStreamingResponse. It exposes the same
attributes and methods as StreamingResponse (status, headers, cookies, URL,
raise_for_status(), is_closed, is_consumed, the synchronous iter_*(),
read() and close()), plus async variants that read each chunk on the
client's thread pool so the event loop is never blocked:
aiter_bytes¶
Iterate over the response body as bytes chunks. async for chunk in response is equivalent.
aiter_text¶
Iterate over the response body as text chunks, decoded with the response's encoding.
aiter_lines¶
Iterate over the response body line by line (lines include their trailing newline).
aread¶
Read the entire remaining body into memory.
aclose¶
Close the stream and release its connection. Called automatically when the async with block exits.
Example:
async with httpr.AsyncClient() as client:
async with client.stream("GET", "https://example.com/events") as response:
async for line in response.aiter_lines():
handle(line)
The synchronous iter_bytes(), iter_text(), iter_lines() and read() remain available on the async response but block the event loop until the next chunk arrives; prefer the a-prefixed methods in async code. A stream that is open when client.aclose() is called keeps its connection and can still be read to the end.
Important Notes¶
- Always use as context manager: Ensures proper cleanup of resources
- Headers available immediately: Status code, headers, cookies, and URL are accessible before reading the body
- Body only read on demand: The response body is only fetched when you iterate or call
read() - Cannot re-read: Once consumed, the stream cannot be read again
- Supported for all methods: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
Exception Handling¶
The streaming response raises specific exceptions:
StreamClosed: Raised when attempting to read from a closed streamStreamConsumed: Raised when attempting to re-read a consumed stream
Example: