Historical Odds Data API

Recorded price changes, opening and closing prices and graded results, in the same shape as live odds.

Historical odds answer the questions live prices cannot: where a line opened, how it moved, and where it closed before the start. SockOdds v1 records its own observations of the bookmakers it delivers and keeps every change in price or availability. History is on the Pro plan. It goes back to 22 December 2025 for the leagues listed on the history page and to 8 October 2026 for the rest, and it holds only our own recordings: a stretch we did not record is never filled in.

Why SockOdds

  • Every change, not samples. GET /odds/history returns one row per change for a game, with kind set to opened, changed or withdrawn, plus decimal_odds, line and observed_at.
  • Opening and closing in one call. snapshots=opening,closing on a game's odds adds the first and last price recorded before the start to each quote.
  • Same ids as live odds. History rows use the same market and outcome ids as the odds routes and the results, so joins are exact.
  • Graded results. GET /results settles outcomes from official sources, so every recorded price can be labelled won, lost, void, dead_heat, half_won or half_lost.
  • Honest limits. A game, market or bookmaker with no history rows means nothing was recorded, not that the price never moved.

What you can build

Line movement charts

Request one game over a window of up to 24 hours and plot decimal_odds against observed_at, one line per bookmaker. Step through longer periods one window at a time.

Backtests

Replay a strategy over the prices that were really on offer at each moment, then grade every pick with /results.

Closing line value

Compare the price you took with the closing snapshot for the same outcome and bookmaker. Averaged over many bets, it shows whether your timing is good.

Try it

This request asks for Sportsbet's winner prices on the Rugby League World Cup opener over an eight hour window.

curl -sS "https://api.sockodds.com/v1/odds/history?event_id=ev_000ckk0zrx33&from=2026-10-15T00:00:00Z&to=2026-10-15T08:00:00Z&markets=winner&bookmakers=sportsbet" \
  -H "Authorization: Bearer $SOCKODDS_API_KEY"
const response = await fetch("https://api.sockodds.com/v1/odds/history?event_id=ev_000ckk0zrx33&from=2026-10-15T00:00:00Z&to=2026-10-15T08:00:00Z&markets=winner&bookmakers=sportsbet", {
  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/odds/history?event_id=ev_000ckk0zrx33&from=2026-10-15T00:00:00Z&to=2026-10-15T08:00:00Z&markets=winner&bookmakers=sportsbet",
    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": [
    {
      "event": "ev_000ckk0zrx33",
      "market": {
        "id": "mk_pz0msgze2rqwwktb",
        "key": "winner",
        "name": "Winner",
        "period": "full",
        "subject": null
      },
      "outcome": {
        "id": "oc_nncgcssmzjhyt5pe",
        "name": "Australia",
        "side": "home"
      },
      "bookmaker": "sportsbet",
      "observed_at": "2026-10-15T01:58:00.000Z",
      "kind": "opened",
      "decimal_odds": 1.79,
      "implied_probability": 0.558659,
      "line": null
    },
    {
      "event": "ev_000ckk0zrx33",
      "market": {
        "id": "mk_pz0msgze2rqwwktb",
        "key": "winner",
        "name": "Winner",
        "period": "full",
        "subject": null
      },
      "outcome": {
        "id": "oc_cwsr1gda0qcgf03j",
        "name": "New Zealand",
        "side": "away"
      },
      "bookmaker": "sportsbet",
      "observed_at": "2026-10-15T01:58:00.000Z",
      "kind": "opened",
      "decimal_odds": 2.2,
      "implied_probability": 0.454545,
      "line": null
    },
    {
      "event": "ev_000ckk0zrx33",
      "market": {
        "id": "mk_pz0msgze2rqwwktb",
        "key": "winner",
        "name": "Winner",
        "period": "full",
        "subject": null
      },
      "outcome": {
        "id": "oc_nncgcssmzjhyt5pe",
        "name": "Australia",
        "side": "home"
      },
      "bookmaker": "sportsbet",
      "observed_at": "2026-10-15T07:58:00.000Z",
      "kind": "changed",
      "decimal_odds": 1.72,
      "implied_probability": 0.581395,
      "line": null
    },
    {
      "event": "ev_000ckk0zrx33",
      "market": {
        "id": "mk_pz0msgze2rqwwktb",
        "key": "winner",
        "name": "Winner",
        "period": "full",
        "subject": null
      },
      "outcome": {
        "id": "oc_cwsr1gda0qcgf03j",
        "name": "New Zealand",
        "side": "away"
      },
      "bookmaker": "sportsbet",
      "observed_at": "2026-10-15T07:58:00.000Z",
      "kind": "changed",
      "decimal_odds": 2.12,
      "implied_probability": 0.471698,
      "line": null
    }
  ],
  "meta": {
    "count": 4,
    "cursor": null
  },
  "links": {
    "next": null
  }
}

Each row has a kind. The first two are opened, with the price each team opened at, and the next two are changed, with the price it moved to and when. A long window can return more rows than one page holds: follow links.next unchanged until it is null. The line movement example charts the result.

Search across the SockOdds documentation and catalogue.