History
Three endpoints, all under /v1/historical/sports/{sport}/events/{id}/, all requiring the Scale plan or above (x-plan-min: scale). Scale covers fixtures that started in the last 14 days (or have not started yet); Business covers everything recorded. The archive begins in September 2026, so an earlier fixture has nothing recorded and the response says so explicitly rather than answering with an empty list.
| Endpoint | Returns |
|---|---|
/odds | Every recorded price between the opening and closing line: the full line movement. |
/openers | The first price each book showed for each market. |
/closing | The last price each book showed before the fixture started. |
Recorded vs. not recorded
A market is either genuinely absent from the archive (not_recorded) or it was seen and never repriced (no_movement) or it moved (recorded). Those three are never collapsed into one another: an empty points array only ever means not_recorded.
Example
GET
/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/odds?markets=spreads,totalsGET/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/openers?markets=spreads,h2h_h1GET/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/closing?markets=spreadscurl --request GET \
--url "https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/odds?markets=spreads,totals" \
--header "X-API-Key: ok_live_..."import requests
resp = requests.get(
"https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/odds",
params={"markets": "spreads,totals"},
headers={"X-API-Key": "ok_live_..."},
timeout=10,
)
resp.raise_for_status()
data = resp.json()
print(resp.headers["x-requests-remaining"])const res = await fetch(
"https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/odds?markets=spreads,totals",
{ headers: { "X-API-Key": "ok_live_..." } }
);
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET",
"https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/odds?markets=spreads,totals", nil)
req.Header.Set("X-API-Key", "ok_live_...")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}<?php
$ch = curl_init("https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/odds?markets=spreads,totals");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: ok_live_..."],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);curl --request GET \
--url "https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/openers?markets=spreads,h2h_h1" \
--header "X-API-Key: ok_live_..."import requests
resp = requests.get(
"https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/openers",
params={"markets": "spreads,h2h_h1"},
headers={"X-API-Key": "ok_live_..."},
timeout=10,
)
resp.raise_for_status()
data = resp.json()
print(resp.headers["x-requests-remaining"])const res = await fetch(
"https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/openers?markets=spreads,h2h_h1",
{ headers: { "X-API-Key": "ok_live_..." } }
);
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET",
"https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/openers?markets=spreads,h2h_h1", nil)
req.Header.Set("X-API-Key", "ok_live_...")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}<?php
$ch = curl_init("https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/openers?markets=spreads,h2h_h1");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: ok_live_..."],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);curl --request GET \
--url "https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/closing?markets=spreads" \
--header "X-API-Key: ok_live_..."import requests
resp = requests.get(
"https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/closing",
params={"markets": "spreads"},
headers={"X-API-Key": "ok_live_..."},
timeout=10,
)
resp.raise_for_status()
data = resp.json()
print(resp.headers["x-requests-remaining"])const res = await fetch(
"https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/closing?markets=spreads",
{ headers: { "X-API-Key": "ok_live_..." } }
);
if (!res.ok) throw new Error((await res.json()).error.code);
const data = await res.json();package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET",
"https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/closing?markets=spreads", nil)
req.Header.Set("X-API-Key", "ok_live_...")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}<?php
$ch = curl_init("https://oddslink.app/v1/historical/sports/nfl/events/nfl:falcons-panthers:2026-09-20/closing?markets=spreads");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: ok_live_..."],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);200 OKresponse.json
{
"id": "nfl:falcons-panthers:2026-09-20",
"sport_key": "nfl",
"sport_title": "NFL",
"commence_time": "2026-09-20T17:00:00Z",
"home_team": "Atlanta Falcons",
"away_team": "Carolina Panthers",
"archive_from": "2026-09-01T00:00:00Z",
"bookmakers": [
{
"key": "draftkings",
"title": "DraftKings",
"markets": [
{
"key": "spreads",
"movement": "recorded",
"points": [
{
"recorded_at": "2026-09-15T14:00:03Z",
"outcomes": [
{
"name": "Atlanta Falcons",
"price": -110,
"point": 3
},
{
"name": "Carolina Panthers",
"price": -110,
"point": -3
}
]
},
{
"recorded_at": "2026-09-18T18:08:20Z",
"outcomes": [
{
"name": "Atlanta Falcons",
"price": 100,
"point": 2.5
},
{
"name": "Carolina Panthers",
"price": -120,
"point": -2.5
}
]
}
]
},
{
"key": "totals",
"movement": "no_movement",
"points": [
{
"recorded_at": "2026-09-15T14:00:03Z",
"outcomes": [
{
"name": "Over",
"price": -108,
"point": 43.5
},
{
"name": "Under",
"price": -112,
"point": 43.5
}
]
}
]
},
{
"key": "h2h_h1",
"movement": "not_recorded",
"points": []
}
]
}
],
"unmatched_bookmakers": []
}{
"id": "nfl:falcons-panthers:2026-09-20",
"sport_key": "nfl",
"sport_title": "NFL",
"commence_time": "2026-09-20T17:00:00Z",
"home_team": "Atlanta Falcons",
"away_team": "Carolina Panthers",
"bookmakers": [
{
"key": "draftkings",
"title": "DraftKings",
"markets": [
{
"key": "spreads",
"status": "recorded",
"recorded_at": "2026-09-15T14:00:03Z",
"outcomes": [
{
"name": "Atlanta Falcons",
"price": -110,
"point": 3
},
{
"name": "Carolina Panthers",
"price": -110,
"point": -3
}
]
},
{
"key": "h2h_h1",
"status": "not_recorded",
"recorded_at": null,
"outcomes": []
}
]
}
],
"unmatched_bookmakers": []
}{
"id": "nfl:falcons-panthers:2026-09-20",
"sport_key": "nfl",
"sport_title": "NFL",
"commence_time": "2026-09-20T17:00:00Z",
"home_team": "Atlanta Falcons",
"away_team": "Carolina Panthers",
"bookmakers": [
{
"key": "draftkings",
"title": "DraftKings",
"markets": [
{
"key": "spreads",
"status": "recorded",
"recorded_at": "2026-09-20T16:59:41Z",
"outcomes": [
{
"name": "Atlanta Falcons",
"price": 100,
"point": 2.5
},
{
"name": "Carolina Panthers",
"price": -120,
"point": -2.5
}
]
}
]
}
],
"unmatched_bookmakers": []
}