Every operational issue in a live sportsbook eventually traces back to a latency budget that nobody wrote down. Here's the one we use, and the four numbers worth monitoring continuously.
| Hop | Target | What goes wrong |
|---|---|---|
| 1. Upstream → your origin | < 200ms | Provider's own infra slow; bad peering |
| 2. Origin parse + cache write | < 20ms | JSON parse hot path; Redis SET latency |
| 3. Origin → CDN/edge | < 50ms | Cache invalidation delay; tag-purge slow |
| 4. Edge → user's network | < 200ms | User's ISP, mobile network, distance |
| 5. Browser paint | < 30ms | React re-render storm; layout thrash |
Total budget: < 500ms. Anything more and a 1-second human-perceptible lag creeps in. At 1.5+ seconds, advantage players notice.
Date.now() - serverTs. Includes network + browser.Stamp the upstream's server timestamp (every Euro365 outcome ships ts as the last field — see docs). Diff against your own server clock at the moment you ingested it. Don't use NTP-naive clocks; the diff is small enough that 100ms of clock drift will dominate.
Wrap the parse-and-store path in a timing block. We use Prometheus histograms; for smaller setups a single in-memory ring buffer + p95 calc every minute is enough.
If you're behind Cloudflare/Fastly, this is the cache-purge delay. Most operators don't realize their "real-time" feed is being cached for 5 seconds at the edge until they look. Cache-Control: private, no-cache on push channels; max-age=2 on poll endpoints.
Send the server timestamp inside the WebSocket frame; on the client log performance.now() - frame.ts and bucket by user agent. You'll quickly see that 90% of latency above your budget is one specific mobile carrier in one specific country — and you can't do much about that one.
| Metric | Good | Trouble |
|---|---|---|
| Upstream → origin p95 | < 200ms | > 500ms |
| Origin → edge p95 | < 100ms | > 300ms |
| Edge → browser p95 | < 250ms | > 600ms |
| End-to-end p95 | < 500ms | > 1500ms |
Teams optimize hops 1–3 to perfection — Redis tuned, edge purges instant — then hop 5 (React re-render) takes 800ms because every odds change triggers a full tree re-render. The browser is part of the latency budget; measure it before you blame the API.