Building with AI? Give your coding tool the whole reference in one file: https://soccer-data.co/llms.txt
SOCCER DATA Docs
Sign in Get API key
// Guides

Odds

Pre-match and in-play prices from five bookmakers, and how to compare them.

Soccer Data collects prices from several bookmakers so you can compare them side by side, follow how they move before kickoff, and track them in play. This guide covers the two odds endpoints, the bookmakers and markets, and the checks worth making first.

Every example sends your key in the X-Api-Key header. Keep that key on your server.

Which plan you need

Endpoint What it returns Needs
/matches/{match_id}/odds Pre-match prices, with opening and latest values. Pro or Business plan
/matches/{match_id}/odds/live In-play prices while the match is live. Pro or Business plan

On the Free plan, you get 403 with the code PLAN_UPGRADE_REQUIRED. You can upgrade on the plans page of your dashboard.

Bookmakers

Bookmaker Slug
Bet365 bet365
Paddy Power paddy-power
BetMGM UK betmgm-uk
Pinnacle pinnacle
Betfair Exchange betfair-exchange

The pre-match endpoint takes a bookmaker filter with one or more slugs, comma-separated. Leave it out to get every bookmaker. An unknown slug returns 400.

In-play prices come from Bet365, Paddy Power, BetMGM UK and Betfair Exchange.

Odds format

All prices are decimal odds. If your users expect fractional or American odds, convert them in your app.

Pre-match and in-play prices are strings, such as "1.380".

Check before you call

Matches and competitions carry flags so you don't have to ask blindly:

Flag On Means
odds_available Match Pre-match prices exist. Always false for postponed and cancelled matches.
live_odds_available Match In-play prices exist right now.
odds_available Competition Its matches have prices.
live_odds_available Competition At least one of its matches has in-play prices right now.

If a match has no prices of the kind you ask for, /odds and /odds/live return 404. Treat that as "no odds for this match", not as a failure.

Pre-match odds

curl "https://api.soccer-data.co/v1/matches/match_yoeml0e6ae37/odds?bookmaker=bet365,pinnacle" \
  -H "X-Api-Key: YOUR_API_KEY"
{
  "data": {
    "match_id": "match_yoeml0e6ae37",
    "bookmakers": [
      {
        "bookmaker": "Bet365",
        "markets": {
          "match_odds": {
            "home": { "opening": "1.380", "last_seen": "1.380" },
            "draw": { "opening": "4.500", "last_seen": "5.000" },
            "away": { "opening": "7.500", "last_seen": "7.500" }
          },
          "btts": {
            "yes": { "opening": "2.000", "last_seen": "2.050" },
            "no": { "opening": "1.750", "last_seen": "1.700" }
          },
          "total_goals": {
            "2.5": {
              "over": { "opening": "1.725", "last_seen": "1.800" },
              "under": { "opening": "2.075", "last_seen": "2.000" }
            }
          }
        }
      },
      {
        "bookmaker": "Pinnacle",
        "markets": {
          "match_odds": {
            "home": { "opening": "1.377", "last_seen": "1.413" },
            "draw": { "opening": "4.910", "last_seen": "4.870" },
            "away": { "opening": "7.620", "last_seen": "7.760" }
          }
        }
      }
    ]
  }
}

Every selection has two prices:

  • opening: the first price we saw, or null if we don't have it.
  • last_seen: the latest price. Once the match has kicked off, this is the closing price.

The endpoint works for upcoming and finished matches alike, so you can also look back at closing lines.

Markets

The example shows three common markets:

  • match_odds: home, draw or away.
  • btts: both teams to score, yes or no.
  • total_goals: a key per line, such as "2.5", each with over and under.

Depending on the bookmaker and the match, you'll also find full-time and half-time results, corners, cards, Asian and European handicaps, team totals, shots, correct score, to qualify and penalty specials. On bigger matches Bet365 adds markets for shots, throw-ins, goal kicks, tackles and offsides, some with per-half versions such as first_half_match_shots.

A market only appears if that bookmaker prices it. Check that a key exists before reading it.

In-play odds

Ask only while live_odds_available is true:

curl "https://api.soccer-data.co/v1/matches/match_0i2m68v7oaxa/odds/live" \
  -H "X-Api-Key: YOUR_API_KEY"
{
  "data": {
    "match_id": "match_0i2m68v7oaxa",
    "bookmakers": [
      {
        "bookmaker": "Bet365",
        "markets": {
          "match_odds": {
            "home": { "live": "3.400" },
            "draw": { "live": "3.100" },
            "away": { "live": "2.250" }
          }
        }
      },
      {
        "bookmaker": "Betfair Exchange",
        "markets": {
          "match_odds": {
            "home": {
              "live": "3.650",
              "available_to_back": [{ "price": 3.65, "size": 1209.44 }, { "price": 3.6, "size": 1795.66 }],
              "available_to_lay": [{ "price": 3.7, "size": 480.78 }, { "price": 3.75, "size": 2184.81 }]
            }
          }
        }
      }
    ]
  }
}

Each selection has a single live price. Betfair Exchange also gives the money waiting in the market: available_to_back and available_to_lay, up to three levels each, best price first, with a price and a size.

Books suspend and reopen during a match, so bookmakers and markets come and go between calls. After full time the endpoint returns 404.

In-play prices follow your plan's live refresh, normally 15 seconds, so polling faster than that returns the same prices. Live matches has a polling loop you can reuse.

How far back odds go

Odds history is thinner than match history:

  • From the 2026/27 season, matches can have opening and closing prices from all five bookmakers.
  • Before that, most matches only have Bet365 closing prices. For those, opening is null and last_seen holds the closing price.

/coverage/leagues/{competition_id} shows which seasons have prices, with opening_odds, closing_odds and odds counted separately for each season. To find competitions that have odds at all, use /coverage/leagues with data_type=odds, opening_odds or closing_odds. For example, Premier League 26/27 has both opening and closing prices, while 25/26 has closing prices only.

Common mistakes

  • Assuming a market or bookmaker is always there. Check that it exists in the response first.
  • Treating 404 as an error. On the odds endpoints it means the match has no prices of that kind.
  • Expecting prices for postponed or cancelled matches. odds_available is always false for them.
  • Mistyping a bookmaker slug. An unknown slug returns 400.