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.typedmarker, strict-mypy clean,Final/Protocol/frozendataclassthroughout. - 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 dict — build_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.headersexists and has anupdate(mapping)method (forapply_default_headers), or- the HTTP library accepts a
headers=kwarg that is a plaindict[str, str](forbuild_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
Release history Release notifications | RSS feed
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 source_identity_http-0.1.0.dev2.tar.gz.
File metadata
- Download URL: source_identity_http-0.1.0.dev2.tar.gz
- Upload date:
- Size: 5.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83d2428c7c00a7d7ec2fa1241360b812c276e9f0a3db65fbe39e458a34046754
|
|
| MD5 |
1c788ee730a8cd51d1578b9644ebb0fb
|
|
| BLAKE2b-256 |
4a8a269eba088afc72066e4961505a174c92b006aefdbb1777a2ad838f03fa66
|
File details
Details for the file source_identity_http-0.1.0.dev2-py3-none-any.whl.
File metadata
- Download URL: source_identity_http-0.1.0.dev2-py3-none-any.whl
- Upload date:
- Size: 6.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
988a61703587be63c9eeead9004764e6bf855fd0b09d7f2bc24ab4fce0534da0
|
|
| MD5 |
1e229b12661fff3b19abac1399f8288b
|
|
| BLAKE2b-256 |
5848efa978e63bc9172dbf54d5cbebf5c688fa4223414374a7c4468a64b0c27b
|