Pinnacle closing odds API
The closing line, actually at the close.
With a freshness flag so you know.
Most closing-odds sources have a problem nobody talks about: the feed cuts off long before kickoff and the row labeled “close” isn't the close. PropLine's /odds/closing returns the last snapshot at or before commence_time per (book, market, outcome), and stamps each row with closing_age_seconds + is_stale— so you can drop dead rows instead of grading CLV against a line that died at 5 PM for a 9 PM start.
Hobby $9/mo unlocks /odds/closing with the full freshness fields. Free tier is redacted (you see the structure, prices are nulled).
Where everyone else falls short
If you've ever tried to measure CLV by “just grab the last row from the SBR archive,” you've already hit this. Here's the honest landscape.
| Source | Why it's not enough |
|---|---|
| SBR (sportsbookreview.com) | Free, but the feed routinely stops 1–3 hours before kickoff. The 'latest' row is whatever they happened to capture before the cutoff — not the actual close. Their own forum members warn about this. |
| OddsPortal | Decent archive, but no API and no per-book breakdown. Scraping it is fragile and the close is usually the consensus line, not Pinnacle specifically. |
| Manual screen-scrape of Pinnacle | Doable if you run a polling job, but Pinnacle's guest API isn't documented, and you still have to roll your own 'pick the last snapshot at or before kickoff' query. |
| the-odds-api / OddsJam | Neither carries Pinnacle in their API tier. Closing-line value against a soft book is circular — that's the line you bet against, not the line you grade against. |
| PropLine /odds/closing | One call per event returns the last snapshot at or before commence_time, per (book, market, outcome) — with closing_age_seconds + is_stale so you can drop rows whose feed died early. |
What you get back — freshness fields per outcome
One call per event. One row per (book, market, outcome). Each row carries the snapshot timestamp, how many seconds before kickoff it was recorded, and a boolean stale flag at the 10-minute threshold (late steam clusters in the last 10 minutes, so older rows miss the close).
# The whole closing line, in one call
GET https://api.prop-line.com/v1/sports/baseball_mlb/events/123/odds/closing
?markets=h2h,pitcher_strikeouts
&apiKey=YOUR_KEY
→ {
"id": "123",
"sport_key": "baseball_mlb",
"home_team": "Yankees",
"away_team": "Red Sox",
"commence_time": "2026-05-24T23:05:00Z",
"bookmakers": [
{
"key": "pinnacle", "title": "Pinnacle",
"markets": [
{ "key": "h2h", "outcomes": [
{
"name": "Yankees",
"price": -135,
"closing_at": "2026-05-24T23:04:12Z", # 48s before kickoff
"closing_age_seconds": 48,
"is_stale": false # fresh
},
{
"name": "Red Sox",
"price": +118,
"closing_at": "2026-05-24T23:04:12Z",
"closing_age_seconds": 48,
"is_stale": false
}
]}
]
},
{
"key": "draftkings", "title": "DraftKings",
"markets": [
{ "key": "h2h", "outcomes": [
{
"name": "Yankees",
"price": -125,
"closing_at": "2026-05-24T21:47:31Z", # 78m before kickoff
"closing_age_seconds": 4_649,
"is_stale": true # DK stopped quoting
}
]}
]
}
]
}
Python SDK — grade CLV in 12 lines
from propline import Client
c = Client(api_key="YOUR_KEY")
close = c.get_event_odds_closing(
sport="baseball_mlb",
event_id="123",
markets="h2h,pitcher_strikeouts",
)
# Grade your bet against Pinnacle's last fresh snapshot
for bm in close["bookmakers"]:
if bm["key"] != "pinnacle":
continue
for m in bm["markets"]:
for o in m["outcomes"]:
if o["is_stale"]:
continue # skip the dead close
print(o["name"], o["price"], "closing_at:", o["closing_at"])
Or grab the open-source CLV tracker
We built and open-sourced propline-clv-tracker — a CLI that takes a CSV of your bets, fetches /odds/closing per event, and prints CLV% + EV$ per bet plus a portfolio summary (avg + stake-weighted CLV). Two-minute setup. Free to use; the data behind it is the Hobby tier.
# 1. Clone
git clone https://github.com/proplineapi/propline-clv-tracker.git
cd propline-clv-tracker && npm install
# 2. Run against your bet log
PROPLINE_API_KEY=YOUR_KEY npm start -- bets.csv
→ bet 1 │ MLB Yankees -135 │ closing -148 │ CLV +5.2%
bet 2 │ NBA Celtics -110 │ closing -115 │ CLV +2.1%
Portfolio: 24 bets │ avg CLV +3.6% │ stake-weighted +4.1%FAQ
How is this different from /odds/history?
/odds/history returns every snapshot we recorded. /odds/closing is the one snapshot you actually want for CLV — the last one at or before commence_time, per (book, market, outcome). One call, one row per outcome, with a freshness flag. No post-processing on your end.
What exactly does closing_age_seconds measure?
Seconds between the closing snapshot's recorded_at timestamp and commence_time. 0 means the snapshot was taken at kickoff. 4,649 means the book stopped quoting 78 minutes early. Late steam clusters in the last 10 minutes, so anything beyond 600 seconds (is_stale=true) misses meaningful pre-game movement.
Why Pinnacle specifically?
Pinnacle's low margin and high limits mean its closing line is the closest thing to a fair-market consensus in sports betting. CLV measured against a soft book is mostly noise; CLV measured against Pinnacle's no-vig fair line is the industry-standard benchmark. We carry Pinnacle on every event — most other API providers don't.
Do you cover Pinnacle for everything?
Pinnacle's guest API exposes game lines (h2h / spreads / totals) across MLB, NBA, NHL, plus 27 soccer leagues. Player-prop depth is concentrated in MLB (batter total bases, batter home runs, pitcher strikeouts, hits allowed, earned runs, pitching outs). NHL adds goalie saves O/U. That's the honest shape — and we expose every bit of it.
What's the cheapest tier with /odds/closing?
Hobby at $9/mo. Free is redacted (you see the structure but not the prices). Hobby unlocks 5,000 req/day + the full closing data + 30-day event-age cap. Pro at $19 raises both.
Grade your bets against the actual close
Free tier covers 1,000 req/day (redacted closing data). Hobby at $9/mo unlocks full /odds/closing with freshness fields + 5,000 req/day. Pro at $19 raises the cap + extends history to 90 days.
Free tier includes 1,000 requests/day. Upgrade anytime.