Lord Of The Http Clients — a fast, Rust-backed, typed HTTP client for Python, with first-class optional support for pydantic and msgspec as both decode and encode targets.
uv add 'lothc[pydantic,msgspec]'
(Any subset of the extras works — the base package alone gets you bytes/dict support.)
import asyncio
from lothc import HTTPClient
from pydantic import BaseModel
class Pokemon(BaseModel):
id: int
name: str
async def main() -> None:
async with HTTPClient(base_url="https://pokeapi.co/api/v2/") as client:
response = await client.get("pokemon/pikachu", into=Pokemon)
print(response.status, response.data) # 200 id=25 name='pikachu'
asyncio.run(main())pyreqwest is a genuinely great Rust-backed HTTP
client, and the inspiration for lothc. Its API is a builder (client.get(url).build() before you
can .send()), so lothc gives it the requests/httpx call shape, and adds its own native
transport, faster still. pyreqwest stays lothc's transport on Windows, for HTTP/2 and with
native="off".
Where other clients' .json() returns an unvalidated dict, lothc decodes straight into a
validated pydantic or msgspec type.
On Linux and macOS, lothc's own native transport (the default) runs async get(into=Struct) at
about 2.2x bare pyreqwest under load: 97,501 requests a second, each decoded into a validated
msgspec Struct. On its pyreqwest transport (Windows, http2=True, or native="off"), lothc
stays close to pyreqwest's own throughput.
50,000 requests at concurrency 100 on an AWS c8a.xlarge, validation included.
Full numbers.
| Typed decoding | into= any type expression (Pokemon, list[Pokemon], ...) gives back a validated pydantic BaseModel or msgspec Struct, not a dict. |
| Two clients, one API | HTTPClient (async) and SyncHTTPClient (sync) — identical surface. |
| Every verb | get, post, put, patch, delete, head, options, download (each returning a Response: .data, .status, .headers) — see Verbs. |
| Typed params, headers & forms | A BaseModel/Struct for query params or headers (with None-field omission), or a body sent urlencoded (data=) or as a real multipart form (form=). |
| Precise static types | Every verb is paired @overloads, not a cast-laden generic — your editor knows the exact return type. |
| SSE & streaming | Spec-compliant SSE — automatic reconnect with Last-Event-ID, server retry: honored, typed decode (discriminated unions included) — plus raw or NDJSON-typed stream_get/stream_post — see SSE / Streaming. |
| Retries with real backoff | max_retries/retry_methods, Retry-After-aware — see Retries. |
| Authentication | Static bearer_token, a per-request-refreshed bearer_auth, basic_auth, or default_headers for anything else — plus a ready-made OAuth 2 client-credentials provider (OAuthProvider: mint, leeway-based renewal, refresh-then-mint fallback, optional on-disk token cache) — see Authentication. |
| Cookies, redirects, proxy | cookie_store, follow_redirects/max_redirects, proxy= — see Networking. |
| A real error hierarchy | HTTPTransportError/HTTPTimeoutError/HTTPConnectionError for no response, HTTPResponseError for 4xx/5xx — see Error handling. |
See Development.
lothc is MIT. It's built on pyreqwest (Apache-2.0), which is built on the Rust crate reqwest (MIT or Apache-2.0). Thanks to both.