httpcore
httpcore is a minimal, low-level HTTP/1.1 and HTTP/2 client library for Python, intended as a transport layer for higher-level clients such as httpx. It provides synchronous and (optionally) asynchronous connection pooling, SOCKS proxy support, streaming responses, and a 'trace' extension for request lifecycle introspection. Current stable version is 1.0.9 (April 2025). The project follows SEMVER and releases several times per year.
Warnings
- breaking CVE-2025-43859 (GHSA-vqfr-h8mv-ghfj): h11 <=0.15.0 accepts malformed Chunked-Encoding bodies enabling HTTP request smuggling (CVSS 9.1). httpcore <1.0.9 pulls in vulnerable h11. Upgrade to httpcore 1.0.9+ which requires h11>=0.16.0.
- breaking Async support is NOT included in the default install since 1.0.0. Importing or instantiating AsyncConnectionPool without the async extras raises a RuntimeError at runtime, not at import time.
- gotcha Response headers (and request headers) are List[Tuple[bytes, bytes]], not a string-keyed dict. Comparing or accessing headers with plain strings will silently fail or raise a TypeError.
- gotcha httpcore.request() and httpcore.stream() are top-level convenience helpers that open a new connection on every call. Using them in production code bypasses connection pooling entirely, causing a new TCP+TLS handshake per request.
- breaking The pre-1.0 class names SyncConnectionPool and SyncHTTPProxy were removed. Any code importing these names directly will raise an ImportError.
- gotcha Timeouts are passed via the request extensions dict, not as top-level kwargs: httpcore.request('GET', url, extensions={'timeout': {'connect': 5.0, 'read': 10.0}}). Passing timeout= as a keyword argument has no effect and is silently ignored.
- gotcha response.content is only available after the full body has been read. When using httpcore.stream() or iterating response.iter_stream(), accessing response.content before calling response.read() raises an error.
Install
-
pip install httpcore -
pip install 'httpcore[asyncio]' -
pip install 'httpcore[trio]' -
pip install 'httpcore[http2]' -
pip install 'httpcore[socks]' -
pip install 'httpcore[asyncio,http2,socks]'
Imports
- httpcore
import httpcore
- ConnectionPool
import httpcore http = httpcore.ConnectionPool()
- AsyncConnectionPool
import httpcore http = httpcore.AsyncConnectionPool()
- HTTPProxy
import httpcore proxy = httpcore.HTTPProxy(proxy_url='http://proxy:8080')
- request (top-level function)
import httpcore response = httpcore.request('GET', 'https://example.com')
Quickstart
import httpcore
# One-off request (no connection reuse)
response = httpcore.request('GET', 'https://httpbin.org/get')
print(response.status) # int, e.g. 200
# Headers are List[Tuple[bytes, bytes]] — decode explicitly
for name, value in response.headers:
print(name.decode(), value.decode())
print(response.content) # bytes
# Production pattern: reuse a ConnectionPool
with httpcore.ConnectionPool() as http:
r = http.request('GET', 'https://httpbin.org/get')
print(r.status)
# Streaming large response
with httpcore.stream('GET', 'https://httpbin.org/stream-bytes/1024') as r:
for chunk in r.iter_stream():
pass # process chunk (bytes)
# Async (requires: pip install 'httpcore[asyncio]')
import asyncio
async def main():
async with httpcore.AsyncConnectionPool() as http:
r = await http.request('GET', 'https://httpbin.org/get')
print(r.status)
asyncio.run(main())