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

# Historical Odds

> Fetch historical closing lines from Odds-API.io. Pull closing odds for whole leagues and seasons in one request, or event by event, for backtesting and model building.

## What are closing lines?

The closing line is the final price a bookmaker offered before an event started. It is the most informative number a bookmaker publishes, because it reflects every bet taken and every adjustment made right up to kick-off. Most quantitative work starts here: you compare your model's price, or the price you actually got, against the close.

Odds-API.io stores closing odds for settled events across every bookmaker and market we cover.

## Which endpoint should I use?

There are two ways to get closing lines, and picking the right one matters.

| Endpoint                       | Use it when                                                                     | Plans         |
| ------------------------------ | ------------------------------------------------------------------------------- | ------------- |
| `/v3/historical/closing-lines` | You want closing odds for many events at once, such as a whole league or season | Paid          |
| `/v3/historical/events`        | You only need the fixture list and results for a league and date range          | Free and paid |
| `/v3/historical/odds`          | You already have an event ID and want everything for that one event             | Free and paid |

If you are building a dataset, use `/v3/historical/closing-lines`. Pulling a season through the per-event endpoint means one request per match, which for five leagues over a season is roughly 1,900 requests. The same data comes back from the bulk endpoint in about 20.

## Fetching closing lines in bulk

`/v3/historical/closing-lines` takes a sport, up to 10 leagues, a date range and the markets you care about, and returns every settled event in that window with its closing odds attached.

### Query Parameters

| Parameter    | Required | Description                                                                            |
| ------------ | -------- | -------------------------------------------------------------------------------------- |
| `apiKey`     | Yes      | Your API key                                                                           |
| `sport`      | Yes      | Sport slug (e.g. `football`)                                                           |
| `leagues`    | Yes      | Comma-separated league slugs, maximum 10 (e.g. `england-premier-league,spain-la-liga`) |
| `from`       | Yes      | Start date in RFC3339 format (e.g. `2026-01-01T00:00:00Z`)                             |
| `to`         | Yes      | End date in RFC3339 format. Maximum 366 days after `from`                              |
| `markets`    | Yes      | Comma-separated market names (e.g. `ML,Totals`)                                        |
| `bookmakers` | Yes      | Comma-separated bookmaker names, maximum 30                                            |
| `limit`      | No       | Events per response. Capped at 100; use `limit` and `skip` to paginate                 |
| `skip`       | No       | Number of events to skip                                                               |

`markets` and `bookmakers` are both required here. A single call can otherwise cover 100 events across 30 bookmakers and every market, which produces a response nobody wants to download.

### Example

```bash theme={null}
curl "https://api.odds-api.io/v3/historical/closing-lines?apiKey=YOUR_API_KEY&sport=football&leagues=england-premier-league,spain-la-liga&from=2026-01-01T00:00:00Z&to=2026-01-31T23:59:59Z&markets=ML&bookmakers=Bet365,Unibet&limit=100"
```

The response is an array of events, each with the same shape returned by `/v3/historical/odds`:

```json theme={null}
[
  {
    "id": 123456,
    "home": "Arsenal",
    "away": "Chelsea",
    "date": "2026-01-14T20:00:00Z",
    "status": "settled",
    "sport": { "name": "Football", "slug": "football" },
    "league": { "name": "England - Premier League", "slug": "england-premier-league" },
    "scores": { "home": 2, "away": 1, "periods": [] },
    "bookmakers": {
      "Bet365": [
        {
          "name": "ML",
          "updatedAt": "2026-01-14T19:58:12Z",
          "odds": [{ "home": "2.10", "draw": "3.40", "away": "3.60" }]
        }
      ]
    }
  }
]
```

Events with no odds from your selected bookmakers still appear, with an empty `bookmakers` object. That is deliberate: you can tell the difference between a match we have no prices for and a match that never happened.

### Paginating a full season

The span limit is 366 days, so a whole season fits in one date range. Page through it with `skip`:

