lothc
Lord Of The Http Clients — a typed HTTP client for Python, built on pyreqwest (an awesome Rust-backed HTTP client), with first-class optional support for pydantic and msgspec as both decode and encode targets, plus TypedDict (validated via typeguard if installed) as a decode target.
Installing
uv add 'lothc[pydantic,msgspec,typeguard]'
(Any subset of the extras works — the base package alone gets you bytes/JSON/TypedDict-without-validation support.)
Quickstart
from lothc import HTTPClient
from pydantic import BaseModel
class Pokemon(BaseModel):
id: int
name: str
async with HTTPClient.build(base_url="https://pokeapi.co/api/v2/") as client:
pikachu = await client.get("pokemon/pikachu", response_data_type=Pokemon)
print(pikachu) # id=25 name='pikachu'
Why?
Why was lothc built, and why should you use it?
pyreqwest is a genuinely excellent HTTP client for Python, fast because the heavy lifting happens
in Rust, not pure Python. But its API is a builder pattern (client.get(path).build() before you
can even .send() it), unfamiliar to anyone coming from requests/httpx/niquests, where a
call is just client.get(url, params=..., headers=...). lothc wraps pyreqwest with exactly that
familiar shape, so you get pyreqwest's speed without giving up the ergonomics Python HTTP users
already expect.
The other reason: Python's most popular third-party HTTP clients (httpx, aiohttp, niquests) all
have first-class JSON support: call .json() and get back a dict. But in order for a Python
project to be end-to-end type-safe, data crossing I/O boundaries must be validated, and if
invalid, handled accordingly (raise an error) — a plain dict does neither. Closing that gap is
exactly what lothc is designed for.
The original idea was to make lothc backend-agnostic: httpx, aiohttp, niquests, and pyreqwest all interchangeable underneath the same typed lothc interface, so switching backends never meant switching your call sites. However, in the author's opinion there's currently no compelling reason not to just use pyreqwest, so pyreqwest is the only backend implemented today, but, if there's demand for an httpx/aiohttp/niquests backend, the author is happy to consider adding one.
Fast
Benchmarked with perf.py against a tiny Rust-based static JSON server, 10,000 requests at
concurrency 100 — including lothc's fully-typed decode targets (response_data_type= a msgspec
Struct, a pydantic BaseModel, or a TypedDict validated via typeguard), not just raw bytes or
an untyped dict. The object handed back from those runs isn't just parsed JSON — it's a real,
constructed, field-validated instance of your own type, and that validation cost is included in
the numbers, not benchmarked around. Decoding into a real msgspec Struct even edged out the
unvalidated dict path in this run; typeguard's pure-Python validation was the one clear exception,
costing a real, visible slowdown. Full numbers: docs/benchmarks.md.
Highlights
Pass response_data_type to any lothc call and get back a real, constructed, field-validated
pydantic BaseModel, msgspec Struct, or typeguard-checked TypedDict, instead of the plain
dict .json() leaves you to validate yourself. See Benchmarks for what
that costs (usually nothing).
| Two clients, one API | HTTPClient (async) and SyncHTTPClient (sync) — identical surface, both backed by pyreqwest. |
| Every verb | get/get_result, post, put, patch, delete, head, download — see Verbs. |
| Typed params, headers & forms | A BaseModel/Struct for query params or headers (with None-field omission), or a real multipart body via form=. |
| Precise static types | Every verb is paired @overloads, not a cast-laden generic — your editor knows the exact return type. |
| SSE & streaming | Typed SSE decode (discriminated unions included), plus raw or NDJSON-typed stream_get/stream_post — see SSE / Streaming. |
| Retries with real backoff | max_retries/retry_methods, a genuine pyreqwest middleware hook, Retry-After-aware — see Retries. |
| Authentication | Static bearer_token, a per-request-refreshed bearer_auth, or default_headers for anything else — 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. |
| Everything optional | pydantic, msgspec, typeguard — works with none, either, or all three installed. |
Development
See CLAUDE.md and style-guide.md for architecture notes and the (non-obvious in places) design
conventions this codebase relies on.
task deps-sync # install
task test # run tests
task check # lint + format-check + typecheck (what CI runs)
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lothc-0.0.4.tar.gz.
File metadata
- Download URL: lothc-0.0.4.tar.gz
- Upload date:
- Size: 21.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94abdd8827325dd242e177f023991882088d1aa21776f5afd889189590ece7ae
|
|
| MD5 |
2db0f6200784ef4d37c3e97575ca61d4
|
|
| BLAKE2b-256 |
161a4a7da5bd5d85fa1d0af3c5760ad9b13851b669d0a620028ac3cebb72ebc9
|
File details
Details for the file lothc-0.0.4-py3-none-any.whl.
File metadata
- Download URL: lothc-0.0.4-py3-none-any.whl
- Upload date:
- Size: 19.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e431d9eb70570513eb8e2073cec9bdc5b64038079dfb51a8d1af1fc3ba5325b6
|
|
| MD5 |
74131ee2635cc753ddf98c4c1e39aa41
|
|
| BLAKE2b-256 |
053c46b4bf47a36a5acd94b8bde1e7032fb57a974f15826e55e2d5038637695a
|