httpx-frugal
An httpx client with persistent caching (hishel) and per-domain rate limiting (pyrate-limiter), designed for data pipelines that need to conserve API quota across process restarts.
Cache is checked before rate limiting, so cached responses do not consume rate-limit tokens.
Install
pip install httpx-frugal
Optional extras:
pip install httpx-frugal[http2] # HTTP/2 inner transport
Quickstart (sync)
import pathlib
from pyrate_limiter import Duration, Rate
from httpx_frugal import RateLimitedCacheClient
rates = [Rate(5, Duration.MINUTE)]
cache_db = pathlib.Path("~/.cache/myapp/http-cache.sqlite").expanduser()
rate_db = pathlib.Path("~/.cache/myapp/rate-limiter.sqlite").expanduser()
cache_db.parent.mkdir(parents=True, exist_ok=True)
rate_db.parent.mkdir(parents=True, exist_ok=True)
client_wrapper = RateLimitedCacheClient(
rates=rates,
cache_db_path=cache_db,
rate_limiter_db_path=rate_db,
)
if client_wrapper.would_hit_cache("https://api.example.com/data"):
print("served from cache on next request")
with client_wrapper as client:
response = client.get("https://api.example.com/data")
print(response.extensions.get("hishel_from_cache"))
Async
from httpx_frugal import AsyncRateLimitedCacheClient
async with AsyncRateLimitedCacheClient(
rates=rates,
cache_db_path=cache_db,
rate_limiter_db_path=rate_db,
) as client:
response = await client.get("https://api.example.com/data")
Blocking mode
Wait for a rate-limit token instead of raising immediately:
client_wrapper = RateLimitedCacheClient(
rates=rates,
cache_db_path=cache_db,
rate_limiter_db_path=rate_db,
blocking=True,
rate_limit_timeout_seconds=30.0,
)
Per-request cache TTL
Override TTL for a single request via httpx extensions:
with client_wrapper as client:
client.get("https://api.example.com/short-lived", extensions={"hishel_ttl": 60})
Or use the helper:
from httpx_frugal import request_with_ttl
with client_wrapper as client:
req = request_with_ttl(client, "GET", "https://api.example.com/x", ttl=120)
client.send(req)
Rate limit introspection
remaining = client_wrapper.tokens_available("api.example.com")
# e.g. {"5/60000s": 3}
Cache invalidation
client_wrapper.clear_cache() # all entries
client_wrapper.clear_cache_for("https://...") # one URL
client_wrapper.clear_rate_limiter() # reset tokens
Multi-process pipelines
Enable SQLite file locking on the rate limiter bucket:
RateLimitedCacheClient(..., use_file_lock=True)
would_hit_cache caveat
would_hit_cache() uses hishel internal APIs and may break if hishel changes them. Prefer checking response.extensions["hishel_from_cache"] after requests when possible. httpx-frugal pins hishel>=1.2,<2.
Development
uv sync
uv run ruff check .
uv run pytest
License
MIT
Release files for httpx-frugal 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| httpx_frugal-0.2.1.tar.gz | 49.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| httpx_frugal-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:61.3 kB
Release files / httpx_frugal-0.2.1.tar.gz
| Download URL | httpx_frugal-0.2.1.tar.gz |
|---|---|
| Size | 49.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ea9d8e04b399ac237f10616ec4f6afb2612735afae6ab5cb20107c034b2c9e99
|
|
BLAKE2b-256 checksum How to use checksums |
3c339ee63e8dc3bd8e03ce4b1b5281557fa9b8dd0c3e3b62fe66071c6952588f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.4
|
Release files / httpx_frugal-0.2.1-py3-none-any.whl
| Download URL | httpx_frugal-0.2.1-py3-none-any.whl |
|---|---|
| Size | 11.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1851e437bf471fc23df336a048757a0033c15a57c362e72b04d2b163e3b12e06
|
|
BLAKE2b-256 checksum How to use checksums |
658cabd2d99e899e7b94168c1f3e5c5f63fe94f33fb47a69ddabaa36193f4ef1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.4
|