Typed, resilient HTTP clients for Python, sync and async.
- A 4xx or 5xx response raises an exception named after its status, such as
NotFoundErrorfor 404 orRateLimitedErrorfor 429. All of them subclasshttpware.StatusError, so you never callraise_for_status(). response_model=Userdecodes the body into your pydantic or msgspec type. If no installed decoder handles the type, the call fails before the request is sent.- Retry with a retry budget, bulkhead, circuit breaker, and timeout ship as middleware you compose per client.
httpware is a thin layer over httpx2: requests and responses are plain
httpx2.Request and httpx2.Response objects.
Status: Pre-1.0. Public API is subject to change between minor releases until v1.0.
pip install httpware # core only, no decoder
pip install httpware[pydantic] # PydanticDecoder: BaseModel, dataclasses, primitives, generics
pip install httpware[msgspec] # MsgspecDecoder: Struct, dataclasses, primitives, generics
pip install httpware[pydantic,msgspec] # both; BaseModel goes to pydantic, Struct to msgspec
pip install httpware[otel] # OpenTelemetry span events
pip install httpware[all] # pydantic, msgspec, and otelA typed GET against a live API (needs pip install httpware[pydantic]):
import asyncio
from httpware import AsyncClient
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
async def main() -> None:
async with AsyncClient(base_url="https://jsonplaceholder.typicode.com") as client:
user = await client.get("/users/1", response_model=User)
print(user.name) # Leanne Graham
asyncio.run(main())The sync Client works the same way: use Client instead of AsyncClient, and drop await and async with. A 4xx/5xx response raises a typed StatusError; a malformed body raises DecodeError. Both subclass httpware.ClientError.
Full guides live at httpware.modern-python.org:
- Quickstart: first requests, client options, streaming.
- Resilience: retry and retry budget, bulkhead, circuit breaker, timeout.
- Errors: the exception tree and how to catch it.
- Decoders: typed response bodies and custom decoders.
- Middleware: writing your own (auth, tracing, request IDs).
- Observability: logger and event names, OpenTelemetry wiring.
- Testing: injecting
httpx2.MockTransport. - Recipes: DI wiring, phase decorators, Link header pagination.
🗒️ Release notes · 📦 PyPI · 📝 License
Browse the full list of templates and libraries in
modern-python; the org profile has the categorized index.