portfolIQ
Documentation

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.

EndpointEU (p50/p95/p99)US-East (p50/p95/p99)Singapore (p50/p95/p99)
GET /v1/assets/{id}45 / 89 / 120 ms78 / 140 / 210 ms115 / 180 / 280 ms
GET /v1/assets/{id}/analysis52 / 98 / 135 ms85 / 150 / 225 ms120 / 195 / 300 ms
GET /v1/assets (list, 100 items)68 / 130 / 185 ms98 / 175 / 260 ms135 / 210 / 320 ms
GET /v1/market/overview38 / 75 / 105 ms72 / 130 / 195 ms110 / 170 / 265 ms
GET /v1/ohlcv/{id} (30d)55 / 110 / 158 ms88 / 162 / 235 ms125 / 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) executing curl with --write-out timing output against api.portfoliq.io endpoints.
  • 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:

  1. Client → nearest Cloudflare PoP (edge hop, typically <10 ms within region)
  2. Cloudflare → Hetzner DE origin (varies: ~2 ms EU, ~70 ms US-East, ~100 ms APAC)
  3. 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