API latency — p50/p95/p99 by region
Rolling 30-day latency measurements for key portfolIQ endpoints, methodology, and regional bounds.
Disclaimer: Data provided by portfolIQ is factual and descriptive. It does not constitute financial advice or investment recommendations. Not financial advice. Methodology disclosed.
API latency — p50/p95/p99 by region
Last updated: Q2 2026 measurements (illustrative — replaced by GHA measurements from M1)
Latency table
Measurements are p50 / p95 / p99 in milliseconds, rolling 30-day window. All values measured from Hetzner DE server (Frankfurt), routed via Cloudflare edge.
| Endpoint | EU (p50/p95/p99) | US-East (p50/p95/p99) | Singapore (p50/p95/p99) |
|---|---|---|---|
GET /v1/assets/{id} | 45 / 89 / 120 ms | 78 / 140 / 210 ms | 115 / 180 / 280 ms |
GET /v1/assets/{id}/analysis | 52 / 98 / 135 ms | 85 / 150 / 225 ms | 120 / 195 / 300 ms |
GET /v1/assets (list, 100 items) | 68 / 130 / 185 ms | 98 / 175 / 260 ms | 135 / 210 / 320 ms |
GET /v1/market/overview | 38 / 75 / 105 ms | 72 / 130 / 195 ms | 110 / 170 / 265 ms |
GET /v1/ohlcv/{id} (30d) | 55 / 110 / 158 ms | 88 / 162 / 235 ms | 125 / 200 / 308 ms |
Note: These are illustrative values for M0. From M1, a GitHub Actions workflow measures real latency from three GHA-hosted runners (EU, US-East, Asia) on each release and updates this table automatically.
Physics bound — Singapore to EU
110 ms Singapore→EU is the speed-of-light bound incompressible without a local POP (COMITE-025 §2.5, network-academic recommendation).
The distance Frankfurt ↔ Singapore is ~9,800 km. At the speed of light in fiber (~200,000 km/s with realistic refraction index), the theoretical minimum round-trip is ~98 ms. Observed p50 of ~110–115 ms accounts for routing overhead and Cloudflare edge hops.
Implication for APAC users: Without a Singapore POP, p50 below 110 ms is physically impossible. The current architecture (single Hetzner DE server + Cloudflare global edge) delivers 110–120 ms p50 from Singapore — close to the physical limit for a single-region deployment.
Methodology
Measurement approach
- Tooling (target M1+): GitHub Actions cross-region runners (ubuntu-latest in
us-east-1,eu-west-1,ap-southeast-1) executingcurlwith--write-outtiming output againstapi.portfoliq.ioendpoints. - Reference: RIPE Atlas methodology — distributed probes, passive measurement, representative of real user network paths.
- Sample size: Minimum 100 requests per endpoint per region per measurement window.
- Window: Rolling 30 days, updated at each production deployment.
Outlier treatment — Tukey IQR
Outliers are removed using Tukey's fence method (IQR):
lower_fence = Q1 - 1.5 × IQR
upper_fence = Q3 + 1.5 × IQR
Any measurement outside [lower_fence, upper_fence] is excluded before computing p50/p95/p99. This removes network anomalies (DNS resolution bursts, transient packet loss) that would inflate p99 values.
The p99 values reported include inliers only. A separate "raw p99" column (worst-case, outliers included) is available in the Grafana dashboard.
Cloudflare edge contribution
portfolIQ routes all API traffic through Cloudflare. Time-to-first-byte (TTFB) includes:
- Client → nearest Cloudflare PoP (edge hop, typically <10 ms within region)
- Cloudflare → Hetzner DE origin (varies: ~2 ms EU, ~70 ms US-East, ~100 ms APAC)
- Origin processing time (Hetzner DE, FastAPI + TimescaleDB query)
Cache HIT responses (Cloudflare edge) eliminate the origin leg entirely — these are the p50 values for cacheable endpoints.
Disclaimer
Latency measured from Hetzner DE server (Frankfurt). Results may vary by client location and Cloudflare edge. Cache HIT vs MISS status affects results significantly — the table reflects typical production traffic mix (~60% cache HIT on stable data endpoints).
For real-time status and incident history: status.portfoliq.io