apidepth-python
Track outbound API latency, error rates, and rate limit quota across your third-party vendors — Stripe, OpenAI, Anthropic, Twilio, GitHub, and more.
Zero config for supported vendors. No code changes to your existing HTTP calls.
Installation
pip install apidepth
For requests instrumentation (most common):
pip install "apidepth[requests]"
For httpx instrumentation:
pip install "apidepth[httpx]"
Quick start
Django
Add to INSTALLED_APPS and configure in settings.py:
INSTALLED_APPS = [
...
"apidepth.integrations.django",
]
APIDEPTH = {
"api_key": env("APIDEPTH_API_KEY"),
"environment": env("DJANGO_ENV", default="development"),
}
Flask
from flask import Flask
from apidepth.integrations.flask import Apidepth
app = Flask(__name__)
app.config["APIDEPTH_API_KEY"] = os.environ["APIDEPTH_API_KEY"]
app.config["APIDEPTH_ENVIRONMENT"] = "production"
Apidepth(app)
Standalone / scripts
import apidepth
from apidepth import registry_loader
apidepth.configure(
api_key=os.environ["APIDEPTH_API_KEY"],
environment="production",
)
apidepth.instrument() # call before any outbound HTTP
registry_loader.load_and_start() # loads remote vendor registry + starts refresh thread
import requests
resp = requests.get("https://api.stripe.com/v1/charges/ch_abc123", ...)
load_and_start() fetches the latest vendor registry from the network (with a local disk cache fallback) and starts a background refresh thread. Without it, only the six bundled vendors are recognised. Django and Flask integrations call this automatically.
For Gunicorn / uWSGI, call Collector.register_fork_safety() once before the server forks so each worker gets its own flush thread:
from apidepth.collector import Collector
Collector.register_fork_safety()
Configuration
| Option | Default | Description |
|---|---|---|
api_key |
None |
Required. Your Apidepth API key. |
environment |
None |
Deployment environment tag, e.g. "production". |
enabled |
True |
Set False to disable all instrumentation. |
sample_rate |
1.0 |
Float 0.0–1.0. Fraction of requests to capture. |
ignored_hosts |
[] |
List of hostnames to never record. |
extra_vendors |
{} |
Map {"vendor-name": "host"} for in-house APIs. |
flush_interval |
20 |
Background flush interval in seconds. |
registry_cache_path |
/tmp/apidepth_registry.json |
Disk cache for the vendor registry. |
registry_refresh_interval |
21600 |
Registry refresh interval in seconds (6 h). |
on_flush_error |
None |
Callable(exc, ctx) for routing errors to Sentry etc. |
collector_url |
production endpoint | Override for self-hosted collectors. |
What gets captured
Every outbound HTTP request to a recognised vendor produces one event:
| Field | Example |
|---|---|
vendor |
"stripe" |
endpoint |
"/v1/charges/:id" |
method |
"POST" |
status |
200 |
outcome |
"success" / "client_error" / "server_error" / "timeout" |
duration_ms |
234 |
cold_start |
false (always — see Known differences) |
env |
"production" |
ts |
1747008000000 (epoch ms) |
rl_remaining |
4999 (when rate limit headers present) |
rl_limit |
5000 |
rl_reset_at |
1747008060000 (epoch ms) |
Never captured: request/response bodies, headers, query parameters, credentials.
Supported vendors
| Vendor | Host |
|---|---|
| Stripe | api.stripe.com |
| OpenAI | api.openai.com |
| Anthropic | api.anthropic.com |
| Twilio | api.twilio.com |
| Resend | api.resend.com |
| GitHub | api.github.com |
Additional vendors are loaded from the remote registry every 6 hours.
Custom vendors
apidepth.configure(
extra_vendors={"payments-api": "api.payments.internal"},
)
Rate limit tracking
The SDK extracts quota state from response headers and includes it in every event. Supported header families (checked in priority order):
- OpenAI / Anthropic:
x-ratelimit-remaining-requests,x-ratelimit-limit-requests,x-ratelimit-reset-requests - GitHub:
x-ratelimit-remaining,x-ratelimit-limit,x-ratelimit-reset - IETF draft / HubSpot:
ratelimit-remaining,ratelimit-limit,ratelimit-reset - Stripe / generic 429:
retry-after
Reset values are normalised to epoch milliseconds regardless of the source format (Unix timestamp, seconds-from-now, OpenAI duration strings like "1m30s").
Debugging
from apidepth.collector import Collector
print(Collector.instance().stats())
# {
# 'queue_size': 0,
# 'consecutive_failures': 0,
# 'total_dropped': 0,
# 'last_flush_at': 1747008000.123,
# }
Framework compatibility
| Framework | Version | Integration |
|---|---|---|
| Django | 3.2+ | apidepth.integrations.django in INSTALLED_APPS |
| Flask | 2.0+ | Apidepth(app) |
| FastAPI / Starlette | any | Call apidepth.instrument() at startup |
| Scripts / workers | — | Call apidepth.instrument() at startup |
Python compatibility
Python 3.9–3.13. No required runtime dependencies (stdlib only). requests and httpx are optional instrumentation targets detected at runtime.
Known differences from the Ruby gem
cold_start is always false
The Ruby gem tags the first outbound request on each TCP connection with cold_start: true using Net::HTTP#started?. The Apidepth collector uses this flag to exclude DNS + TCP + TLS handshake overhead from latency percentile calculations (p50/p95/p99), keeping those metrics representative of steady-state vendor performance.
Neither requests (backed by urllib3) nor httpx exposes a public API for detecting whether the underlying socket is a keep-alive reuse. The Python SDK therefore always sends cold_start: false.
Practical impact depends on your traffic pattern:
| Traffic pattern | Impact |
|---|---|
| High-throughput web service | Negligible — cold starts are a tiny fraction of total requests; percentile inflation is unmeasurable |
| Low-throughput service / cron job | Noticeable — the first request per run pays ~50–200 ms of connection overhead that isn't excluded from percentiles; p95/p99 may read slightly higher than in Ruby |
| Serverless / short-lived worker | Material — every invocation starts cold; all latency data includes connection overhead; comparisons against Ruby-instrumented services will show the Python side as systematically higher |
The raw duration values are accurate — only the percentile statistics are affected. If cold-start exclusion matters for your environment, filter those events manually in your dashboard (e.g. a warm-up request flag in thread-local state) until the underlying libraries expose the required connection-state API.
Metadata
Release files for apidepth 0.1.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 | |
|---|---|---|---|
| apidepth-0.1.1.tar.gz | 41.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| apidepth-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 83.4 kB
Release files / apidepth-0.1.1.tar.gz
| Download URL | apidepth-0.1.1.tar.gz |
|---|---|
| Size | 41.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ab1f3c5fc81a2fefbbdb47ad38f9251c584027e26929d725397cb48d1f097fa8
|
|
BLAKE2b-256 checksum How to use checksums |
4f98f9dc7a31be67ebecfade67ed2d9af8c989c1e6b9edfdfee7034c7d378ac9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.11
|
Release files / apidepth-0.1.1-py3-none-any.whl
| Download URL | apidepth-0.1.1-py3-none-any.whl |
|---|---|
| Size | 41.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
389ade4333d3b28ace48e549cf347738f88d8db9cdf0645cddf9634b66b91e83
|
|
BLAKE2b-256 checksum How to use checksums |
8b913021cc027cd56c4764b644ffee370b9df0510b90080b9920b128c3302663
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.11
|