Odds Data API for Sports Betting Models and Backtesting

Features, labels and a closing-price benchmark, joined by shared ids.

A betting model needs features (prices), labels (outcomes) and a benchmark to judge itself against (the closing price). SockOdds v1 provides all three under the same ids, so building a training set is a join rather than a matching project. Price history and opening and closing snapshots are on the Pro plan, and history goes back to 22 December 2025 for some leagues (dates by league).

Why SockOdds

  • Prices as features. Every bookmaker's decimal_odds and implied_probability per outcome, plus line for handicaps and totals.
  • Movement as features. GET /odds/history returns every recorded change for a game, so opening price, size of move and time before the start are all things you can derive.
  • Labels graded from official sources. GET /results gives each outcome a verdict of won, lost, void, dead_heat, half_won or half_lost. Outcomes the evidence cannot settle are left out, not guessed.
  • A closing benchmark. snapshots=closing gives the last price recorded before the start for each quote, the usual yardstick for a model's prices.
  • The same ids everywhere. Event, market and outcome ids match across odds, history and results, and across bookmakers. The JSON loads into any data frame library.

What you can build

A training set

Page through /events for a date window, pull each game's history and results, and join on outcome id. Treat an outcome with no verdict as unlabelled, never as a loss.

A backtest

Replay your model's picks against the prices recorded at the time, settle them with /results, and report both return and closing line value.

A calibration check

Compare your predicted probabilities with the closing implied_probability once the margin is removed, and with what actually happened.

Try it

This request asks for Sportsbet's closing winner prices on a finished NFL game.

curl -sS "https://api.sockodds.com/v1/events/ev_000ckk0h3n6r/odds?markets=winner&bookmakers=sportsbet&snapshots=closing" \
  -H "Authorization: Bearer $SOCKODDS_API_KEY"
const response = await fetch("https://api.sockodds.com/v1/events/ev_000ckk0h3n6r/odds?markets=winner&bookmakers=sportsbet&snapshots=closing", {
  headers: { Authorization: `Bearer ${process.env.SOCKODDS_API_KEY}` },
});
console.log(JSON.stringify(await response.json(), null, 2));
import json, os, urllib.request

request = urllib.request.Request(
    "https://api.sockodds.com/v1/events/ev_000ckk0h3n6r/odds?markets=winner&bookmakers=sportsbet&snapshots=closing",
    headers={"Authorization": "Bearer " + os.environ["SOCKODDS_API_KEY"]},
)
with urllib.request.urlopen(request) as response:
    print(json.dumps(json.load(response), indent=2))

Response from a local copy of the API with invented test prices

{
  "data": {
    "id": "ev_000ckk0h3n6r",
    "name": "Arizona Cardinals v Atlanta Falcons",
    "league": {
      "key": "nfl",
      "name": "NFL"
    },
    "scheduled_start": "2026-10-12T08:00:00.000Z",
    "status": "final",
    "participants": [
      {
        "role": "away",
        "identity": {
          "key": "atlanta_falcons",
          "name": "Atlanta Falcons"
        }
      },
      {
        "role": "home",
        "identity": {
          "key": "arizona_cardinals",
          "name": "Arizona Cardinals"
        }
      }
    ],
    "markets": [
      {
        "id": "mk_s4fv79z47rbkwev7",
        "key": "winner",
        "name": "Winner",
        "period": "full",
        "subject": null,
        "alternate": false,
        "outcomes": [
          {
            "id": "oc_53f4yzaqeyex1xhg",
            "name": "Arizona Cardinals",
            "side": "home",
            "line": null,
            "prices": [
              {
                "bookmaker": "sportsbet",
                "decimal_odds": null,
                "implied_probability": null,
                "available": false,
                "closing": {
                  "decimal_odds": 1.72,
                  "implied_probability": 0.581395,
                  "observed_at": "2026-10-12T06:00:00.000Z"
                },
                "observed_at": "2026-10-12T06:00:00.000Z"
              }
            ]
          },
          {
            "id": "oc_1xh21t2rzp7ss665",
            "name": "Atlanta Falcons",
            "side": "away",
            "line": null,
            "prices": [
              {
                "bookmaker": "sportsbet",
                "decimal_odds": null,
                "implied_probability": null,
                "available": false,
                "closing": {
                  "decimal_odds": 2.12,
                  "implied_probability": 0.471698,
                  "observed_at": "2026-10-12T06:00:00.000Z"
                },
                "observed_at": "2026-10-12T06:00:00.000Z"
              }
            ]
          }
        ]
      }
    ]
  },
  "meta": {
    "count": 1,
    "cursor": null
  },
  "links": {
    "next": null
  }
}

Each outcome has available: false and a null current price, because the game is over, and a closing object with the last price recorded before the start. Join these outcome ids to /results?event_id= for the labels, and to /odds/history for the movement that led there.

Search across the SockOdds documentation and catalogue.