Live Odds Scraper: NFL, NBA, MLB In-Play & Closing Lines avatar

Live Odds Scraper: NFL, NBA, MLB In-Play & Closing Lines

Pricing

$3.00 / 1,000 per odds line returneds

Go to Apify Store
Live Odds Scraper: NFL, NBA, MLB In-Play & Closing Lines

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

Scrapers Delight

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

7 days ago

Last modified

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 stateEventsCarrying odds on the scoreboard feedCarrying odds on the core API
Scheduled111
In progress505 (count = 2 each)
Final909

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 close block 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

FieldTypeDefaultWhat it does
leaguesmulti-selectNFL, NCAAF, NBA, MLB, NHL, NCAAM, MLS, EPLWhich leagues to pull. The enum is a hard-coded, live-probed list — ESPN's slugs are not guessable.
dateFromstringtoday (US Eastern)YYYY-MM-DD. Past dates work and are how you build a closing-line dataset.
dateTostringYYYY-MM-DD. Blank ⇒ uses Days ahead.
daysAheadinteger6Days past the start date when dateTo is blank.
lineTypesselectallprematch, live, or both.
gameStatusselectanyscheduled / live / final. Use final for a CLV dataset.
maxEventsinteger50Stop after N games, earliest kickoff first. 0 = unlimited.
maxConcurrencyinteger10Parallel requests. 10 measured clean.
proxyConfigurationproxyoffNot 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 {}): SUCCEEDED in 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): SUCCEEDED in 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

FieldA%B%Notes
eventId100100ESPN event id
sport100100football / basketball / baseball / hockey / soccer
league · leagueSlug · leagueAbbrev100100e.g. National Football League / nfl / NFL
seasonYear · seasonType100100e.g. 2026 / regular-season
week3314NFL & college football only
eventName · shortName100100New England Patriots at Seattle Seahawks / NE @ SEA
kickoffUtc100100ISO 8601
statusName · statusState · statusDetail · statusCompleted100100STATUS_IN_PROGRESS / in / Top 8th
statusPeriod · statusClock9386inning / quarter / period, and the clock
neutralSite · conferenceCompetition9350absent on soccer
attendance100100
venueName · venueCity100100
venueState · venueIndoor9350US venues
venueCountry4064
broadcast100100e.g. NBC
weatherCondition · weatherTemperature7318outdoor US games
gameUrl7896ESPN game page

Teams & score

FieldA%B%Notes
homeTeamId · homeTeamAbbrev · homeTeamName · homeTeamDisplayName · homeTeamRecord100100same five for away*
homeScore · awayScore4272null 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

FieldA%B%Notes
providerId · providerName · providerPriority100100100 DraftKings · 200 DraftKings - Live Odds · 2000 Bet 365
lineType100100prematch or live

Headline line

FieldA%B%Notes
details9879e.g. SEA -3.5
spread · overUnder · overOdds · underOdds9879
initialSpread · initialOverUnder6036
moneylineWinner · spreadWinner100100⚠️ 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*)9879
homeMoneyline · awayMoneyline8776flat American shortcut
drawMoneyline749soccer only
homeSpreadOdds · awaySpreadOdds3843

Moneyline — open / close / current × American / decimal / fractional

Field groupA%B%
homeMoneylineOpen{American,Decimal,Fraction} (+ away*)8776
homeMoneylineClose{American,Decimal,Fraction} (+ away*)3356
homeMoneylineCurrent{American,Decimal,Fraction} (+ away*)8996
drawMoneylineCurrent{American,Decimal}749
drawMoneylineCurrentFraction221

Spread — the handicap line and the price on it

Field groupA%B%
homeSpreadOpenLine · homeSpreadOpenOdds{American,Decimal,Fraction} (+ away*)9879
homeSpreadCloseLine · homeSpreadCloseOdds{…} (+ away*)3356
homeSpreadCurrentLine · homeSpreadCurrentOdds{…} (+ away*)9879

*SpreadOpenLine is the handicap (-3.5); *SpreadOpenOddsAmerican is the juice on it (-110). Most competitors collapse these into one column and lose the price.

Total (over / under)

Field groupA%B%
totalOpenLine · over/underOpen{American,Decimal,Fraction}9879
totalCloseLine · over/underClose{…}3356
totalCurrentLine · over/underCurrent{…}9879
FieldA%B%
bettingLinkHome · bettingLinkAway8997
bettingLinkOver · bettingLinkUnder · bettingLinkGame9879

Derived analytics

FieldA%B%Definition
impliedProbHome / Away / Draw89 / 89 / 796 / 96 / 49100 / decimal, vig included
noVigProbHome / Away / Draw89 / 89 / 796 / 96 / 49implied, normalised so the book's outcomes sum to 100%
holdPercentMoneyline8996the book's moneyline vig, in points
holdPercentTotal9879the book's vig on over/under
moneylineMoveOpenToCurrent8776home implied-probability move, percentage points (positive = home shortened). Points, not raw American, because American odds do not subtract across the ±100 boundary
spreadMoveOpenToCurrent9879homeSpreadCurrentLine − homeSpreadOpenLine
totalMoveOpenToCurrent9879totalCurrentLine − totalOpenLine
clvHomeMoneylineOpenToClose3356the CLV number — home implied-prob move open→close, in points
lineDirection100100home / away / none

Graded result (null until the game is completed)

FieldA%B%Values
homeWon2061true / false / null (draw)
moneylineWinnerSide2068home / away / draw
spreadResultSide2053home / away / push — graded against the closing line, falling back to current
totalResult2053over / under / push
capturedAt100100ISO 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.

Eventodds_rowPer odds line returned
Price$0.003 per row ($3 / 1,000)
Billed forEach betting line delivered to your dataset
Never billed forStarting 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.

PathResult
Home IP → scoreboard200, 250,817 bytes
Apify proxy groups-AUTO → scoreboard200, 250,817 bytes — byte-identical
Apify proxy groups-RESIDENTIAL,country-US → scoreboard200, 250,817 bytes, 0.96 s
Burst: 30 core-API /odds calls @ concurrency 10 through the proxy30 × 200, 0 non-200, 1.5 s wall
91-game college-football slate @ concurrency 1091 × 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.txt
HTTP/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.txt
HTTP/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 sourcesite.api.espn.com/apis/site/v2/sports/…/scoreboard (context) + sports.core.api.espn.com/v2/sports/…/odds (the line)
Authnone
RuntimeNode 20, plain HTTP (got-scraping), 1024 MB, no browser
Concurrency10 by default
Zero-row behaviourexits SUCCEEDED with an explanatory status message — a block and an empty slate are reported differently and are never conflated