BlogOdds APIs

Using a Sports Scores API Alongside Live Odds

A sports scores API returns the status and score of games: scheduled, in progress, final, and the points on each side. Alongside odds data it does three jobs. It settles bets once a game ends, it gives live prices their context, and it tells you when a price on a game in progress should no longer be trusted.

The detail that catches people out is that odds and scores often come from different places, at different speeds, with different ids. This guide covers what a score record holds, how to join it to prices, how to settle the common bet types from a final score, and where a score feed attached to an odds API stops.

What a score record holds

At minimum you need a game id, the two teams, a status and a score. A useful record also says when the status was last observed, so you can tell a game that's still in play from one whose source has gone quiet.

In ZippyOddsAPI, /v1/scores returns events that have a score, in the same row shape as /v1/events:

curl -s "https://zippyoddsapi.com/v1/scores?league=nba" \
  -H "Authorization: Bearer $ZIPPYODDS_KEY"
FieldMeaning
idThe event id, the same one /v1/odds uses
statusscheduled, live, final or cancelled
status_observed_atWhen a source last called the game live
status_staleTrue when no source has re-observed the live label for an hour
score{ "home": 0, "away": 0 } once a source reports one, otherwise null
clockAlways null today

Because the event id is shared, joining a score to its prices is a lookup, not a name match.

Where this sports score api stops

Be clear about the limits before you design around it. In ZippyOddsAPI a game leaves the board as soon as a source reports it final, so /v1/scores has no finished games to return. In practice it lists games in progress, and live games need a plan with the live feature. Below that, it returns an empty list.

That makes it a live-context feed, not a results archive. For settlement you need final scores from a results source, and you'll want to store them yourself. The stream helps on the live side: an event message arrives whenever a status or score changes.

{ "type": "event", "event_id": "evt_0h7k2m9q4x1tzb", "status": "live", "score": { "home": 12, "away": 9 }, "seq": 3 }

Settling bets from a final score

Settlement is simple arithmetic once you have the final score and the exact line. Take a game that ends home 112, away 108.

Moneyline. The home side won, so a home moneyline wins and an away moneyline loses.

Spread. Add the side's handicap to its own score and compare.

  • Home -3.5: 112 - 3.5 = 108.5 against 108. The home side covers.
  • Home -4: 112 - 4 = 108 against 108. A push, so the stake comes back.
  • Home -6.5: 105.5 against 108. Away +6.5 covers.

Total. The combined score is 220.

  • Total 224.5: under wins.
  • Total 220: push.

Payout. A winning bet at -110 for $110 returns $100 profit plus the stake. In decimal that's 1.9091 × $110 = $210.00 back.

Here's the same logic as generic Python, with no API calls:

def settle_spread(home, away, home_line):
    margin = home + home_line - away
    return "win" if margin > 0 else "push" if margin == 0 else "loss"

def settle_total(home, away, line, side):
    total = home + away
    if total == line:
        return "push"
    return "win" if (total > line) == (side == "over") else "loss"

print(settle_spread(112, 108, -3.5))       # win
print(settle_spread(112, 108, -4))         # push
print(settle_total(112, 108, 224.5, "under"))  # win

Two cautions. Settle against the period the bet was on: a first-half total needs the first-half score, not the final. And each sportsbook has its own rules for overtime, postponements and abandoned games, so read the rules of the book whose bet you're settling.

Scores tell you when a price is stale

A live price is only useful while it matches the state of the game. A score change usually moves every market on that game. If your odds store holds a price observed before the last score change, treat it with suspicion.

ZippyOddsAPI applies a stricter age limit once a game has started: a price more than 90 seconds old is stale, and a price read before kickoff is a pre-match price, not a current one. Use the score feed the same way in your own code. When an event message arrives, mark that game's prices as needing a fresh read.

Joining scores to the right game

If your scores and odds come from different providers, you have to match games yourself. Use the start time and both teams, and prefer no match to a wrong one. A wrong join settles bets on the wrong game.

Sport by sport, the shape of the problem changes. NFL games are few and fixed, so matching is easy, and the NFL API guide covers that sport's data. Soccer has many leagues, reserve sides and three-way markets, which the soccer odds API guide covers.

Judging a free sports scores api

If you're comparing a free sports scores api with a paid one, look at:

  1. Final scores. Does it keep them, and for how long?
  2. Status clocks. Does it say when a status was last observed?
  3. Ids. Can you join its games to your odds without matching names?
  4. Periods. Does it give quarter, half or inning scores, for part-game bets?
  5. Corrections. Does it tell you when a final score is revised?

For prices to go with your scores, the sports odds API guide covers what an odds API returns. The ZippyOddsAPI docs document the scores endpoint, the event row and the stream's event message in full.