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 otel
A 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.
If this project saves you time, consider giving it a star on GitHub.
Browse the full list of templates and libraries in
modern-python; the org profile has the categorized index.