Skip to content

HTTP client

standard-http provides an asynchronous HTTPX client under std.http. It keeps connection ownership, authentication, retries, and response validation in one small API.

Install

For an application, install the HTTP integration through its extra or directly:

python -m pip install 'standard[http]'
# or
python -m pip install standard-http

For development from this repository, install the core and HTTP projects together with python -m pip install -e . -e packages/standard-http. The provider distribution owns only std.http.

Make a request

from std.http import HttpClient

async with HttpClient(base_url="https://api.example.com/v1/") as client:
    response = await client.get("users", params={"active": "true"})
    response.raise_for_status()
    users = response.json()

HttpClient supports request, get, post, put, patch, and delete. Each method accepts HTTPX request options such as params, json, data, files, and per-request timeout. Relative URLs are joined with base_url. Per-request headers override defaults case-insensitively. A request returns 4xx and 5xx responses as-is; call raise_for_status() when the caller wants HTTP status errors.

For advanced connection-pool access, await client.client returns the underlying httpx.AsyncClient. The constructor's transport option accepts an HTTPX async transport such as httpx.MockTransport, which is useful for testing integrations without a network service.

Authenticate

from std.http import ApiKeyAuth, AuthConfig, BasicAuth, BearerAuth, HttpClient

client = HttpClient(
    base_url="https://api.example.com",
    auth_config=AuthConfig(auth=BearerAuth("token")),
)

BearerAuth sets Authorization: Bearer …. BasicAuth encodes username and password as UTF-8 before Base64 encoding. ApiKeyAuth uses X-API-Key by default and can target a custom header or add a prefix. All three redact their secret values from repr() output.

Native httpx.Auth implementations and custom AuthBase subclasses are also accepted. A custom handler implements auth_flow(request) and returns the request after adding authentication.

For a refreshable bearer or API-key handler, supply a synchronous callback that returns a new secret. It runs once after a 401 response, then the request is retried if its method and body are replayable and the retry budget permits it:

auth = AuthConfig(
    auth=BearerAuth("expired-token"),
    refresh_token="refresh-token",
    refresh_callback=refresh_access_token,
)

The callback takes no arguments. It can close over refresh_token or another credential source. Callback errors produce a sanitized HttpClientError; the callback's exception text is not included.

Retries and timeouts

from std.http import HttpClient, RetryConfig

retries = RetryConfig(
    max_attempts=4,
    retry_statuses=[429, 500, 502, 503, 504],
    retry_methods=["GET", "HEAD"],
    backoff_factor=0.25,
    jitter=True,
)
client = HttpClient(base_url="https://api.example.com", timeout=5, retries=retries)

max_attempts counts the initial request. The integer retries constructor form is retained for compatibility and also means total attempts; zero means one attempt. By default, the client retries transport errors and the listed server statuses for GET, HEAD, PUT, DELETE, OPTIONS, and TRACE. It does not retry a streamed or otherwise non-replayable request body. Backoff doubles per attempt, is capped at 60 seconds, and adds bounded jitter when enabled. retry_exceptions adds exception classes on top of the default HTTPX transport-error handling.

Timeout accepts a number of seconds, an httpx.Timeout, or None to disable the default timeout. Per-request timeout options follow HTTPX behavior.

Validate a response model

from pydantic import BaseModel
from std.http import HttpClient


class User(BaseModel):
    name: str
    age: int


async with HttpClient(base_url="https://api.example.com") as client:
    user = await client.get_model("users/42", User)

The client provides get_model, post_model, put_model, patch_model, and delete_model. These helpers require a successful status and valid JSON that matches the Pydantic model. Failures raise HttpClientError with the status and response attached when available.

Errors and cleanup

HttpClientError exposes status_code and response when available. Its message and retry logs omit URL, query, header, credential, and underlying exception text so secrets are not copied into logs. Network and timeout errors are wrapped after the configured attempts; HTTP status responses remain ordinary responses for non-model methods.

Use async with for deterministic cleanup. close() is idempotent and closes the connection pool; the next request can lazily create a fresh HTTPX client.

Tests

The package tests use HTTPX MockTransport to exercise request construction, auth, retries, error paths, and cleanup without depending on public services.