Live Odds Scraper: NFL, NBA, MLB In-Play & Closing Lines
Pricing
$3.00 / 1,000 per odds line returneds
Live Odds Scraper: NFL, NBA, MLB In-Play & Closing Lines
Opening, closing and current moneyline, spread and total for every game in American, decimal and fractional odds, plus the in-play line while a game runs and the graded result after it ends. NFL, NCAAF, NCAAB, NBA, MLB, NHL, WNBA and 20 soccer leagues. No API key, no browser.
Pricing
$3.00 / 1,000 per odds line returneds
Rating
0.0
(0)
Developer
Scrapers Delight
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
7 days ago
Last modified
Categories
Share
🎯 Live Odds Scraper — In-Play & Closing Lines for NFL, NBA, MLB and 25 more leagues
Every game's betting line, including the two that other ESPN-based scrapers structurally cannot give you: the line while the game is running, and the line it closed at.
Opening, closing and current moneyline, spread and total for every game — in American, decimal and fractional odds — plus the in-play line while a game is live and the graded result (moneyline / against-the-spread / over-under) once it ends. 154 fields per row. No API key, no login, no browser, no proxy.
🧨 Why this one and not the other ESPN odds scrapers
Every other actor in this lane reads ESPN's public scoreboard feed. That feed has one property nobody mentions on their store page:
The scoreboard silently drops the odds the moment a game starts, and never brings them back.
Measured on the MLB slate of 2026-09-02 — 15 events:
| Game state | Events | Carrying odds on the scoreboard feed | Carrying odds on the core API |
|---|---|---|---|
| Scheduled | 1 | 1 | 1 |
| In progress | 5 | 0 | 5 (count = 2 each) |
| Final | 9 | 0 | 9 |
This actor reads the scoreboard for context (teams, venue, weather, score, status) and ESPN's core sports API for the line. That is where the extra data lives:
- a
closeblock appears on every game once it starts — the actual closing line, the number a betting model is graded against; - a second provider,
200— "DraftKings - Live Odds" — appears while the game is running, carrying the real in-play market.
Real example from that run: DET @ MIN, top of the 8th. Pre-match line MIN -120, total 8.5 (opened at 9). In-play line at the same instant: DET -1940, total 7.5. The scoreboard feed showed neither.
Reconciliation check on a 91-game college-football slate: the core API covered 91 of 91 games the scoreboard had odds for, and there were 0 games where the scoreboard had a line the core API lacked. It is a strict superset.
📊 What you get
- 🏈 28 leagues, all live-probed: NFL · NCAA Football · NBA · WNBA · NCAA Men's & Women's Basketball · MLB · NCAA Baseball · NHL · MLS · NWSL · Premier League · Championship · LaLiga · Serie A · Bundesliga · Ligue 1 · Eredivisie · Primeira Liga · Liga MX · Süper Lig · Argentine Liga Profesional · Brasileirão · Champions League · Europa League · Conference League · Libertadores · Leagues Cup
- 💰 Three price states per market —
open,close,current— for moneyline, spread and total - 🔢 Three odds formats on every price — American (
-175), decimal (1.57), fractional (4/7) - ⚡ The in-play line (
lineType: "live") while a game is running - 📈 Derived analytics: implied probability, no-vig probability, book hold % on both the moneyline and the total, open→current movement on all three markets, CLV (open→close in probability points), and line direction
- 🏆 Graded results once a game is final:
moneylineWinnerSide,spreadResultSide(ATS),totalResult(O/U) - 🏟️ Full game context: venue + city/state, weather, broadcaster, attendance, team records, live score, period and clock
- 🔗 Direct bet-slip deep links for home / away / over / under / game
⚙️ Input
| Field | Type | Default | What it does |
|---|---|---|---|
leagues | multi-select | NFL, NCAAF, NBA, MLB, NHL, NCAAM, MLS, EPL | Which leagues to pull. The enum is a hard-coded, live-probed list — ESPN's slugs are not guessable. |
dateFrom | string | today (US Eastern) | YYYY-MM-DD. Past dates work and are how you build a closing-line dataset. |
dateTo | string | — | YYYY-MM-DD. Blank ⇒ uses Days ahead. |
daysAhead | integer | 6 | Days past the start date when dateTo is blank. |
lineTypes | select | all | prematch, live, or both. |
gameStatus | select | any | scheduled / live / final. Use final for a CLV dataset. |
maxEvents | integer | 50 | Stop after N games, earliest kickoff first. 0 = unlimited. |
maxConcurrency | integer | 10 | Parallel requests. 10 measured clean. |
proxyConfiguration | proxy | off | Not needed — see Anti-bot below. |
⏱ Dates are US-Eastern, because that is how ESPN files a game. A 10pm-ET Tuesday game is 02:00 UTC Wednesday and ESPN still returns it for the Tuesday date. Taking "today" from UTC would skip the entire evening slate — every in-progress game, which is exactly where the in-play and closing lines live.
Example input
{"leagues": ["nfl", "college-football", "mlb", "usa.1", "eng.1"],"daysAhead": 3,"maxEvents": 25}
Closing-line-value dataset (settled games only, with graded results):
{"leagues": ["nfl", "nba", "mlb", "nhl"],"dateFrom": "2026-01-01","dateTo": "2026-01-31","gameStatus": "final","maxEvents": 0}
📐 Output — measured field fill
One row per game per line type. A game usually yields one row; a running game or a top-European-soccer match yields two.
All percentages below are measured on real Apify runs on 2026-09-03, not estimated.
- Run A — the default (empty input
{}):SUCCEEDEDin 3.1 s, 45 rows from 40 games across MLB / NCAAF / EPL / MLS. 4 in-play rows, 15 rows carrying a closing line.chargedEventCounts.odds_row= 45 = row count. - Run B — wide (10 leagues, 2026-08-29 → 2026-09-06,
maxEvents: 200):SUCCEEDEDin 3.8 s, 237 rows from 183 games. Books: DraftKings 183, Bet 365 50, DraftKings-Live-Odds 4. 132 rows (56%) carried a closing line.chargedEventCounts.odds_row= 237 = row count.
A% = fill on run A · B% = fill on run B.
Game identity & context
| Field | A% | B% | Notes |
|---|---|---|---|
eventId | 100 | 100 | ESPN event id |
sport | 100 | 100 | football / basketball / baseball / hockey / soccer |
league · leagueSlug · leagueAbbrev | 100 | 100 | e.g. National Football League / nfl / NFL |
seasonYear · seasonType | 100 | 100 | e.g. 2026 / regular-season |
week | 33 | 14 | NFL & college football only |
eventName · shortName | 100 | 100 | New England Patriots at Seattle Seahawks / NE @ SEA |
kickoffUtc | 100 | 100 | ISO 8601 |
statusName · statusState · statusDetail · statusCompleted | 100 | 100 | STATUS_IN_PROGRESS / in / Top 8th |
statusPeriod · statusClock | 93 | 86 | inning / quarter / period, and the clock |
neutralSite · conferenceCompetition | 93 | 50 | absent on soccer |
attendance | 100 | 100 | |
venueName · venueCity | 100 | 100 | |
venueState · venueIndoor | 93 | 50 | US venues |
venueCountry | 40 | 64 | |
broadcast | 100 | 100 | e.g. NBC |
weatherCondition · weatherTemperature | 73 | 18 | outdoor US games |
gameUrl | 78 | 96 | ESPN game page |
Teams & score
| Field | A% | B% | Notes |
|---|---|---|---|
homeTeamId · homeTeamAbbrev · homeTeamName · homeTeamDisplayName · homeTeamRecord | 100 | 100 | same five for away* |
homeScore · awayScore | 42 | 72 | null until the game starts — ESPN reports "0" for an unplayed game and this actor refuses to ship that as a 0-0 score |
Book & line type
| Field | A% | B% | Notes |
|---|---|---|---|
providerId · providerName · providerPriority | 100 | 100 | 100 DraftKings · 200 DraftKings - Live Odds · 2000 Bet 365 |
lineType | 100 | 100 | prematch or live |
Headline line
| Field | A% | B% | Notes |
|---|---|---|---|
details | 98 | 79 | e.g. SEA -3.5 |
spread · overUnder · overOdds · underOdds | 98 | 79 | |
initialSpread · initialOverUnder | 60 | 36 | |
moneylineWinner · spreadWinner | 100 | 100 | ⚠️ ESPN's own booleans — false on every game we sampled, including settled finals the favourite won. Shipped verbatim because they are real API fields; use moneylineWinnerSide / spreadResultSide for the actual graded result. |
homeFavorite · homeUnderdog · homeFavoriteAtOpen (+ away*) | 98 | 79 | |
homeMoneyline · awayMoneyline | 87 | 76 | flat American shortcut |
drawMoneyline | 7 | 49 | soccer only |
homeSpreadOdds · awaySpreadOdds | 38 | 43 |
Moneyline — open / close / current × American / decimal / fractional
| Field group | A% | B% |
|---|---|---|
homeMoneylineOpen{American,Decimal,Fraction} (+ away*) | 87 | 76 |
homeMoneylineClose{American,Decimal,Fraction} (+ away*) | 33 | 56 |
homeMoneylineCurrent{American,Decimal,Fraction} (+ away*) | 89 | 96 |
drawMoneylineCurrent{American,Decimal} | 7 | 49 |
drawMoneylineCurrentFraction | 2 | 21 |
Spread — the handicap line and the price on it
| Field group | A% | B% |
|---|---|---|
homeSpreadOpenLine · homeSpreadOpenOdds{American,Decimal,Fraction} (+ away*) | 98 | 79 |
homeSpreadCloseLine · homeSpreadCloseOdds{…} (+ away*) | 33 | 56 |
homeSpreadCurrentLine · homeSpreadCurrentOdds{…} (+ away*) | 98 | 79 |
*SpreadOpenLineis the handicap (-3.5);*SpreadOpenOddsAmericanis the juice on it (-110). Most competitors collapse these into one column and lose the price.
Total (over / under)
| Field group | A% | B% |
|---|---|---|
totalOpenLine · over/underOpen{American,Decimal,Fraction} | 98 | 79 |
totalCloseLine · over/underClose{…} | 33 | 56 |
totalCurrentLine · over/underCurrent{…} | 98 | 79 |
Bet-slip deep links
| Field | A% | B% |
|---|---|---|
bettingLinkHome · bettingLinkAway | 89 | 97 |
bettingLinkOver · bettingLinkUnder · bettingLinkGame | 98 | 79 |
Derived analytics
| Field | A% | B% | Definition |
|---|---|---|---|
impliedProbHome / Away / Draw | 89 / 89 / 7 | 96 / 96 / 49 | 100 / decimal, vig included |
noVigProbHome / Away / Draw | 89 / 89 / 7 | 96 / 96 / 49 | implied, normalised so the book's outcomes sum to 100% |
holdPercentMoneyline | 89 | 96 | the book's moneyline vig, in points |
holdPercentTotal | 98 | 79 | the book's vig on over/under |
moneylineMoveOpenToCurrent | 87 | 76 | home implied-probability move, percentage points (positive = home shortened). Points, not raw American, because American odds do not subtract across the ±100 boundary |
spreadMoveOpenToCurrent | 98 | 79 | homeSpreadCurrentLine − homeSpreadOpenLine |
totalMoveOpenToCurrent | 98 | 79 | totalCurrentLine − totalOpenLine |
clvHomeMoneylineOpenToClose | 33 | 56 | the CLV number — home implied-prob move open→close, in points |
lineDirection | 100 | 100 | home / away / none |
Graded result (null until the game is completed)
| Field | A% | B% | Values |
|---|---|---|---|
homeWon | 20 | 61 | true / false / null (draw) |
moneylineWinnerSide | 20 | 68 | home / away / draw |
spreadResultSide | 20 | 53 | home / away / push — graded against the closing line, falling back to current |
totalResult | 20 | 53 | over / under / push |
capturedAt | 100 | 100 | ISO timestamp of the snapshot |
37 of 154 fields are 100% filled on run B; not one field was 0% filled. Every sub-100% number above has a stated structural reason.
💵 Pricing
Pay per event — $0.003 per odds line returned. No monthly fee, no platform-usage surcharge.
| Event | odds_row — Per odds line returned |
| Price | $0.003 per row ($3 / 1,000) |
| Billed for | Each betting line delivered to your dataset |
| Never billed for | Starting a run · a game with no line posted (skipped before delivery) · the dataset write itself |
Billing is budget-aware: rows are delivered through Actor.pushData(items, 'odds_row'), which charges atomically as it pushes and stops cleanly at your charge cap. Delivered always equals billed — verified on the runs above (45 rows → odds_row: 45; 237 rows → odds_row: 237).
The current market rate on the Apify Store for the same event is $0.012 per row. This actor is 4× cheaper.
Worked example: a daily 06:00 snapshot of the default eight leagues ≈ 45–200 rows/day ≈ $4–18/month.
⚠️ Honest limits — what this does NOT do
1. ESPN carries ONE sportsbook. This is not a book-comparison product.
Verified across 26 events in 5 leagues: DraftKings (provider 100) only for NFL, NCAAF, NCAAB, MLB, NHL, NBA and MLS. Bet 365 (provider 2000) appears as a second book on top European and South American soccer only — measured on EPL, Championship, LaLiga, Serie A, Bundesliga, Ligue 1, Eredivisie, Primeira Liga, Süper Lig, Argentine Liga Profesional, Brasileirão, Champions League, Europa League, Conference League and Libertadores. Requests for other providers (/odds/58 ESPN BET, /odds/1002) return 404 "no instance found" on an NFL event. If you need a five-book line shop, this is the wrong actor — and no ESPN-based actor can honestly claim one.
2. The closing line is null until the game starts. A scheduled game's odds object carries open + current only; the close block appears at kickoff. Every *Close* field, and clvHomeMoneylineOpenToClose, is null on a pre-match row and is never silently mirrored from current. That is why close fill was 33% on the default (mostly upcoming games) and 56% on the wide backfill run.
3. Coverage is line-posting dependent, not game dependent. DraftKings posts most lines roughly a day out — MLB especially. Measured over an 8-day forward window: 8 of 100 MLB games carried a line; the other 92 did not have one posted yet. A far-future date range legitimately returns fewer rows than games. When that happens the run exits SUCCEEDED with a status message naming the reason ("No betting line posted yet… This is a real answer from ESPN, not a block"), never a failure, and never as "no results found".
4. Bet 365 rows are moneyline-only. Provider 2000 ships home/away/draw prices with no spread, no total and no open/close history. Those columns are null on a Bet 365 row by design — that is what ESPN returns, not a parsing gap.
5. ESPN's own moneylineWinner / spreadWinner booleans are dead. They read false on every completed game sampled, including a settled NFL final the favourite won. They are shipped verbatim as real API fields; the usable graded result is in moneylineWinnerSide / spreadResultSide / totalResult, computed here from the final score against the closing line.
6. Leagues deliberately excluded because ESPN carries no line for them: UFC/MMA (27 fights across three cards sampled — zero with odds, including past cards), PGA, F1, NCAA men's hockey and the NBA G League. They are absent from the enum rather than present and empty.
7. Player props and futures are out of scope. ESPN exposes a propBets collection on the same object (242 items on one NFL game) and an FPI predictor. Both are deliberately not scraped — bundling them would make this a combo actor rather than one that does one thing completely.
8. Two league slugs need per-day queries. mens-college-basketball and womens-college-basketball 404 on a date range and return 200 on a single date. The actor walks those day by day automatically, and falls back to day-by-day for any league that starts refusing a range.
🤖 Anti-bot / proxy — measured, not assumed
There is no anti-bot on this feed. No API key, no auth, no Cloudflare, no browser, no cookies.
| Path | Result |
|---|---|
| Home IP → scoreboard | 200, 250,817 bytes |
Apify proxy groups-AUTO → scoreboard | 200, 250,817 bytes — byte-identical |
Apify proxy groups-RESIDENTIAL,country-US → scoreboard | 200, 250,817 bytes, 0.96 s |
Burst: 30 core-API /odds calls @ concurrency 10 through the proxy | 30 × 200, 0 non-200, 1.5 s wall |
| 91-game college-football slate @ concurrency 10 | 91 × 200, 0 failures, 0.8 s |
The proxy option is therefore off by default. Turn it on only if your own network blocks ESPN.
📄 robots.txt
Neither API host serves a robots.txt. Quoted verbatim, fetched 2026-09-03:
$ curl -i https://sports.core.api.espn.com/robots.txtHTTP/1.1 403 Forbidden<html><head><title>403 Forbidden</title></head><body><center><h1>403 Forbidden</h1></center></body></html>$ curl -i https://site.api.espn.com/robots.txtHTTP/1.1 403 Forbidden<HTML><HEAD><TITLE>Access Denied</TITLE></HEAD><BODY><H1>Access Denied</H1>You don't have permission to access "http://site.api.espn.com/robots.txt" on this server.</BODY></HTML>
There is no crawl directive to honour on either host, and no rule on the separate www.espn.com robots.txt covers these API paths. Data returned is public, unauthenticated sports information. You are responsible for how you use it, and for gambling regulation in your own jurisdiction — this actor reports published prices and is not betting advice.
❓ FAQ
What is "closing line value" and why does this actor exist?
CLV is the gap between the price you took and the price the game closed at. It is the single best predictor of whether a betting model is actually profitable, and it is why the close block matters more than the current one. Scoreboard-based scrapers cannot give it to you, because that feed drops odds at kickoff.
Which sportsbook are these odds from? DraftKings for everything, plus Bet 365 as a second book on top European and South American soccer. See honest limit #1 — this is not a multi-book comparison tool.
Can I get the in-play line for a game happening right now?
Yes — set lineTypes: "live" and gameStatus: "live". Rows come back with lineType: "live" and providerName: "DraftKings - Live Odds".
Can I backfill historical closing lines?
Yes. Set dateFrom / dateTo to a past range and gameStatus: "final". Those rows carry the close block and the graded moneylineWinnerSide / spreadResultSide / totalResult.
How do I track line movement over time?
You don't need to. ESPN ships open, close and current inline on a single fetch — no cross-run diffing, no key-value store, no scheduled snapshots required for the basic move. Re-run on a schedule only if you want intraday granularity between open and close.
Why are odds in three formats?
American for US books, decimal for modelling (implied probability is 100 / decimal), fractional because ESPN ships it. All three are taken straight from the API where present and derived from each other where not.
Why did my run return fewer rows than games? The line was not posted yet for those games. See honest limit #3. The run's status message tells you exactly which case you hit.
Does it need a proxy? No. See the measured table above — home IP, Apify AUTO and Apify RESIDENTIAL all returned byte-identical payloads.
How fast is it? Measured: 45 rows in 3.1 s, 237 rows across 183 games and 10 leagues in 3.8 s, at concurrency 10.
What happens if ESPN changes the payload? There is an integrity gate: if the actor pulls 10+ odds objects and not one yields a moneyline, spread or total, it fails loudly rather than delivering a dataset of empty rows you would still be charged for.
Are the derived probabilities de-vigged?
Both are shipped. impliedProb* includes the book's margin; noVigProb* is normalised so the outcomes sum to 100%. holdPercentMoneyline and holdPercentTotal tell you how big that margin was.
🔧 Technical
| Data source | site.api.espn.com/apis/site/v2/sports/…/scoreboard (context) + sports.core.api.espn.com/v2/sports/…/odds (the line) |
| Auth | none |
| Runtime | Node 20, plain HTTP (got-scraping), 1024 MB, no browser |
| Concurrency | 10 by default |
| Zero-row behaviour | exits SUCCEEDED with an explanatory status message — a block and an empty slate are reported differently and are never conflated |