```python theme={null}
import requests

BASE = "https://api.odds-api.io/v3/historical/closing-lines"
params = {
    "apiKey": "YOUR_API_KEY",
    "sport": "football",
    "leagues": "england-premier-league",
    "markets": "ML",
    "bookmakers": "Bet365,Unibet",
    "limit": 100,
}

def fetch_season(start, end):
    events, skip = [], 0
    while True:
        page = requests.get(BASE, params={**params, "from": start, "to": end, "skip": skip}).json()
        events.extend(page)
        if len(page) < params["limit"]:
            return events
        skip += params["limit"]

season = fetch_season("2026-03-01T00:00:00Z", "2026-12-01T00:00:00Z")
print(f"{len(season)} events")
```

A short page means you have reached the end. A 240-match season is three requests at `limit=100`.

## Fetching a single event

If you already have an event ID, `/v3/historical/odds` returns the closing odds for that one event.

### Query Parameters

| Parameter    | Required | Description                                                    |
| ------------ | -------- | -------------------------------------------------------------- |
| `apiKey`     | Yes      | Your API key                                                   |
| `eventId`    | Yes      | Event ID from `/v3/historical/events`                          |
| `bookmakers` | Yes      | Comma-separated bookmaker names, maximum 30                    |
| `markets`    | No       | Comma-separated market names. Returns all markets when omitted |

```bash theme={null}
curl "https://api.odds-api.io/v3/historical/odds?apiKey=YOUR_API_KEY&eventId=123456&bookmakers=Bet365&markets=ML"
```

Adding `markets` is worth doing even when you do not strictly need it. A busy football match carries hundreds of markets per bookmaker, so filtering server-side keeps responses small.

## Finding events

`/v3/historical/events` returns settled events for one league and date range, with final scores. Use it when you want results and fixtures rather than odds.

### Query Parameters

| Parameter | Required | Description                                              |
| --------- | -------- | -------------------------------------------------------- |
| `apiKey`  | Yes      | Your API key                                             |
| `sport`   | Yes      | Sport slug (e.g. `football`)                             |
| `league`  | Yes      | A single league slug                                     |
| `from`    | Yes      | Start date in RFC3339 format                             |
| `to`      | Yes      | End date in RFC3339 format. Maximum 31 days after `from` |

```bash theme={null}
curl "https://api.odds-api.io/v3/historical/events?apiKey=YOUR_API_KEY&sport=football&league=england-premier-league&from=2026-01-01T00:00:00Z&to=2026-01-31T23:59:59Z"
```

## Limits

| Limit      | Value                | Applies to                                            |
| ---------- | -------------------- | ----------------------------------------------------- |
| Date span  | 366 days per request | `/v3/historical/closing-lines`                        |
| Date span  | 31 days per request  | `/v3/historical/events`                               |
| Leagues    | 10 per request       | `/v3/historical/closing-lines`                        |
| Bookmakers | 30 per request       | `/v3/historical/closing-lines`, `/v3/historical/odds` |
| Events     | 100 per response     | `/v3/historical/closing-lines`                        |
| Events     | 1000 per response    | `/v3/historical/events`                               |

## Plan access

`/v3/historical/events` and `/v3/historical/odds` are available on every plan, including the free one, so you can evaluate the data before committing.

`/v3/historical/closing-lines` is a paid feature. Free plans calling it get a `403` explaining the alternative. You can [upgrade in the dashboard](https://odds-api.io/dashboard).

## Notes on the data

Closing odds go back to **December 2025**. Events before that are returned by `/v3/historical/events` with their scores, but carry no odds, so they come back with an empty `bookmakers` object.

Only events with a status of `settled` are returned. If a match has finished but has not settled in our pipeline yet, it will not appear.

The core market names are the same across sports: `ML` (moneyline / match winner), `Totals` and `Spread`, each with half-time variants such as `ML HT` and `Totals HT`.

Beyond those, availability depends on the sport. Football adds `Correct Score`, `Both Teams To Score`, `Double Chance`, `Half Time / Full Time` and `European Handicap`. Basketball adds `Player Props` and `3-Way Result`. Tennis uses `Totals (Games)` and `Spread (Games)` alongside per-set markets like `ML 1st Set`.

Matching is case-insensitive, so `ml` and `ML` both work, but the name must match in full. There is no market called "Match Winner", and no bare "Over/Under" or "Asian Handicap", though longer names such as `Goals Over/Under` and `Alternative Asian Handicap` do exist.

To see exactly which markets an event carries, call `/v3/historical/odds` for it without a `markets` filter.
