apidepth 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,41 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ name: Lint and test
12
+ runs-on: ubuntu-latest
13
+
14
+ strategy:
15
+ matrix:
16
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Checkout collector (SSRF fixture)
22
+ uses: actions/checkout@v4
23
+ with:
24
+ repository: cmwright33/apidepth-collector
25
+ token: ${{ secrets.GH_PAT }}
26
+ path: apidepth-collector
27
+ sparse-checkout: tests/fixtures
28
+ sparse-checkout-cone-mode: false
29
+
30
+ - uses: actions/setup-python@v5
31
+ with:
32
+ python-version: ${{ matrix.python-version }}
33
+
34
+ - name: Install dev dependencies
35
+ run: pip install -e ".[dev]"
36
+
37
+ - name: Lint
38
+ run: ruff check .
39
+
40
+ - name: Tests
41
+ run: pytest
@@ -0,0 +1,38 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+ *.egg
8
+ *.egg-info/
9
+ dist/
10
+ build/
11
+ .eggs/
12
+ *.whl
13
+
14
+ # Virtual environments
15
+ .venv/
16
+ venv/
17
+ env/
18
+ .env
19
+
20
+ # Testing
21
+ .pytest_cache/
22
+ .coverage
23
+ htmlcov/
24
+ .tox/
25
+
26
+ # Type checking
27
+ .mypy_cache/
28
+ .ruff_cache/
29
+
30
+ # Editor
31
+ .DS_Store
32
+ .idea/
33
+ .vscode/
34
+ *.swp
35
+ *.swo
36
+
37
+ # Runtime
38
+ /tmp/apidepth_registry.json
apidepth-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Apidepth
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,282 @@
1
+ Metadata-Version: 2.4
2
+ Name: apidepth
3
+ Version: 0.1.0
4
+ Summary: Apidepth SDK for Python — track outbound API latency, error rates, and rate limit quota.
5
+ Project-URL: Homepage, https://www.apidepth.io
6
+ Project-URL: Source, https://github.com/apidepth/apidepth-python
7
+ Project-URL: Issues, https://github.com/apidepth/apidepth-python/issues
8
+ License: MIT License
9
+
10
+ Copyright (c) 2026 Apidepth
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Keywords: api,apidepth,http,monitoring,observability
31
+ Classifier: Development Status :: 4 - Beta
32
+ Classifier: Framework :: Django
33
+ Classifier: Framework :: Flask
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.9
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Programming Language :: Python :: 3.13
43
+ Classifier: Topic :: Software Development :: Libraries
44
+ Classifier: Topic :: System :: Monitoring
45
+ Classifier: Typing :: Typed
46
+ Requires-Python: >=3.9
47
+ Provides-Extra: all
48
+ Requires-Dist: django>=3.2; extra == 'all'
49
+ Requires-Dist: flask>=2.0; extra == 'all'
50
+ Requires-Dist: httpx>=0.23; extra == 'all'
51
+ Requires-Dist: requests>=2.20; extra == 'all'
52
+ Provides-Extra: dev
53
+ Requires-Dist: httpx>=0.23; extra == 'dev'
54
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
55
+ Requires-Dist: pytest>=8.0; extra == 'dev'
56
+ Requires-Dist: requests>=2.20; extra == 'dev'
57
+ Requires-Dist: responses>=0.25; extra == 'dev'
58
+ Requires-Dist: respx>=0.21; extra == 'dev'
59
+ Requires-Dist: ruff>=0.4; extra == 'dev'
60
+ Provides-Extra: django
61
+ Requires-Dist: django>=3.2; extra == 'django'
62
+ Provides-Extra: flask
63
+ Requires-Dist: flask>=2.0; extra == 'flask'
64
+ Provides-Extra: httpx
65
+ Requires-Dist: httpx>=0.23; extra == 'httpx'
66
+ Provides-Extra: requests
67
+ Requires-Dist: requests>=2.20; extra == 'requests'
68
+ Description-Content-Type: text/markdown
69
+
70
+ # apidepth-python
71
+
72
+ Track outbound API latency, error rates, and rate limit quota across your third-party vendors — Stripe, OpenAI, Anthropic, Twilio, GitHub, and more.
73
+
74
+ Zero config for supported vendors. No code changes to your existing HTTP calls.
75
+
76
+ ---
77
+
78
+ ## Installation
79
+
80
+ ```bash
81
+ pip install apidepth
82
+ ```
83
+
84
+ For `requests` instrumentation (most common):
85
+ ```bash
86
+ pip install "apidepth[requests]"
87
+ ```
88
+
89
+ For `httpx` instrumentation:
90
+ ```bash
91
+ pip install "apidepth[httpx]"
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Quick start
97
+
98
+ ### Django
99
+
100
+ Add to `INSTALLED_APPS` and configure in `settings.py`:
101
+
102
+ ```python
103
+ INSTALLED_APPS = [
104
+ ...
105
+ "apidepth.integrations.django",
106
+ ]
107
+
108
+ APIDEPTH = {
109
+ "api_key": env("APIDEPTH_API_KEY"),
110
+ "environment": env("DJANGO_ENV", default="development"),
111
+ }
112
+ ```
113
+
114
+ ### Flask
115
+
116
+ ```python
117
+ from flask import Flask
118
+ from apidepth.integrations.flask import Apidepth
119
+
120
+ app = Flask(__name__)
121
+ app.config["APIDEPTH_API_KEY"] = os.environ["APIDEPTH_API_KEY"]
122
+ app.config["APIDEPTH_ENVIRONMENT"] = "production"
123
+
124
+ Apidepth(app)
125
+ ```
126
+
127
+ ### Standalone / scripts
128
+
129
+ ```python
130
+ import apidepth
131
+ from apidepth import registry_loader
132
+
133
+ apidepth.configure(
134
+ api_key=os.environ["APIDEPTH_API_KEY"],
135
+ environment="production",
136
+ )
137
+ apidepth.instrument() # call before any outbound HTTP
138
+ registry_loader.load_and_start() # loads remote vendor registry + starts refresh thread
139
+
140
+ import requests
141
+ resp = requests.get("https://api.stripe.com/v1/charges/ch_abc123", ...)
142
+ ```
143
+
144
+ `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.
145
+
146
+ For **Gunicorn / uWSGI**, call `Collector.register_fork_safety()` once before the server forks so each worker gets its own flush thread:
147
+
148
+ ```python
149
+ from apidepth.collector import Collector
150
+ Collector.register_fork_safety()
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Configuration
156
+
157
+ | Option | Default | Description |
158
+ |---|---|---|
159
+ | `api_key` | `None` | **Required.** Your Apidepth API key. |
160
+ | `environment` | `None` | Deployment environment tag, e.g. `"production"`. |
161
+ | `enabled` | `True` | Set `False` to disable all instrumentation. |
162
+ | `sample_rate` | `1.0` | Float 0.0–1.0. Fraction of requests to capture. |
163
+ | `ignored_hosts` | `[]` | List of hostnames to never record. |
164
+ | `extra_vendors` | `{}` | Map `{"vendor-name": "host"}` for in-house APIs. |
165
+ | `flush_interval` | `20` | Background flush interval in seconds. |
166
+ | `registry_cache_path` | `/tmp/apidepth_registry.json` | Disk cache for the vendor registry. |
167
+ | `registry_refresh_interval` | `21600` | Registry refresh interval in seconds (6 h). |
168
+ | `on_flush_error` | `None` | `Callable(exc, ctx)` for routing errors to Sentry etc. |
169
+ | `collector_url` | production endpoint | Override for self-hosted collectors. |
170
+
171
+ ---
172
+
173
+ ## What gets captured
174
+
175
+ Every outbound HTTP request to a recognised vendor produces one event:
176
+
177
+ | Field | Example |
178
+ |---|---|
179
+ | `vendor` | `"stripe"` |
180
+ | `endpoint` | `"/v1/charges/:id"` |
181
+ | `method` | `"POST"` |
182
+ | `status` | `200` |
183
+ | `outcome` | `"success"` / `"client_error"` / `"server_error"` / `"timeout"` |
184
+ | `duration_ms` | `234` |
185
+ | `cold_start` | `false` (always — see [Known differences](#known-differences-from-the-ruby-gem)) |
186
+ | `env` | `"production"` |
187
+ | `ts` | `1747008000000` (epoch ms) |
188
+ | `rl_remaining` | `4999` (when rate limit headers present) |
189
+ | `rl_limit` | `5000` |
190
+ | `rl_reset_at` | `1747008060000` (epoch ms) |
191
+
192
+ **Never captured:** request/response bodies, headers, query parameters, credentials.
193
+
194
+ ---
195
+
196
+ ## Supported vendors
197
+
198
+ | Vendor | Host |
199
+ |---|---|
200
+ | Stripe | `api.stripe.com` |
201
+ | OpenAI | `api.openai.com` |
202
+ | Anthropic | `api.anthropic.com` |
203
+ | Twilio | `api.twilio.com` |
204
+ | Resend | `api.resend.com` |
205
+ | GitHub | `api.github.com` |
206
+
207
+ Additional vendors are loaded from the remote registry every 6 hours.
208
+
209
+ ### Custom vendors
210
+
211
+ ```python
212
+ apidepth.configure(
213
+ extra_vendors={"payments-api": "api.payments.internal"},
214
+ )
215
+ ```
216
+
217
+ ---
218
+
219
+ ## Rate limit tracking
220
+
221
+ The SDK extracts quota state from response headers and includes it in every event.
222
+ Supported header families (checked in priority order):
223
+
224
+ - **OpenAI / Anthropic**: `x-ratelimit-remaining-requests`, `x-ratelimit-limit-requests`, `x-ratelimit-reset-requests`
225
+ - **GitHub**: `x-ratelimit-remaining`, `x-ratelimit-limit`, `x-ratelimit-reset`
226
+ - **IETF draft / HubSpot**: `ratelimit-remaining`, `ratelimit-limit`, `ratelimit-reset`
227
+ - **Stripe / generic 429**: `retry-after`
228
+
229
+ Reset values are normalised to epoch milliseconds regardless of the source format (Unix timestamp, seconds-from-now, OpenAI duration strings like `"1m30s"`).
230
+
231
+ ---
232
+
233
+ ## Debugging
234
+
235
+ ```python
236
+ from apidepth.collector import Collector
237
+
238
+ print(Collector.instance().stats())
239
+ # {
240
+ # 'queue_size': 0,
241
+ # 'consecutive_failures': 0,
242
+ # 'total_dropped': 0,
243
+ # 'last_flush_at': 1747008000.123,
244
+ # }
245
+ ```
246
+
247
+ ---
248
+
249
+ ## Framework compatibility
250
+
251
+ | Framework | Version | Integration |
252
+ |---|---|---|
253
+ | Django | 3.2+ | `apidepth.integrations.django` in `INSTALLED_APPS` |
254
+ | Flask | 2.0+ | `Apidepth(app)` |
255
+ | FastAPI / Starlette | any | Call `apidepth.instrument()` at startup |
256
+ | Scripts / workers | — | Call `apidepth.instrument()` at startup |
257
+
258
+ ---
259
+
260
+ ## Python compatibility
261
+
262
+ Python 3.9–3.13. No required runtime dependencies (stdlib only). `requests` and `httpx` are optional instrumentation targets detected at runtime.
263
+
264
+ ---
265
+
266
+ ## Known differences from the Ruby gem
267
+
268
+ ### `cold_start` is always `false`
269
+
270
+ 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.
271
+
272
+ 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`.
273
+
274
+ **Practical impact depends on your traffic pattern:**
275
+
276
+ | Traffic pattern | Impact |
277
+ |---|---|
278
+ | High-throughput web service | **Negligible** — cold starts are a tiny fraction of total requests; percentile inflation is unmeasurable |
279
+ | 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 |
280
+ | 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 |
281
+
282
+ 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.
@@ -0,0 +1,213 @@
1
+ # apidepth-python
2
+
3
+ Track outbound API latency, error rates, and rate limit quota across your third-party vendors — Stripe, OpenAI, Anthropic, Twilio, GitHub, and more.
4
+
5
+ Zero config for supported vendors. No code changes to your existing HTTP calls.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install apidepth
13
+ ```
14
+
15
+ For `requests` instrumentation (most common):
16
+ ```bash
17
+ pip install "apidepth[requests]"
18
+ ```
19
+
20
+ For `httpx` instrumentation:
21
+ ```bash
22
+ pip install "apidepth[httpx]"
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Quick start
28
+
29
+ ### Django
30
+
31
+ Add to `INSTALLED_APPS` and configure in `settings.py`:
32
+
33
+ ```python
34
+ INSTALLED_APPS = [
35
+ ...
36
+ "apidepth.integrations.django",
37
+ ]
38
+
39
+ APIDEPTH = {
40
+ "api_key": env("APIDEPTH_API_KEY"),
41
+ "environment": env("DJANGO_ENV", default="development"),
42
+ }
43
+ ```
44
+
45
+ ### Flask
46
+
47
+ ```python
48
+ from flask import Flask
49
+ from apidepth.integrations.flask import Apidepth
50
+
51
+ app = Flask(__name__)
52
+ app.config["APIDEPTH_API_KEY"] = os.environ["APIDEPTH_API_KEY"]
53
+ app.config["APIDEPTH_ENVIRONMENT"] = "production"
54
+
55
+ Apidepth(app)
56
+ ```
57
+
58
+ ### Standalone / scripts
59
+
60
+ ```python
61
+ import apidepth
62
+ from apidepth import registry_loader
63
+
64
+ apidepth.configure(
65
+ api_key=os.environ["APIDEPTH_API_KEY"],
66
+ environment="production",
67
+ )
68
+ apidepth.instrument() # call before any outbound HTTP
69
+ registry_loader.load_and_start() # loads remote vendor registry + starts refresh thread
70
+
71
+ import requests
72
+ resp = requests.get("https://api.stripe.com/v1/charges/ch_abc123", ...)
73
+ ```
74
+
75
+ `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.
76
+
77
+ For **Gunicorn / uWSGI**, call `Collector.register_fork_safety()` once before the server forks so each worker gets its own flush thread:
78
+
79
+ ```python
80
+ from apidepth.collector import Collector
81
+ Collector.register_fork_safety()
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Configuration
87
+
88
+ | Option | Default | Description |
89
+ |---|---|---|
90
+ | `api_key` | `None` | **Required.** Your Apidepth API key. |
91
+ | `environment` | `None` | Deployment environment tag, e.g. `"production"`. |
92
+ | `enabled` | `True` | Set `False` to disable all instrumentation. |
93
+ | `sample_rate` | `1.0` | Float 0.0–1.0. Fraction of requests to capture. |
94
+ | `ignored_hosts` | `[]` | List of hostnames to never record. |
95
+ | `extra_vendors` | `{}` | Map `{"vendor-name": "host"}` for in-house APIs. |
96
+ | `flush_interval` | `20` | Background flush interval in seconds. |
97
+ | `registry_cache_path` | `/tmp/apidepth_registry.json` | Disk cache for the vendor registry. |
98
+ | `registry_refresh_interval` | `21600` | Registry refresh interval in seconds (6 h). |
99
+ | `on_flush_error` | `None` | `Callable(exc, ctx)` for routing errors to Sentry etc. |
100
+ | `collector_url` | production endpoint | Override for self-hosted collectors. |
101
+
102
+ ---
103
+
104
+ ## What gets captured
105
+
106
+ Every outbound HTTP request to a recognised vendor produces one event:
107
+
108
+ | Field | Example |
109
+ |---|---|
110
+ | `vendor` | `"stripe"` |
111
+ | `endpoint` | `"/v1/charges/:id"` |
112
+ | `method` | `"POST"` |
113
+ | `status` | `200` |
114
+ | `outcome` | `"success"` / `"client_error"` / `"server_error"` / `"timeout"` |
115
+ | `duration_ms` | `234` |
116
+ | `cold_start` | `false` (always — see [Known differences](#known-differences-from-the-ruby-gem)) |
117
+ | `env` | `"production"` |
118
+ | `ts` | `1747008000000` (epoch ms) |
119
+ | `rl_remaining` | `4999` (when rate limit headers present) |
120
+ | `rl_limit` | `5000` |
121
+ | `rl_reset_at` | `1747008060000` (epoch ms) |
122
+
123
+ **Never captured:** request/response bodies, headers, query parameters, credentials.
124
+
125
+ ---
126
+
127
+ ## Supported vendors
128
+
129
+ | Vendor | Host |
130
+ |---|---|
131
+ | Stripe | `api.stripe.com` |
132
+ | OpenAI | `api.openai.com` |
133
+ | Anthropic | `api.anthropic.com` |
134
+ | Twilio | `api.twilio.com` |
135
+ | Resend | `api.resend.com` |
136
+ | GitHub | `api.github.com` |
137
+
138
+ Additional vendors are loaded from the remote registry every 6 hours.
139
+
140
+ ### Custom vendors
141
+
142
+ ```python
143
+ apidepth.configure(
144
+ extra_vendors={"payments-api": "api.payments.internal"},
145
+ )
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Rate limit tracking
151
+
152
+ The SDK extracts quota state from response headers and includes it in every event.
153
+ Supported header families (checked in priority order):
154
+
155
+ - **OpenAI / Anthropic**: `x-ratelimit-remaining-requests`, `x-ratelimit-limit-requests`, `x-ratelimit-reset-requests`
156
+ - **GitHub**: `x-ratelimit-remaining`, `x-ratelimit-limit`, `x-ratelimit-reset`
157
+ - **IETF draft / HubSpot**: `ratelimit-remaining`, `ratelimit-limit`, `ratelimit-reset`
158
+ - **Stripe / generic 429**: `retry-after`
159
+
160
+ Reset values are normalised to epoch milliseconds regardless of the source format (Unix timestamp, seconds-from-now, OpenAI duration strings like `"1m30s"`).
161
+
162
+ ---
163
+
164
+ ## Debugging
165
+
166
+ ```python
167
+ from apidepth.collector import Collector
168
+
169
+ print(Collector.instance().stats())
170
+ # {
171
+ # 'queue_size': 0,
172
+ # 'consecutive_failures': 0,
173
+ # 'total_dropped': 0,
174
+ # 'last_flush_at': 1747008000.123,
175
+ # }
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Framework compatibility
181
+
182
+ | Framework | Version | Integration |
183
+ |---|---|---|
184
+ | Django | 3.2+ | `apidepth.integrations.django` in `INSTALLED_APPS` |
185
+ | Flask | 2.0+ | `Apidepth(app)` |
186
+ | FastAPI / Starlette | any | Call `apidepth.instrument()` at startup |
187
+ | Scripts / workers | — | Call `apidepth.instrument()` at startup |
188
+
189
+ ---
190
+
191
+ ## Python compatibility
192
+
193
+ Python 3.9–3.13. No required runtime dependencies (stdlib only). `requests` and `httpx` are optional instrumentation targets detected at runtime.
194
+
195
+ ---
196
+
197
+ ## Known differences from the Ruby gem
198
+
199
+ ### `cold_start` is always `false`
200
+
201
+ 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.
202
+
203
+ 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`.
204
+
205
+ **Practical impact depends on your traffic pattern:**
206
+
207
+ | Traffic pattern | Impact |
208
+ |---|---|
209
+ | High-throughput web service | **Negligible** — cold starts are a tiny fraction of total requests; percentile inflation is unmeasurable |
210
+ | 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 |
211
+ | 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 |
212
+
213
+ 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.