MONITORING
Monitoring a Pinnacle odds pipeline.
A pipeline that polls the Pinnacle odds API fails quietly more often than loudly: prices stop moving, a cursor stalls, a worker backs off and never comes back. Five metrics catch those failures, and each threshold follows from the poll interval and the plan you are on.
The five metrics
Record them per board, meaning per sport and phase, because a live tennis board and a prematch soccer board fail independently. One counter per poll is enough to derive all five.
| Metric | How to measure it | Alert when |
|---|---|---|
| Snapshot age | Seconds since the last poll that returned a valid envelope | Above three poll intervals during a slate |
| Cursor progress | The last value from each response, compared with the previous one | Unchanged for 30 minutes while events on the board are in play |
| Poll latency | Time from request to parsed response, p50 and p95 | p95 above 2 seconds for 10 minutes |
| Error rate | Responses by status per hour: 2xx, 429, 5xx, timeouts | 429 above 1% of requests, or any 401 or 403 |
| Request rate | Requests per second at peak, and per day on the free key | Above 70% of the plan ceiling, or 90 of the free 100 a day |
Snapshot age is the one that matters
Every other failure shows up here eventually, which makes age the metric to put on the wall. Compute it from the last successful, validated poll, not the last attempt: a poller that receives 5xx every ten seconds is attempting constantly and succeeding never. Expose the same number to your consumers next to the prices, so a dashboard can label a stale board instead of showing old prices as current. The public match desk on pnclHUB does exactly this, withholding prices when the source's freshness is unconfirmed rather than showing them with a caveat.
The threshold scales with the interval. Polling live boards every 5 seconds, alert at 15; polling prematch every 30, alert at 90. Outside a slate, a board with no events can legitimately go quiet, so gate the alert on the board carrying events whose start time has passed.
Cursor progress and the empty delta
The since cursor makes a poll return only what changed, and
a healthy live board returns something most cycles. A cursor that stops
advancing while matches are in play means one of three things: the poller
is sending a stale cursor, the response is being discarded before the
cursor is stored, or the feed itself has stalled. Log the cursor with each
poll and the first two are visible in a minute. The polling discipline in
REST integration covers where
the cursor should live so a restart resumes from it.
Budget metrics
Requests per second against the ceiling is the number that predicts tomorrow's 429s. Keep the steady state under 70% of the plan's rate, 7 a second on the 10-a-second plan and 21 on the 30-a-second plan, and treat the peak on a match day as the figure to compare, since rejections arrive when boards are busiest. On the free key the daily count is the one to watch: at 90 of the 100 daily requests, stop optional calls so the scheduled ones finish the day. The arithmetic behind the peak is on the capacity planning page.
Where the numbers come from
One structured log line per poll carries everything: board, status,
latency, cursor before and after, event count. A metrics layer can derive
the five series from those lines, and a log search answers the questions
a dashboard cannot, such as which board produced the first 429 on
Saturday. Name the series plainly, odds_poll_age_seconds,
odds_poll_latency_seconds, odds_poll_total with
a status label, and a new engineer reads the dashboard without a legend.
{"board": "soccer/live", "status": 200,
"latency_ms": 412, "since": 141, "last": 158,
"events": 7, "at": "2026-09-29T14:03:10Z"}
Add one synthetic check that reads your own store, not the API: fetch a known event from the cache every minute and confirm its age. It catches the failure where the poller runs but the consumers cannot see its work. If the poller uses the SSE stream as well, count reconnects per hour on the same dashboard; the SSE setup guide on pnclPULSE describes what a healthy reconnect cycle looks like.
Questions this raises
Is snapshot age the same as the vendor's data age?
No. Snapshot age measures your poller. The feed's own freshness is a separate question, and the docs describe prematch refreshing about every 30 seconds while live prices arrive within a fraction of a second. Track both when you can.
How much history should the metrics keep?
Two weeks at full resolution covers two weekends, which is where the peaks are. Longer retention can be downsampled to hourly maxima.
Do I need all five on the free key?
Age and the daily request count are enough for a prototype. Add the rest when the poller moves to a paid plan and a per-second ceiling.
Allowances and refresh figures follow the vendor's published documentation, checked on 26 September 2026. Published .