polite-http
A courteous, dependency-free HTTP client for Python. It plays nice with rate-limited APIs out of the box:
- Per-host rate limiting — cross-process, via a shared file lock.
- Automatic retries on transient errors (HTTP 429, 5xx) and network errors.
- Exponential backoff with optional jitter.
Retry-Aftersupport (server-directed backoff, both seconds and HTTP-date forms).X-Throttling-Controlproactive backpressure (as used by PubChem / NCBI).- Streaming helpers for large line-oriented and binary responses.
- Zero third-party dependencies — built entirely on the standard library
(
urllib.request).
Installation
pip install polite-http
Requires Python 3.9+. Cross-process rate limiting works on Linux, macOS, and
Windows — it uses fcntl on POSIX and msvcrt on Windows for the shared file
lock (both standard library). On the rare platform that provides neither, it
falls back to a best-effort in-process timer.
Quick start
from polite_http import HttpClient
# Scope a client to a base URL and a steady-state rate of 3 requests/second.
client = HttpClient("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/", qps=3)
# GET + parse JSON (relative paths are resolved against the base URL).
data = client.fetch_json("esummary.fcgi?db=pubmed&id=123456")
# POST a JSON body.
result = client.fetch_json(
"esearch.fcgi",
method="POST",
json_body={"db": "pubmed", "term": "cancer"},
)
# Download raw bytes with a custom timeout.
pdf = client.fetch_bytes(
"efetch.fcgi?db=pubmed&id=123456&rettype=abstract",
timeout=60,
)
Streaming
# Stream a large response line-by-line without buffering it all in memory.
for line in client.stream_lines("large-export.tsv"):
process(line)
# Stream binary content in chunks (e.g. to a file).
with open("paper.pdf", "wb") as f:
for chunk in client.stream_bytes("paper.pdf"):
f.write(chunk)
Configuration
HttpClient accepts the following keyword arguments:
| Argument | Default | Description |
|---|---|---|
qps |
required | Maximum queries per second (steady state). |
default_headers |
None |
Headers added to every request. |
max_retries |
7 |
Retry attempts for transient errors (total attempts = max_retries + 1). |
timeout |
60.0 |
Per-request timeout in seconds. |
backoff_base |
3.0 |
Base delay for exponential backoff. |
backoff_max |
180.0 |
Cap on backoff delay. |
jitter |
0.5 |
Max uniform random jitter added to each backoff. |
user_agent |
env / "" |
User-Agent header; falls back to POLITE_HTTP_USER_AGENT. |
retryable_status_codes |
{429, 500, 502, 503, 504} |
Status codes that trigger a retry. |
referer |
None |
Optional Referer header sent with every request. |
Environment variables
POLITE_HTTP_USER_AGENT— defaultUser-Agentwhen one isn't passed explicitly. Many APIs (e.g. NCBI) reject requests without a descriptive User-Agent, so setting this is recommended.POLITE_HTTP_LOCK_DIR— directory for the cross-process rate-limit lock files (defaults to the system temp directory).
Error handling
Failed requests raise HttpError, which carries the status_code, raw body
bytes, and url:
from polite_http import HttpClient, HttpError
client = HttpClient("https://api.example.com/", qps=5)
try:
data = client.fetch_json("widgets/42")
except HttpError as exc:
print(exc.status_code, exc.url)
if exc.body:
print(exc.json()) # parse the error body as JSON, if applicable
Acknowledgements
The HTTP client at the heart of this package is derived from the
science-skills project by
Google DeepMind, used under the Apache License 2.0. See NOTICE for
details of the changes.
License
Metadata
Release files for polite-http 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| polite_http-0.1.3.tar.gz | 21.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| polite_http-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.3 kB
Release files / polite_http-0.1.3.tar.gz
| Download URL | polite_http-0.1.3.tar.gz |
|---|---|
| Size | 21.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7c89c3dfc1539a1aaceb39590452679dfcb218878843f749d3a1a1eb8239cd01
|
|
BLAKE2b-256 checksum How to use checksums |
32e9456f0468ef68f45ad1ed128e2eb21f7947f65086388004e04f66563cbee6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency logRelease files / polite_http-0.1.3-py3-none-any.whl
| Download URL | polite_http-0.1.3-py3-none-any.whl |
|---|---|
| Size | 19.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c6564299087a6635d62b45ecfdf3d2a8c18fd172a939708e0006b426b3cd1b61
|
|
BLAKE2b-256 checksum How to use checksums |
697fe07c5398684a1031643f0ce500298265ab8693495a63a800b3f33bfc69c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency log