Skip to main content

Typed, zero-dependency caller-identity and trace headers (X-Source-ID, X-Trace-ID, ...) for httpx, requests, and any HTTP client with .headers

Project description

source-identity-http

Tiny, zero-dependency (stdlib only), typed helpers to attach caller-identity and distributed-trace headers — X-Source-ID, X-Source-Version, X-Source-Team, X-Trace-ID, X-Request-ID — to outbound HTTP requests made with httpx, requests, or any client that exposes a mutable .headers mapping.

  • No runtime deps. Pure stdlib. Pulls in nothing.
  • Typed. py.typed marker, strict-mypy clean, Final/Protocol/frozen dataclass throughout.
  • Library-agnostic. Works with httpx (sync + async), requests, urllib3-based clients, or anything with .headers.update(...).
  • Three ergonomics. Build a raw dict, merge into call kwargs, or set defaults on a session/client.

Why

In a microservice architecture, knowing who called what, and which request that call belongs to, is the difference between a five-minute incident and a five-hour one. The conventions for surfacing this on the wire are well established:

Header Meaning
X-Source-ID Name of the calling service (e.g. order-service)
X-Source-Version Version of the calling service (e.g. 2.1.0)
X-Source-Team Team or org owning the caller (e.g. platform)
X-Trace-ID Correlation id propagated across hops
X-Request-ID Same as X-Trace-ID here (sent for compatibility)

This package gives you a single, typed way to set them — without each service hand-rolling its own header plumbing, and without dragging in a heavy observability framework.

It plays well with:

  • Reverse proxies / call trackers (e.g. Traefik with a call-tracker middleware) that aggregate X-Source-* to draw service-dependency graphs.
  • APM / log pipelines that group logs by X-Trace-ID / X-Request-ID.
  • Anything that just wants a consistent "who is calling" header on every outbound call.

Install

pip install source-identity-http

Requires Python 3.10+. No runtime dependencies.

Usage

1. One-shot calls — merge_http_kwargs

Merges identity headers into the headers= slot of any call-style kwargs dict. Safe to use with both httpx and requests module-level helpers:

import httpx, requests
from source_identity_http import SourceIdentity, merge_http_kwargs

identity = SourceIdentity(
    source_id="order-service",
    version="2.1.0",
    team="platform",
    trace_id="abc-123",
)

r = httpx.get("https://api.example/v1", **merge_http_kwargs({}, identity))

r = requests.post(
    "https://api.example/v1",
    **merge_http_kwargs({"json": {"foo": "bar"}}, identity),
)

Any existing headers= you pass in is preserved; identity headers are merged on top (the identity wins on key conflicts — that's intentional, so a caller can't accidentally spoof its own source id).

2. Default headers on a long-lived client — apply_default_headers

For httpx.Client / httpx.AsyncClient / requests.Session, set the headers once and forget:

import httpx
from source_identity_http import SourceIdentity, apply_default_headers

identity = SourceIdentity(source_id="billing-svc", team="risk")

async with httpx.AsyncClient() as client:
    apply_default_headers(client, identity)
    await client.get("https://upstream/health")
    await client.get("https://upstream/invoices")
    # Both requests carry the identity headers.

Per-request headers= on individual calls still merge on top in the usual library-specific way.

3. Raw header dictbuild_headers

For everything else (custom transport, urllib, ASGI test clients, an SDK that doesn't take a session, …):

from source_identity_http import SourceIdentity, build_headers

headers = build_headers(
    SourceIdentity(source_id="search-api", trace_id="t-9f2a"),
    extra={"Authorization": "Bearer ..."},
)
# headers -> {
#     "X-Source-ID": "search-api",
#     "X-Trace-ID":  "t-9f2a",
#     "X-Request-ID": "t-9f2a",
#     "Authorization": "Bearer ...",
# }

extra is merged last, so it can override non-required headers if you really need to (don't override X-Source-ID unless you know what you're doing).

API

Symbol Role
SourceIdentity Frozen dataclass: source_id (required), optional version, team, trace_id
build_headers Returns a dict[str, str] for headers=
merge_http_kwargs Copies a kwargs mapping and merges identity into its headers slot
apply_default_headers Mutates client.headers so every request includes identity headers
SupportsDefaultHeaders Protocol describing clients with a mutable .headers attribute
HEADER_SOURCE_ID "X-Source-ID"
HEADER_SOURCE_VERSION "X-Source-Version"
HEADER_SOURCE_TEAM "X-Source-Team"
HEADER_TRACE_ID "X-Trace-ID"
HEADER_REQUEST_ID "X-Request-ID"

trace_id, when set, is emitted as both X-Trace-ID and X-Request-ID — different upstreams pick one or the other, and you almost always want both.

Compatibility

Tested against httpx >= 0.27 and requests >= 2.31. The implementation only assumes:

  • client.headers exists and has an update(mapping) method (for apply_default_headers), or
  • the HTTP library accepts a headers= kwarg that is a plain dict[str, str] (for build_headers / merge_http_kwargs).

That covers essentially every popular Python HTTP client.

Layout

src/source_identity_http/
├── __init__.py   # public re-exports
├── core.py       # SourceIdentity dataclass + build_headers
├── kwargs.py     # merge_http_kwargs
├── client.py     # apply_default_headers + SupportsDefaultHeaders protocol
└── py.typed      # PEP 561 marker

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

source_identity_http-0.1.0.dev2.tar.gz (5.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

source_identity_http-0.1.0.dev2-py3-none-any.whl (6.7 kB view details)

Uploaded Python 3

File details

Details for the file source_identity_http-0.1.0.dev2.tar.gz.

File metadata

File hashes

Hashes for source_identity_http-0.1.0.dev2.tar.gz
Algorithm Hash digest
SHA256 83d2428c7c00a7d7ec2fa1241360b812c276e9f0a3db65fbe39e458a34046754
MD5 1c788ee730a8cd51d1578b9644ebb0fb
BLAKE2b-256 4a8a269eba088afc72066e4961505a174c92b006aefdbb1777a2ad838f03fa66

See more details on using hashes here.

File details

Details for the file source_identity_http-0.1.0.dev2-py3-none-any.whl.

File metadata

File hashes

Hashes for source_identity_http-0.1.0.dev2-py3-none-any.whl
Algorithm Hash digest
SHA256 988a61703587be63c9eeead9004764e6bf855fd0b09d7f2bc24ab4fce0534da0
MD5 1e229b12661fff3b19abac1399f8288b
BLAKE2b-256 5848efa978e63bc9172dbf54d5cbebf5c688fa4223414374a7c4468a64b0c27b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page