> ## Documentation Index
> Fetch the complete documentation index at: https://docs.therundown.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Historical Odds & Line Movement

> Retrieve opening lines, closing lines, and full price history to build line movement charts and analyze odds changes over time.

TheRundown API provides several endpoints for accessing historical odds data. You can retrieve full price history for charting, compare opening and closing lines, and filter by time range.

For final scores, per-period results, and grading checks, use the [Scores and Results guide](/guides/scores-and-results); odds history describes price changes rather than game results.

## Backtesting

Every price move is timestamped — opens, in-play changes, closes, and reopens — so this history doubles as a backtest-ready dataset: pull a market's timeline for any event inside your plan's odds-history window (Starter and above) and replay exactly how it moved.

| Plan | Odds history |
| - | - |
| Starter | 7 days |
| Pro | 30 days |
| Ultra | 90 days |
| Super | 6 months |
| Mega | 1 year |
| Max | Unlimited |

Opening and closing lines are included on Pro and above (see [Opening Lines](#opening-lines) and [Closing Lines](#closing-lines) below).

<Note>
  Backtesting or validating a model — machine learning, statistical, or otherwise — against TheRundown data requires our prior written permission. Email **[legal@therundown.io](mailto:legal@therundown.io)** to request it. We're glad to talk through backtesting use.
</Note>

## Endpoints Overview

| Endpoint | Description |
| - | - |
| `GET /api/v2/events/{eventID}/markets/history` | Price history rows for the requested markets in an event |
| `GET /api/v2/events/{eventID}/markets/{marketID}/history` | Price history for a specific market (ideal for charting) |
| `GET /api/v2/events/{eventID}/openers` | Opening lines for a single event |
| `GET /api/v2/events/{eventID}/closing` | Closing lines for a single event |
| `GET /api/v2/sports/{sportID}/openers/{date}` | Opening lines for all events in a sport on a date |
| `GET /api/v2/sports/{sportID}/closing/{date}` | Closing lines for all events in a sport on a date |

The API base URL is `https://therundown.io/api/v2`; include `/api` exactly once. Authenticate with the `X-TheRundown-Key` header.

## Full Market History

Use the event history endpoint to get recorded price changes for the requested markets and sportsbooks. If `market_ids` is omitted, the endpoint uses the default markets `1,2,3`; pass up to 12 market IDs when selecting markets explicitly.

```bash theme={null}
curl -H "X-TheRundown-Key: $THERUNDOWN_API_KEY" "https://therundown.io/api/v2/events/{eventID}/markets/history?\
affiliate_ids=19,23"
```

### Query Parameters

| Parameter | Type | Description |
| - | - | - |
| `affiliate_ids` | string | Comma-separated sportsbook IDs to include |
| `market_ids` | string | Comma-separated market IDs to filter |
| `from` | string | Start time in RFC 3339 format (e.g., `2026-02-10T00:00:00Z`) |
| `to` | string | End time in RFC 3339 format (e.g., `2026-02-12T23:59:59Z`) |
| `limit` | integer | Maximum rows to return; defaults to 1000 and accepts up to 5000 |
| `main_line` | boolean | When `true`, return rows that were the sportsbook's main line when written |

`from` and `to` are inclusive filters. Results are returned newest first and are capped by `limit`; invalid or out-of-range limits fall back to 1000. The response does not provide a cursor or `has_more` flag. If the response reaches the cap, narrow the markets, sportsbooks, or time window before treating it as complete.

Historical responses are subject to the history window included with your plan.

### Example Response

The API returns `history` newest first.

```json theme={null}
{
  "meta": {
    "event_id": "abc123",
    "count": 4
  },
  "history": [
    {
      "id": 98004,
      "market_line_price_id": 50002,
      "event_id": "abc123",
      "sport_id": 4,
      "affiliate_id": 19,
      "market_participant_id": 7001,
      "market_id": 2,
      "line": "-4",
      "price": "-110",
      "change_type": "main_line",
      "is_main_line": true,
      "updated_at": "2026-02-11T18:02:00Z"
    },
    {
      "id": 98003,
      "market_line_price_id": 50001,
      "event_id": "abc123",
      "sport_id": 4,
      "affiliate_id": 19,
      "market_participant_id": 7001,
      "market_id": 2,
      "line": "-3.5",
      "price": "-115",
      "change_type": "main_line",
      "is_main_line": false,
      "updated_at": "2026-02-11T18:02:00Z"
    },
    {
      "id": 98002,
      "market_line_price_id": 50001,
      "event_id": "abc123",
      "sport_id": 4,
      "affiliate_id": 19,
      "market_participant_id": 7001,
      "market_id": 2,
      "line": "-3.5",
      "price": "-115",
      "previous_price": "-110",
      "change_type": "price",
      "is_main_line": true,
      "updated_at": "2026-02-11T09:15:00Z"
    },
    {
      "id": 98001,
      "market_line_price_id": 50001,
      "event_id": "abc123",
      "sport_id": 4,
      "affiliate_id": 19,
      "market_participant_id": 7001,
      "market_id": 2,
      "line": "-3.5",
      "price": "-110",
      "change_type": "open",
      "is_main_line": true,
      "updated_at": "2026-02-10T14:30:00Z"
    }
  ]
}
```

Each `market_line_price_id` identifies one sportsbook's price on one line value, so the rows for `-3.5` and the row for `-4` are two different lines, not one line changing value. The main line moving from `-3.5` to `-4` shows up as the `is_main_line` flag leaving `-3.5` and arriving on `-4` at the same timestamp. A row with `change_type: "main_line"` means only the flag changed; when odds and the flag change together, the row is a normal `price` row that carries the new flag.

## Tracking Main-Line Moves

Pass `main_line=true` to keep only rows that were the main line when they were written. Sorted by `updated_at`, each change of `line` for a participant is a main-line move (a spread going from `-3.5` to `-4.5`, or a total from `224.5` to `225.5`) with its timestamp. Alternate-line rows are excluded.

```bash theme={null} theme={null}
curl -H "X-TheRundown-Key: $THERUNDOWN_API_KEY" "https://therundown.io/api/v2/events/{eventID}/markets/history?\
market_ids=2&affiliate_ids=19&main_line=true"
```

<CodeGroup>
  ```python Python theme={null} theme={null}
  response = requests.get(
      f"{BASE_URL}/events/{EVENT_ID}/markets/history",
      headers={"X-TheRundown-Key": API_KEY},
      params={"market_ids": "2", "affiliate_ids": "19", "main_line": "true"},
  )
  rows = sorted(response.json()["history"], key=lambda r: r["updated_at"])

  current_line = {}
  for row in rows:
      key = (row["affiliate_id"], row["market_participant_id"])
      if current_line.get(key) != row["line"]:
          print(f"{row['updated_at']}: participant {row['market_participant_id']} main line -> {row['line']} ({row['price']})")
          current_line[key] = row["line"]
  ```

  ```javascript Node.js 22+ (server ESM) theme={null} theme={null}
  const params = new URLSearchParams({ market_ids: "2", affiliate_ids: "19", main_line: "true" });
  const response = await fetch(`${BASE_URL}/events/${EVENT_ID}/markets/history?${params}`, {
    headers: { "X-TheRundown-Key": API_KEY },
  });
  const rows = (await response.json()).history.sort((a, b) => a.updated_at.localeCompare(b.updated_at));

  const currentLine = new Map();
  for (const row of rows) {
    const key = `${row.affiliate_id}:${row.market_participant_id}`;
    if (currentLine.get(key) !== row.line) {
      console.log(`${row.updated_at}: participant ${row.market_participant_id} main line -> ${row.line} (${row.price})`);
      currentLine.set(key, row.line);
    }
  }
  ```
</CodeGroup>

Without `main_line=true`, every row still carries `is_main_line`, so the same logic works client-side. For real-time tracking, the [delta feed](/api-reference/generated/v2-markets/get-market-price-changes-since-a-given-id) and the [WebSocket](/api-reference/v2/websocket) include `is_main_line` on every update: a main-line move arrives as `is_main_line: true` on the new line value and `is_main_line: false` on the old one.

## Single Market History (Chart Data)

For building a line movement chart, fetch history for a specific market. This returns a chart-optimized response with `series` grouped by sportsbook (keyed by affiliate ID), where each data point uses shorthand fields: `t` (timestamp), `p` (price as a string), `l` (line value), `m` (main-line flag), and `c` (closed\_at). Chart points are ordered oldest first. A market can contain multiple participants and line values. Pass `participant_id` to isolate one selection; optionally pass `line` to isolate one line value, or `main_line=true` to keep main-line points.

Use `participant_id` to keep different sides out of the same chart. `line` is optional; use `main_line=true` instead when the chart should follow a changing main line. The `c` field is omitted when the point has no closing timestamp.

| Parameter | Description |
| - | - |
| `participant_id` | Participant ID to isolate a selection; required for a single-selection chart |
| `affiliate_ids` | Comma-separated sportsbook IDs permitted by your plan |
| `line` | Exact line value to isolate, such as `-3.5` |
| `main_line` | Keep points that were the sportsbook's main line when written |
| `from`, `to` | Inclusive RFC 3339 time bounds |
| `limit` | Maximum points across all affiliate series; defaults to 1000 and accepts up to 5000; invalid values fall back to 1000 |

`123` in the examples is a placeholder. Replace it with the public `participant_id` from [participant discovery](/api-reference/generated/v2-markets/get-market-participants) for the same event and market. The examples pin `line=-3.5` so each chart tracks one handicap. If the response reaches `limit`, narrow the filters or time window; the chart response has no pagination cursor or truncation flag.

```bash theme={null}
# Spread history (market_id=2) for a specific event
curl -H "X-TheRundown-Key: $THERUNDOWN_API_KEY" "https://therundown.io/api/v2/events/{eventID}/markets/2/history?\
participant_id=123&affiliate_ids=19&line=-3.5"
```

<CodeGroup>
  ```python Python theme={null}
  import os
  import requests

  API_KEY = os.environ["THERUNDOWN_API_KEY"]
  BASE_URL = "https://therundown.io/api/v2"
  EVENT_ID = "abc123"

  # Fetch spread history from DraftKings (affiliate_id 19)
  response = requests.get(
      f"{BASE_URL}/events/{EVENT_ID}/markets/2/history",
      headers={"X-TheRundown-Key": API_KEY},
      params={
          "participant_id": "123",
          "affiliate_ids": "19",
          "line": "-3.5",
      }
  )

  data = response.json()
  series = data["series"]  # Map keyed by affiliate ID

  print("Affiliate            | Timestamp                  | Line   | Price")
  print("-" * 75)
  for aff_id, aff_series in series.items():
      for point in aff_series["data"]:
          print(f"{aff_series['affiliate_name']:>20} | {point['t']}  | {point['l']:>6} | {point['p']}")
  ```

  ```javascript Node.js 22+ (server ESM) theme={null}
  const API_KEY = process.env.THERUNDOWN_API_KEY;
  if (!API_KEY) throw Error('Set THERUNDOWN_API_KEY');
  const BASE_URL = "https://therundown.io/api/v2";
  const EVENT_ID = "abc123";

  const params = new URLSearchParams({
    participant_id: "123",
    affiliate_ids: "19",
    line: "-3.5",
  });

  const response = await fetch(
    `${BASE_URL}/events/${EVENT_ID}/markets/2/history?${params}`,
    { headers: { "X-TheRundown-Key": API_KEY } }
  );
  const { series } = await response.json();

  // series is a map keyed by affiliate ID (string)
  console.log("Affiliate            | Timestamp                  | Line   | Price");
  console.log("-".repeat(75));
  for (const [affId, affSeries] of Object.entries(series)) {
    for (const point of affSeries.data) {
      console.log(`${affSeries.affiliate_name}  | ${point.t}  | ${point.l}  | ${point.p}`);
    }
  }
  ```
</CodeGroup>

## Filtering by Time Range

Use `from` and `to` parameters in RFC 3339 format to scope history to a specific window. This is useful for showing line movement in the last 24 hours or during a specific period.

```bash theme={null}
# History from the last 24 hours
curl -H "X-TheRundown-Key: $THERUNDOWN_API_KEY" "https://therundown.io/api/v2/events/{eventID}/markets/2/history?\
participant_id=123&affiliate_ids=19&line=-3.5\
&from=2026-02-11T00:00:00Z\
&to=2026-02-12T00:00:00Z"
```

<CodeGroup>
  ```python Python theme={null}
  from datetime import datetime, timedelta, timezone

  now = datetime.now(timezone.utc)
  yesterday = now - timedelta(days=1)

  response = requests.get(
      f"{BASE_URL}/events/{EVENT_ID}/markets/2/history",
      headers={"X-TheRundown-Key": API_KEY},
      params={
          "participant_id": "123",
          "affiliate_ids": "19",
          "line": "-3.5",
          "from": yesterday.strftime("%Y-%m-%dT%H:%M:%SZ"),
          "to": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
      }
  )

  data = response.json()
  series = data["series"]
  total_points = sum(len(s["data"]) for s in series.values())
  print(f"Found {total_points} price changes in the last 24 hours")
  ```

  ```javascript Node.js 22+ (server ESM) theme={null}
  const now = new Date();
  const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);

  const params = new URLSearchParams({
    participant_id: "123",
    affiliate_ids: "19",
    line: "-3.5",
    from: yesterday.toISOString(),
    to: now.toISOString(),
  });

  const response = await fetch(
    `${BASE_URL}/events/${EVENT_ID}/markets/2/history?${params}`,
    { headers: { "X-TheRundown-Key": API_KEY } }
  );
  const { series } = await response.json();
  const totalPoints = Object.values(series).reduce((sum, s) => sum + s.data.length, 0);
  console.log(`Found ${totalPoints} price changes in the last 24 hours`);
  ```
</CodeGroup>

## Opening and Closing Parameters

Both event and sport/date snapshot endpoints accept these filters:

| Parameter | Description |
| - | - |
| `market_ids` | Comma-separated market IDs; defaults to `1,2,3` (soccer and NHL also include `563`), with a maximum of 12 IDs |
| `affiliate_ids` | Comma-separated sportsbook IDs permitted by your plan |
| `main_line` | Defaults to `false`; when `true`, selects main lines in the snapshot. Use history to follow which line was main at each timestamp |
| `hide_closed` | When `true`, filters closed prices based on their current state; defaults to `false` |

An event can be returned without prices when no recorded price matches the filters. These endpoints require a plan with historical-data access. Opening and closing snapshots also apply the plan's history window to the event date; requests outside that window return `403`. Omit `hide_closed` or set it to `false` when retrieving completed-event snapshots so currently closed prices remain included.

## Opening Lines

The openers endpoints return the first recorded price for each available market line and sportsbook. They can return data before, during, or after an event when a price has been recorded. The response uses the same V2 events structure (with `markets`, `participants`, `lines`, and `prices`).

<CodeGroup>
  ```bash All NBA openers for a date theme={null}
  curl -H "X-TheRundown-Key: $THERUNDOWN_API_KEY" "https://therundown.io/api/v2/sports/4/openers/2026-02-12?\
  market_ids=1,2,3&affiliate_ids=19,23&offset=300"
  ```

  ```bash Single event openers theme={null}
  curl -H "X-TheRundown-Key: $THERUNDOWN_API_KEY" "https://therundown.io/api/v2/events/{eventID}/openers?\
  market_ids=1,2,3&affiliate_ids=19,23"
  ```

  ```python Python theme={null}
  # All openers for a sport + date
  response = requests.get(
      f"{BASE_URL}/sports/{sport_id}/openers/{today}",
      headers={"X-TheRundown-Key": API_KEY},
      params={
          "market_ids": "1,2,3",
          "affiliate_ids": "19,23",
          "offset": "300",
      }
  )

  # Response is { "events": [ { "markets": [...] } ] }
  for event in response.json().get("events", []):
      away = event["teams"][0]["name"]
      home = event["teams"][1]["name"]
      print(f"\n{away} @ {home}")
      for market in event.get("markets", []):
          print(f"  {market['name']} (opened)")
          for participant in market["participants"]:
              for line in participant["lines"]:
                  for aff_id, price in line["prices"].items():
                      print(f"    {participant['name']}: {line['value']} {price['price']}")
  ```

  ```javascript Node.js 22+ (server ESM) theme={null}
  // All openers for a sport + date
  const response = await fetch(
    `${BASE_URL}/sports/${sportId}/openers/${today}?${new URLSearchParams({
      market_ids: "1,2,3",
      affiliate_ids: "19,23",
      offset: "300",
    })}`,
    { headers: { "X-TheRundown-Key": API_KEY } }
  );
  const { events } = await response.json();

  for (const event of events) {
    const [away, home] = event.teams.map((t) => t.name);
    console.log(`\n${away} @ ${home}`);
    for (const market of event.markets || []) {
      console.log(`  ${market.name} (opened)`);
      for (const participant of market.participants) {
        for (const line of participant.lines) {
          for (const [affId, price] of Object.entries(line.prices)) {
            console.log(`    ${participant.name}: ${line.value} ${price.price}`);
          }
        }
      }
    }
  }
  ```
</CodeGroup>

## Closing Lines

The closing endpoint returns the latest recorded price at or before the event's scheduled start time. Before the event starts, that value is provisional and may change; after the start, it represents the pre-start closing snapshot. The response uses the same V2 events structure as openers. See the [event closing reference](/api-reference/generated/v2-events/get-closing-prices-for-an-event) and [sport/date closing reference](/api-reference/generated/v2-events/get-closing-lines-for-a-sport-and-date).

<CodeGroup>
  ```bash All NBA closing lines for a date theme={null}
  curl -H "X-TheRundown-Key: $THERUNDOWN_API_KEY" "https://therundown.io/api/v2/sports/4/closing/2026-02-12?\
  market_ids=1,2,3&affiliate_ids=19,23&offset=300"
  ```

  ```bash Single event closing lines theme={null}
  curl -H "X-TheRundown-Key: $THERUNDOWN_API_KEY" "https://therundown.io/api/v2/events/{eventID}/closing?\
  market_ids=1,2,3&affiliate_ids=19,23"
  ```
</CodeGroup>

For example, this request selects markets `11,12` and sportsbooks `19,23`. Replace the event ID with an event inside your plan's history window:

```bash theme={null}
curl -H "X-TheRundown-Key: $THERUNDOWN_API_KEY" "https://therundown.io/api/v2/events/ebb2dc1a9b7c2e32c4468da5ba60a526/closing?market_ids=11,12&affiliate_ids=19,23"
```

Both closing endpoints accept the shared filters described above. `hide_closed` can remove prices from a completed event when those prices are closed; the event and its score can still be returned.

<CodeGroup>
  ```python Python theme={null}
  # All closing lines for a sport + date
  response = requests.get(
      f"{BASE_URL}/sports/{sport_id}/closing/{today}",
      headers={"X-TheRundown-Key": API_KEY},
      params={
          "market_ids": "1,2,3",
          "affiliate_ids": "19,23",
          "offset": "300",
      }
  )

  # Response is { "events": [ { "markets": [...] } ] }
  for event in response.json().get("events", []):
      away = event["teams"][0]["name"]
      home = event["teams"][1]["name"]
      print(f"\n{away} @ {home}")
      for market in event.get("markets", []):
          print(f"  {market['name']} (closing)")
          for participant in market["participants"]:
              for line in participant["lines"]:
                  for aff_id, price in line["prices"].items():
                      print(f"    {participant['name']}: {line['value']} {price['price']}")
  ```

  ```javascript Node.js 22+ (server ESM) theme={null}
  // All closing lines for a sport + date
  const response = await fetch(
    `${BASE_URL}/sports/${sportId}/closing/${today}?${new URLSearchParams({
      market_ids: "1,2,3",
      affiliate_ids: "19,23",
      offset: "300",
    })}`,
    { headers: { "X-TheRundown-Key": API_KEY } }
  );
  const { events } = await response.json();

  for (const event of events) {
    const [away, home] = event.teams.map((t) => t.name);
    console.log(`\n${away} @ ${home}`);
    for (const market of event.markets || []) {
      console.log(`  ${market.name} (closing)`);
      for (const participant of market.participants) {
        for (const line of participant.lines) {
          for (const [affId, price] of Object.entries(line.prices)) {
            console.log(`    ${participant.name}: ${line.value} ${price.price}`);
          }
        }
      }
    }
  }
  ```
</CodeGroup>

## Building a Line Movement Chart

Here is a complete example that fetches one participant's spread history and formats the data for a charting library. The chart endpoint returns `series` as a map keyed by affiliate ID, with each entry containing an `affiliate_name` and `data` array of `{t, p, l, m, c}` points.

<CodeGroup>
  ```python Python theme={null}
  import os
  import requests
  from datetime import datetime, timezone

  API_KEY = os.environ["THERUNDOWN_API_KEY"]
  BASE_URL = "https://therundown.io/api/v2"
  EVENT_ID = "abc123"

  # Fetch spread history for multiple books
  response = requests.get(
      f"{BASE_URL}/events/{EVENT_ID}/markets/2/history",
      headers={"X-TheRundown-Key": API_KEY},
      params={
          "participant_id": "123",
          "affiliate_ids": "19,23",
          "line": "-3.5",
      }
  )
  data = response.json()
  series = data["series"]  # Map keyed by affiliate ID (string)

  # Print chart data — series is already grouped by sportsbook
  for aff_id, aff_series in series.items():
      book_name = aff_series["affiliate_name"]
      print(f"\n{book_name} (affiliate {aff_id}) Spread Movement:")
      for point in aff_series["data"]:
          closed = " [CLOSED]" if point.get("c") else ""
          print(f"  {point['t']}: price={point['p']}{closed}")

  # To use with matplotlib:
  # import matplotlib.pyplot as plt
  # import matplotlib.dates as mdates
  #
  # fig, ax = plt.subplots(figsize=(12, 6))
  # for aff_id, aff_series in series.items():
  #     dates = [datetime.fromisoformat(p["t"].replace("Z", "+00:00")) for p in aff_series["data"]]
  #     prices = [float(p["p"]) for p in aff_series["data"]]
  #     ax.step(dates, prices, where="post", label=aff_series["affiliate_name"])
  # ax.set_xlabel("Time")
  # ax.set_ylabel("Price")
  # ax.legend()
  # plt.title("Spread Price Movement")
  # plt.show()
  ```

  ```javascript Node.js 22+ (server ESM) theme={null}
  const API_KEY = process.env.THERUNDOWN_API_KEY;
  if (!API_KEY) throw Error('Set THERUNDOWN_API_KEY');
  const BASE_URL = "https://therundown.io/api/v2";
  const EVENT_ID = "abc123";

  const params = new URLSearchParams({
    participant_id: "123",
    affiliate_ids: "19,23",
    line: "-3.5",
  });

  const response = await fetch(
    `${BASE_URL}/events/${EVENT_ID}/markets/2/history?${params}`,
    { headers: { "X-TheRundown-Key": API_KEY } }
  );
  const { series } = await response.json();

  // series is a map keyed by affiliate ID (string), already grouped by sportsbook
  // Format for a charting library (e.g., Chart.js, Recharts)
  const chartDatasets = Object.entries(series).map(([affId, affSeries]) => ({
    label: affSeries.affiliate_name,
    data: affSeries.data.map((p) => ({
      x: new Date(p.t),
      y: parseFloat(p.p),
    })),
  }));

  console.log("Chart datasets:", JSON.stringify(chartDatasets, null, 2));

  // To use with Chart.js:
  // new Chart(ctx, {
  //   type: "line",
  //   data: { datasets: chartDatasets },
  //   options: {
  //     scales: {
  //       x: { type: "time" },
  //       y: { reverse: true, title: { text: "Price" } },
  //     },
  //   },
  // });
  ```
</CodeGroup>

## Comparing Openers to Current Lines

A common use case is showing how far a line has moved from its opener. Fetch both the opener and current odds, then compute the difference. Both endpoints return the same V2 events structure with `markets` > `participants` > `lines` > `prices`.

```python theme={null}
import os
import requests

API_KEY = os.environ["THERUNDOWN_API_KEY"]
BASE_URL = "https://therundown.io/api/v2"
EVENT_ID = "abc123"

# Fetch opener and current data
opener_resp = requests.get(
    f"{BASE_URL}/events/{EVENT_ID}/openers",
    headers={"X-TheRundown-Key": API_KEY},
    params={"market_ids": "2", "affiliate_ids": "19"}
)

current_resp = requests.get(
    f"{BASE_URL}/events/{EVENT_ID}",
    headers={"X-TheRundown-Key": API_KEY},
    params={"market_ids": "2", "affiliate_ids": "19"}
)

# Both return { "events": [ { "markets": [...] } ] }
opener_data = opener_resp.json()
current_data = current_resp.json()

def extract_prices(data):
    """Extract participant prices from V2 events response."""
    results = {}
    for event in data.get("events", []):
        for market in event.get("markets", []):
            for participant in market["participants"]:
                for line in participant["lines"]:
                    for aff_id, price_obj in line["prices"].items():
                        key = (participant["id"], participant["name"])
                        results[key] = {
                            "value": line["value"],
                            "price": price_obj["price"],
                            "is_main_line": price_obj["is_main_line"],
                        }
    return results

opener_prices = extract_prices(opener_data)
current_prices = extract_prices(current_data)

print("Opening vs Current Spread:")
for (pid, name), opener in opener_prices.items():
    current = current_prices.get((pid, name))
    if current:
        print(f"  {name}: opened {opener['value']} ({opener['price']}) -> now {current['value']} ({current['price']})")
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Getting Live Odds" icon="signal" href="/guides/getting-live-odds">
    Fetch current odds for today's games
  </Card>

  <Card title="Player Props" icon="user" href="/guides/player-props">
    Historical data for prop markets too
  </Card>

  <Card title="Market IDs" icon="hashtag" href="/reference/markets">
    Full list of market types
  </Card>

  <Card title="Sportsbook IDs" icon="list" href="/reference/sportsbooks">
    All tracked sportsbooks
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.