A sportsbook data feed is a continuous supply of prices from many books, delivered either as a stream you hold open or as an endpoint you poll. Getting the first prices out of one is easy. Running it for months is mostly operations: knowing which books are actually feeding, noticing when one stops, keeping your copy in step with the source, and handling games that end without saying so.
This guide is about that operational side. If you want the shape of the records themselves, our guide to the odds feed covers the structure of an odds record and normalizing formats.
Push, pull, or both
A betting data feed can reach you in three ways:
- Pull. You call a REST endpoint on a timer. Simple, stateless, easy to restart.
- Push over WebSocket. You hold a connection, receive a snapshot, then deltas as prices change.
- Push over webhook. The provider POSTs to your server when something happens.
In ZippyOddsAPI, REST is available on every plan and the WebSocket stream at /v1/stream comes with plans that include streaming. Webhooks for line moves exist but are set up by the operator for now, and each delivery is signed with HMAC-SHA256 so you can check it came from us.
Most production systems use the stream for speed and REST for checking. That pairing is what the rest of this guide assumes.
Know which books are feeding
No feed covers every book all the time. Venues change their sites, refuse reads for a while, or drop markets. So the first operational habit is to check coverage, not assume it.
ZippyOddsAPI reports it in three places:
| Where | What it tells you | Key needed |
|---|---|---|
/public/coverage | Per book: feeding, stale, no_data or not_expected | No |
/v1/coverage | The same report, inside the usual envelope | Yes, 0 credits |
/v1/status | Each collector: operational, waiting, failing, blocked or off | Yes, 0 credits |
A book counts as feeding when it's on the current board and was observed within 15 minutes, or within three of its source's read intervals if that's longer. Poll one of these every few minutes and alert when a book you depend on leaves feeding. Don't hard-code a list of books into your product. The coverage page shows the current state.
Coverage gaps are data, not errors
When a book stops feeding, the worst response is to hide it. The second worst is to keep showing its last price as if it were live. ZippyOddsAPI keeps old prices at their real age: a book read 40 minutes ago shows a 40-minute-old observed_at, and its prices never count as +EV once they pass the age limit.
Your product should do the same. Show the age, grey out old prices, and leave a missing book missing. A gap in a sports betting data feed is information your users need.
Reconciling a stream with REST
A stream is a copy of the source that you maintain by applying changes. Copies drift. These are the mechanisms that keep yours in step:
- Sequence numbers. Snapshot, delta, event and clocks messages each raise
seqby exactly 1 per connection. A heartbeat repeats the latest value. A jump, or a heartbeat above the lastseqyou saw, means you missed something. Sendresyncand you'll get a fresh snapshot. - Snapshots in parts. A large snapshot arrives in parts, with
partandparts. Don't treat the board as complete until the last part lands. - Removals. A delta carries
removed, a list of market ids. It isn't filtered by your subscription, so ignore ids you don't hold. - Unchanged re-reads. Subscribe with
clocks_v1to hear when a book was read again and its price didn't move, so your held clocks keep advancing. - Dead sockets. If nothing arrives for about 45 seconds, reconnect with a backoff.
seqstarts again on a new connection.
On top of that, run a periodic REST check. Every so often, fetch /v1/odds for a league you stream and compare it market by market with what you hold. Count the differences and log them. A steady trickle that resolves on the next delta is normal. A growing pile means your apply logic has a bug.
Games that end without saying so
Many venues simply stop listing a game when it ends. They don't send a final status. In ZippyOddsAPI a game leaves the board when a venue reports it finished or cancelled, or withdraws it. Until then, its prices stay at their true age and the event's status_stale flag tells you when a live label hasn't been re-observed for an hour.
So don't delete a game from your store because one read left it out. Remove it when the source says it finished, was cancelled or was withdrawn. For results and settlement, see the guide to the sports scores API.
A short runbook
When something looks wrong with your sportsbook data feed, work through it in this order:
- Is the service up?
/healthzneeds no key and says whether the API answers. - Is the book feeding? Check
/public/coveragefor itsstateandreason. - Is the collector healthy?
/v1/statuslists each source's status andlast_ok. - Is your stream in step? Check
seqgaps and sendresync. - Is the request being refused? Branch on
error.codeand quotemeta.request_idwhen you report it.
Choosing who supplies the feed
Operations are much easier when the provider is honest about its own state. Before you commit to any sports betting feeds, check whether the provider publishes live coverage, marks every price with its observation time, and documents its stream's gap handling. Our guide to choosing a sports betting data provider has a full checklist, and the sports odds API guide covers formats and markets. For ZippyOddsAPI's stream messages and close codes in full, see the docs.