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

# Getting Live Odds

> Fetch real-time moneylines, spreads, and totals from multiple sportsbooks using the V2 events endpoint.

This guide walks through fetching current odds for a sport, understanding the response structure, and filtering by sportsbook and market type.

## Fetching Events with Odds

The primary endpoint for odds data is:

```
GET /api/v2/sports/{sportID}/events/{date}
```

Use the `market_ids` parameter to specify which market types to include. The three core markets are:

| Market             | ID | Description       |
| ------------------ | -- | ----------------- |
| Moneyline          | 1  | Who wins the game |
| Point Spread       | 2  | Handicap / spread |
| Total (Over/Under) | 3  | Combined score    |

<Note>
  Always include `offset=300` to align the date boundary with US Central Time. Without it, games starting late at night may appear under the wrong date. The offset is in minutes (300 = 5 hours).
</Note>

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://therundown.io/api/v2/sports/4/events/2026-02-12?key=YOUR_API_KEY&market_ids=1,2,3&offset=300"
  ```

  ```python Python theme={null}
  import requests
  from datetime import date

  API_KEY = "YOUR_API_KEY"
  BASE_URL = "https://therundown.io/api/v2"

  response = requests.get(
      f"{BASE_URL}/sports/4/events/{date.today()}",
      params={
          "key": API_KEY,
          "market_ids": "1,2,3",
          "offset": "300",  # Central Time
      }
  )

  data = response.json()
  for event in data["events"]:
      home = event["teams"][1]["name"]
      away = event["teams"][0]["name"]
      print(f"{away} @ {home}")

      for market in event.get("markets", []):
          print(f"  Market: {market['name']} (ID: {market['market_id']})")
          for participant in market["participants"]:
              for line in participant["lines"]:
                  for aff_id, price in line["prices"].items():
                      print(f"    {participant['name']}: {price['price']} ({aff_id})")
  ```

  ```javascript JavaScript theme={null}
  const API_KEY = "YOUR_API_KEY";
  const BASE_URL = "https://therundown.io/api/v2";
  const today = new Date().toISOString().split("T")[0];

  const response = await fetch(
    `${BASE_URL}/sports/4/events/${today}?key=${API_KEY}&market_ids=1,2,3&offset=300`
  );
  const data = await response.json();

  for (const event of data.events) {
    console.log(`${event.teams[0].name} @ ${event.teams[1].name}`);

    for (const market of event.markets || []) {
      console.log(`  Market: ${market.name} (ID: ${market.market_id})`);
      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}: ${price.price} (${affId})`);
          }
        }
      }
    }
  }
  ```
</CodeGroup>

## Understanding the Response Structure

The V2 response follows a nested hierarchy:

```
event
  ├── score                        # Live status, clock, and current score
  └── markets[]                    # Array of market types (moneyline, spread, total, etc.)
        ├── market_id              # Numeric market identifier
        ├── name                   # Human-readable market name
        └── participants[]         # Teams or players in this market
              ├── id               # Unique participant identifier
              ├── type             # "TYPE_TEAM", "TYPE_PLAYER", or "TYPE_RESULT"
              ├── name             # Participant name (team or player)
              └── lines[]          # Available lines for this participant
                    ├── value      # Line value (e.g., "-3.5" for spread, empty for moneyline)
                    └── prices{}   # Map of affiliate_id → price object
                          ├── price        # American odds (e.g., -110, +150)
                          ├── is_main_line # Whether this is the primary line
                          └── updated_at   # When this price was last updated
```

Here is a condensed example response for a single event:

```json theme={null}
{
  "events": [
    {
      "event_id": "abc123",
      "sport_id": 4,
      "score": {
        "event_status": "STATUS_IN_PROGRESS",
        "score_away": 62,
        "score_home": 55,
        "display_clock": "4:32",
        "game_period": 2,
        "event_status_detail": "4:32 - 2nd",
        "updated_at": "2026-02-12T18:30:00Z"
      },
      "teams": [
        { "team_id": 1, "name": "Boston Celtics" },
        { "team_id": 2, "name": "Los Angeles Lakers" }
      ],
      "markets": [
        {
          "market_id": 1,
          "name": "Moneyline",
          "period_id": 0,
          "participants": [
            {
              "id": 1,
              "type": "TYPE_TEAM",
              "name": "Boston Celtics",
              "lines": [
                {
                  "value": "",
                  "prices": {
                    "19": { "price": -150, "is_main_line": true, "updated_at": "2026-02-12T18:30:00Z" },
                    "23": { "price": -145, "is_main_line": true, "updated_at": "2026-02-12T18:28:00Z" }
                  }
                }
              ]
            },
            {
              "id": 2,
              "type": "TYPE_TEAM",
              "name": "Los Angeles Lakers",
              "lines": [
                {
                  "value": "",
                  "prices": {
                    "19": { "price": 130, "is_main_line": true, "updated_at": "2026-02-12T18:30:00Z" },
                    "23": { "price": 125, "is_main_line": true, "updated_at": "2026-02-12T18:28:00Z" }
                  }
                }
              ]
            }
          ]
        },
        {
          "market_id": 2,
          "name": "Point Spread",
          "period_id": 0,
          "participants": [
            {
              "id": 1,
              "type": "TYPE_TEAM",
              "name": "Boston Celtics",
              "lines": [
                {
                  "value": "-3.5",
                  "prices": {
                    "19": { "price": -110, "is_main_line": true, "updated_at": "2026-02-12T18:30:00Z" }
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

Use `score.event_status` to identify live games, `score.display_clock` and `score.game_period` for the on-screen state, and `score.updated_at` to judge freshness. Some live score metadata, especially `venue_name` and `venue_location`, may be empty strings until the upstream feed provides them.

## Filtering by Sportsbook

Use the `affiliate_ids` parameter to limit results to specific sportsbooks. This reduces payload size and focuses on the books you care about.

```bash theme={null}
# Only DraftKings (19) and FanDuel (23)
curl "https://therundown.io/api/v2/sports/4/events/2026-02-12?\
key=YOUR_API_KEY&market_ids=1,2,3&affiliate_ids=19,23&offset=300"
```

Common affiliate IDs:

| Sportsbook   | ID |
| ------------ | -- |
| DraftKings   | 19 |
| FanDuel      | 23 |
| BetMGM       | 22 |
| theScore Bet | 24 |
| Pinnacle     | 3  |

See [Sportsbook IDs](/reference/sportsbooks) for the full list.

## Main Lines vs. Alternates

By default, the API returns both main lines and alternate lines. Alternate lines are additional spread/total values offered by sportsbooks (e.g., -2.5, -3.0, -3.5, -4.0 for a spread market).

To fetch only the primary line for each market, add `main_line=true`:

```bash theme={null}
curl "https://therundown.io/api/v2/sports/4/events/2026-02-12?\
key=YOUR_API_KEY&market_ids=1,2,3&main_line=true&offset=300"
```

When `main_line=true` is set, each participant will have at most one line object per market, and the `is_main_line` field on each price will be `true`. This is recommended for odds screens where you only need the consensus line.

## Handling the 0.0001 Sentinel Value

A price value of `0.0001` means the line is **off the board** -- the sportsbook has temporarily removed it. This commonly happens when:

* A key injury is being evaluated
* The book is recalculating after sharp action
* The market is approaching game time suspension

<Warning>
  Never display `0.0001` to end users or use it in calculations. Show "Off Board" or "N/A" instead.
</Warning>

<CodeGroup>
  ```python Python theme={null}
  def format_price(price_value):
      """Format a price for display, handling sentinel values."""
      if price_value == 0.0001:
          return "Off Board"
      if price_value > 0:
          return f"+{int(price_value)}"
      return str(int(price_value))


  # Usage
  for event in data["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():
                      display = format_price(price_obj["price"])
                      print(f"{participant['name']}: {display}")
  ```

  ```javascript JavaScript theme={null}
  function formatPrice(priceValue) {
    if (priceValue === 0.0001) return "Off Board";
    if (priceValue > 0) return `+${Math.round(priceValue)}`;
    return String(Math.round(priceValue));
  }

  // Usage
  for (const event of data.events) {
    for (const market of event.markets || []) {
      for (const participant of market.participants) {
        for (const line of participant.lines) {
          for (const [affId, priceObj] of Object.entries(line.prices)) {
            const display = formatPrice(priceObj.price);
            console.log(`${participant.name}: ${display}`);
          }
        }
      }
    }
  }
  ```
</CodeGroup>

## Full Working Example

Here is a complete example that fetches NBA odds, filters to DraftKings and FanDuel, and prints a formatted table:

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

  API_KEY = "YOUR_API_KEY"
  BASE_URL = "https://therundown.io/api/v2"
  BOOKS = {"19": "DraftKings", "23": "FanDuel"}

  response = requests.get(
      f"{BASE_URL}/sports/4/events/{date.today()}",
      params={
          "key": API_KEY,
          "market_ids": "1,2,3",
          "affiliate_ids": "19,23",
          "main_line": "true",
          "offset": "300",  # Central Time
      }
  )
  data = response.json()


  def fmt(price):
      if price == 0.0001:
          return "N/A"
      return f"+{int(price)}" if price > 0 else str(int(price))


  for event in data["events"]:
      away = event["teams"][0]["name"]
      home = event["teams"][1]["name"]
      print(f"\n{'=' * 60}")
      print(f"{away} @ {home}")
      print(f"{'=' * 60}")

      for market in event.get("markets", []):
          print(f"\n  {market['name']}:")
          for participant in market["participants"]:
              for line in participant["lines"]:
                  line_str = f" ({line['value']})" if line.get("value") else ""
                  prices_str = "  ".join(
                      f"{BOOKS.get(aid, aid)}: {fmt(p['price'])}"
                      for aid, p in line["prices"].items()
                  )
                  print(f"    {participant['name']}{line_str}: {prices_str}")
  ```

  ```javascript JavaScript theme={null}
  const API_KEY = "YOUR_API_KEY";
  const BASE_URL = "https://therundown.io/api/v2";
  const today = new Date().toISOString().split("T")[0];
  const BOOKS = { "19": "DraftKings", "23": "FanDuel" };

  const params = new URLSearchParams({
    key: API_KEY,
    market_ids: "1,2,3",
    affiliate_ids: "19,23",
    main_line: "true",
    offset: "300", // Central Time
  });

  const response = await fetch(`${BASE_URL}/sports/4/events/${today}?${params}`);
  const data = await response.json();

  function fmt(price) {
    if (price === 0.0001) return "N/A";
    return price > 0 ? `+${Math.round(price)}` : String(Math.round(price));
  }

  for (const event of data.events) {
    const away = event.teams[0].name;
    const home = event.teams[1].name;
    console.log(`\n${"=".repeat(60)}`);
    console.log(`${away} @ ${home}`);
    console.log(`${"=".repeat(60)}`);

    for (const market of event.markets || []) {
      console.log(`\n  ${market.name}:`);
      for (const participant of market.participants) {
        for (const line of participant.lines) {
          const lineStr = line.value ? ` (${line.value})` : "";
          const prices = Object.entries(line.prices)
            .map(([aid, p]) => `${BOOKS[aid] || aid}: ${fmt(p.price)}`)
            .join("  ");
          console.log(`    ${participant.name}${lineStr}: ${prices}`);
        }
      }
    }
  }
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Building an Odds Screen" icon="display" href="/guides/building-odds-screen">
    Turn this data into a full UI
  </Card>

  <Card title="WebSocket Streaming" icon="bolt" href="/guides/websocket-streaming">
    Get real-time updates instead of polling
  </Card>

  <Card title="Efficient Polling" icon="rotate" href="/guides/efficient-polling">
    Delta endpoints, cache TTLs, and polling intervals
  </Card>

  <Card title="Data Model" icon="diagram-project" href="/reference/data-model">
    How events, markets, lines, and prices relate
  </Card>

  <Card title="Historical Odds" icon="chart-line" href="/guides/historical-odds">
    Track line movement over time
  </Card>

  <Card title="Market IDs Reference" icon="hashtag" href="/reference/markets">
    All available market types
  </Card>
</CardGroup>
