ESPN Odds & Line Movement Scraper
Pricing
from $2.00 / 1,000 odds rows
ESPN Odds & Line Movement Scraper
Betting lines for NFL, NBA, MLB, NHL, NCAA, WNBA and soccer, back to 2014 and up to 17 sportsbooks a game. Moneyline, spread and total with opening and closing prices, de-vigged fair odds, the book's hold, line movement, and how every bet settled — computed from the final score.
Pricing
from $2.00 / 1,000 odds rows
Rating
0.0
(0)
Developer
Elena Vance
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Betting lines for NFL, NBA, MLB, NHL, NCAA football and basketball, WNBA and soccer — moneyline, spread and total, from every sportsbook ESPN carries, back to 2014.
One row is one book's complete pricing of one game: the opening line and the closing line, the margin the book charged, the de-vigged fair price, how far the number moved, and — for finished games — whether each bet actually won.
gameDate league game book ML home spread open move total ATS2024-01-15 nba HOU @ PHI DraftKings -355 -8.0 -7.5 -0.5 229.0 home2024-01-15 nba HOU @ PHI MGM -350 -8.0 -7.0 -1.0 228.5 home2024-01-15 nba HOU @ PHI Caesars (NJ) -355 -8.0 -7.0 -1.0 228.5 home
What this does that the other ESPN scrapers cannot
Historical odds live on one ESPN host, and it is not the obvious one. The
scoreboard endpoint every other ESPN Actor is built on returns odds: [] for
any game that has finished — verified on completed MLB games from 2020, 2024
and 2025 — and so does the game-summary endpoint. Only
sports.core.api.espn.com keeps them, and it keeps every book that ever
priced the game.
That difference is the product:
| Scoreboard-based Actors | This Actor | |
|---|---|---|
| Today's slate | ✅ | ✅ |
| A game from last season | ❌ empty | ✅ |
| Books per game | 1 | up to 17 |
| Opening line | ❌ | ✅ (2024 onward) |
| Bet settlement | ❌ | ✅ computed |
Bet settlement is computed, not copied. Every ESPN odds item carries
moneylineWinner and spreadWinner. Across 691 provider rows on 67 finished
games in three leagues, both were false every single time — including the
395 where the home team won. They carry no information at all. This Actor
ignores them and settles from the final score instead, with pushes handled
properly and a soccer draw kept distinct from a tie.
The betting maths is done for you. Implied probability, the book's overround, its vig and its hold, de-vigged fair probabilities that sum to exactly 1, the fair price at zero margin, line movement in points and in probability, and closing-line value as a percentage.
What one row looks like
{"recordId": "401585183-40","gameDate": "2024-01-15","league": "nba","shortName": "HOU @ PHI","home": { "abbreviation": "PHI", "score": 124 },"away": { "abbreviation": "HOU", "score": 115 },"provider": { "id": "40", "name": "DraftKings", "kind": "sportsbook" },"moneyline": {"home": { "open": { "american": -285, "decimal": 1.3509, "impliedProbability": 0.7402 },"current": { "american": -355, "decimal": 1.2817 },"fairProbability": 0.7468, "fairAmerican": -295,"clvPercent": 5.4 },"away": { "current": { "american": 278 }, "fairProbability": 0.2532 },"draw": { "current": { "american": null } }, // soccer only"holdPercent": 4.28, "vigPercent": 4.48, "overround": 1.0448},"spread": { "openLine": -7.5, "currentLine": -8.0, "movementPoints": -0.5 },"total": { "openLine": 228.5, "currentLine": 229.0, "movementPoints": 0.5 },"settlement": {"isFinal": true, "homeMargin": 9, "combinedScore": 239,"moneyline": "home", "spread": "home", "total": "over","homeSpreadProfitUnits": 0.9091, "awaySpreadProfitUnits": -1.0},"dataQuality": { "snapshots": ["current", "open"], "hasOpeningLine": true }}
Full field reference: .actor/dataset_schema.json — 196 fields, all documented,
all nullable.
Input
| Field | Default | What it does |
|---|---|---|
leagues | NFL, NBA, MLB, NHL | 16 leagues to choose from. |
customLeagues | — | Any other ESPN competition as "soccer/ned.1". |
dateFrom / dateTo | a 5-day window around today | Inclusive. Up to 400 days per run. |
eventIds | — | Specific games, by the id in their espn.com URL. |
providers | every book | Filter by name or id. |
includeModelProviders | false | Add ESPN's forecasting partners. |
onlyCompleted | false | Keep only games with a settled result. |
maxItems | 200 | One row is one book per game. |
maxItems is the cost lever and it is easy to underestimate: a 2026 game
produces 1 row and a 2021 game produces ten, so a date range costs far
more rows than it has games.
Examples
Yesterday's closing lines from every book:
{ "leagues": ["nba"], "dateFrom": "2025-01-15", "dateTo": "2025-01-15" }
A season of NFL history to backtest against — every book, settled:
{ "leagues": ["nfl"], "dateFrom": "2023-09-07", "dateTo": "2024-01-08","onlyCompleted": true, "maxItems": 20000 }
One specific game:
{ "leagues": ["nba"], "eventIds": ["401585183"] }
What ESPN actually has, by era
Measured on the 15 January slate of every NBA season:
| Season | Sportsbooks per game | Opening lines |
|---|---|---|
| 2012 and earlier | none | — |
| 2013 | none — forecasting models only | no |
| 2014 – 2023 | 6 – 13 | no |
| 2024 | ~11 | yes |
| 2025 | 2 (ESPN BET) | yes |
| 2026 | 1 (DraftKings) | yes |
Two consequences worth planning around:
- Multi-book comparison is a historical product. ESPN carried a dozen books through 2024 and its own single book afterwards. For line shopping across today's market you want a book-by-book scraper; for eleven years of closing lines from a dozen books, this is the only place they exist.
- Opening lines start in 2024. Earlier seasons carry the closing line only,
so
movementPointsandclvPercentare null there.dataQuality.hasOpeningLinesays so per row rather than leaving you to infer it.
Known ESPN quirks, handled
| Quirk | What it would do | What this Actor does |
|---|---|---|
moneylineWinner / spreadWinner always false | A "did it win" column that is wrong half the time | Not republished; settlement computed from the score |
close.pointSpread sometimes holds a price (provider 58, some 2023 games) | A closing spread of -115; a closing total of -115 | close is never read; dataQuality.closeDisagreesWithCurrent flags the games |
details names the favourite, not the home team | A line whose sign points at an unknown team | Passed through as a label; spread.currentLine is always home-relative |
Provider id 1001 is accuscore in the NBA and Bet365 in the NHL | A forecasting model billed as a sportsbook | Classified by name, with the league carried alongside |
| Soccer has a draw | A three-way market de-vigged as two-way reports a negative hold | Three-way markets priced and settled as three outcomes |
Provider 2000 quotes decimal odds and no American | Null prices for a book that published them | Both directions converted |
| A game listed on two calendar dates | Priced — and billed — twice | De-duplicated on the competition id |
Notes
- No API key, no proxy, no browser. ESPN's public API has no bot defence: 42 concurrent requests at concurrency 24 all returned 200 with no throttling. A proxy option exists for the case your own network is the problem.
- Odds requests take about 1.7 seconds each regardless of load, so a wide date range is bounded by latency. Runs stop cleanly at a 210-second budget and report how many games went unpriced.
- Unofficial and not affiliated with ESPN or any sportsbook. Informational use only — check the rules that apply where you are before betting.
Built and verified against the live API on 2026-09-08;
docs/architecture.md carries every measurement behind the claims above.