> ## 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.

## Endpoints Overview

| Endpoint                                                  | Description                                              |
| --------------------------------------------------------- | -------------------------------------------------------- |
| `GET /api/v2/events/{eventID}/markets/history`            | Full price history across all markets for 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        |

## Full Market History

Use the full history endpoint to get every recorded price change for an event across all markets and sportsbooks.

```bash theme={null}
curl "https://therundown.io/api/v2/events/{eventID}/markets/history?\
key=YOUR_API_KEY&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`)   |

### Example Response

```json theme={null}
{
  "meta": {
    "event_id": "abc123",
    "count": 2
  },
  "history": [
    {
      "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": "initial",
      "updated_at": "2026-02-10T14:30: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": "-4",
      "price": "-110",
      "previous_price": "-105",
      "change_type": "price",
      "updated_at": "2026-02-11T09:15:00Z"
    }
  ]
}
```

## 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), and `c` (closed\_at).

```bash theme={null}
# Spread history (market_id=2) for a specific event
curl "https://therundown.io/api/v2/events/{eventID}/markets/2/history?\
key=YOUR_API_KEY&affiliate_ids=19"
```

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

  API_KEY = "YOUR_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",
      params={
          "key": API_KEY,
          "affiliate_ids": "19",
      }
  )

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

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

  ```javascript JavaScript theme={null}
  const API_KEY = "YOUR_API_KEY";
  const BASE_URL = "https://therundown.io/api/v2";
  const EVENT_ID = "abc123";

  const params = new URLSearchParams({
    key: API_KEY,
    affiliate_ids: "19",
  });

  const response = await fetch(
    `${BASE_URL}/events/${EVENT_ID}/markets/2/history?${params}`
  );
  const { series } = await response.json();

  // series is a map keyed by affiliate ID (string)
  console.log("Affiliate            | Timestamp                  | Price");
  console.log("-".repeat(65));
  for (const [affId, affSeries] of Object.entries(series)) {
    for (const point of affSeries.data) {
      console.log(`${affSeries.affiliate_name}  | ${point.t}  | ${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 "https://therundown.io/api/v2/events/{eventID}/markets/2/history?\
key=YOUR_API_KEY&affiliate_ids=19\
&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",
      params={
          "key": API_KEY,
          "affiliate_ids": "19",
          "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 JavaScript theme={null}
  const now = new Date();
  const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);

  const params = new URLSearchParams({
    key: API_KEY,
    affiliate_ids: "19",
    from: yesterday.toISOString(),
    to: now.toISOString(),
  });

  const response = await fetch(
    `${BASE_URL}/events/${EVENT_ID}/markets/2/history?${params}`
  );
  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 Lines

The openers endpoints return the first price posted by each sportsbook for each market. 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 "https://therundown.io/api/v2/sports/4/openers/2026-02-12?\
  key=YOUR_API_KEY&market_ids=1,2,3&affiliate_ids=19,23&offset=300"
  ```

  ```bash Single event openers theme={null}
  curl "https://therundown.io/api/v2/events/{eventID}/openers?\
  key=YOUR_API_KEY&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}",
      params={
          "key": API_KEY,
          "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 JavaScript theme={null}
  // All openers for a sport + date
  const response = await fetch(
    `${BASE_URL}/sports/${sportId}/openers/${today}?${new URLSearchParams({
      key: API_KEY,
      market_ids: "1,2,3",
      affiliate_ids: "19,23",
      offset: "300",
    })}`
  );
  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 final price posted before game time. The response uses the same V2 events structure as openers. Closing lines are widely considered the most efficient odds and are useful for evaluating betting performance.

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

  ```bash Single event closing lines theme={null}
  curl "https://therundown.io/api/v2/events/{eventID}/closing?\
  key=YOUR_API_KEY&market_ids=1,2,3&affiliate_ids=19,23"
  ```

  ```python Python theme={null}
  # All closing lines for a sport + date
  response = requests.get(
      f"{BASE_URL}/sports/{sport_id}/closing/{today}",
      params={
          "key": API_KEY,
          "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 JavaScript theme={null}
  // All closing lines for a sport + date
  const response = await fetch(
    `${BASE_URL}/sports/${sportId}/closing/${today}?${new URLSearchParams({
      key: API_KEY,
      market_ids: "1,2,3",
      affiliate_ids: "19,23",
      offset: "300",
    })}`
  );
  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 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, c}` points.

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

  API_KEY = "YOUR_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",
      params={
          "key": API_KEY,
          "affiliate_ids": "19,23",
      }
  )
  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 JavaScript theme={null}
  const API_KEY = "YOUR_API_KEY";
  const BASE_URL = "https://therundown.io/api/v2";
  const EVENT_ID = "abc123";

  const params = new URLSearchParams({
    key: API_KEY,
    affiliate_ids: "19,23",
  });

  const response = await fetch(
    `${BASE_URL}/events/${EVENT_ID}/markets/2/history?${params}`
  );
  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 requests

API_KEY = "YOUR_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",
    params={"key": API_KEY, "market_ids": "2", "affiliate_ids": "19"}
)

current_resp = requests.get(
    f"{BASE_URL}/events/{EVENT_ID}",
    params={"key": API_KEY, "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>
