Sports Scores & Schedules API — NFL, NBA, MLB, NHL, Soccer avatar

Sports Scores & Schedules API — NFL, NBA, MLB, NHL, Soccer

Pricing

from $0.48 / 1,000 games

Go to Apify Store
Sports Scores & Schedules API — NFL, NBA, MLB, NHL, Soccer

Sports Scores & Schedules API — NFL, NBA, MLB, NHL, Soccer

Scores, schedules, standings, teams and headlines for the NFL, NBA, MLB, NHL, WNBA, college sports, 30+ soccer leagues, F1 and more, as clean rows from ESPN's public feed. Odds, venues, broadcasts, records. No API key, no player data. $0.80 per 1,000 games.

Pricing

from $0.48 / 1,000 games

Rating

0.0

(0)

Developer

Insight Solutions

Insight Solutions

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

9 days ago

Last modified

Share

Scores, schedules, standings, teams and headlines for any league ESPN covers — the NFL, NBA, MLB, NHL, WNBA, college football and basketball, 33 soccer competitions from the Premier League to the Champions League, F1, NASCAR, golf, tennis and the UFC — as clean, flat rows in one schema. Live and final scores, status, records, venues, broadcasts and the public pre-game line on every game. No API key, no login, no browser.

Where the data comes from. This Actor reads ESPN's public site API (site.api.espn.com) — an unofficial, public JSON feed: keyless, undocumented, used by many tools and stable for years, but the format may change without notice. This Actor is not affiliated with ESPN, any league or any team. It returns no player data: teams, games, scores, venues, records, odds and standings only.

At a glance

Input — this is the Store prefill; paste it and run:

{ "leagues": ["nfl", "nba"], "mode": "scoreboard" }

Every game on today's NFL and NBA slates, in two requests. Two leagues, so that one of them is always in season. Change mode for the other four: schedule, standings, teams, news.

Output — one row per game; the fields you will use most are startTime, name, status, homeTeam, homeScore, awayTeam, awayScore, venue, broadcasts and spread (full list under Output reference). Standings, teams and headlines come back on the same columns. Anything that could not be read comes back as a free diagnostic row (rowType: "diagnostic", errorType, error) instead of a charge.

Price — $0.80 per 1,000 games on the FREE tier (standings rows and teams $0.0004, headlines $0.0003, + $0.001 per run, charged only once a run has returned something). League summary rows, diagnostic rows, a league that is out of season and a request ESPN refused are all free, and a run that returns nothing bills nothing at all. Pay-per-event, limited permissions — works over the Apify MCP server (mcp.apify.com) and with agentic (x402) payments.

From code — client.actor("insight.solutions/sports-scores-api").call(run_input={…}) with apify-client, or

POST https://api.apify.com/v2/acts/insight.solutions~sports-scores-api/run-sync-get-dataset-items
.


What you get

One row per game. Same columns whatever the league.

A game that has not started yet (the NFL's Monday night game, 2026-10-05):

{
"rowType": "game",
"league": "nfl",
"sport": "football",
"gameId": "401872979",
"season": 2026,
"seasonType": "regular-season",
"week": 4,
"startTime": "2026-10-06T00:15:00.000Z",
"startTimeLocal": "Mon, October 5th at 8:15 PM EDT",
"name": "Atlanta Falcons at New Orleans Saints",
"shortName": "ATL @ NO",
"eventType": "game",
"status": "scheduled",
"completed": false,
"homeTeam": "New Orleans Saints",
"homeAbbr": "NO",
"homeScore": null,
"homeRecord": "1-2",
"awayTeam": "Atlanta Falcons",
"awayAbbr": "ATL",
"awayScore": null,
"awayRecord": "1-2",
"venue": "Caesars Superdome",
"venueCity": "New Orleans",
"venueState": "LA",
"indoor": true,
"broadcasts": ["ESPN"],
"spread": "NO -1.5",
"overUnder": 47.5,
"oddsProvider": "DraftKings",
"weather": "Cloudy",
"temperatureF": 79,
"url": "https://www.espn.com/nfl/game/_/gameId/401872979/falcons-saints"
}

The same game once it is over carries the score, the winner and the gate — here Chiefs at Raiders the day before:

{
"rowType": "game",
"gameId": "401872976",
"name": "Kansas City Chiefs at Las Vegas Raiders",
"status": "final",
"statusDetail": "Final",
"period": 4,
"homeAbbr": "LV",
"homeScore": 27,
"homeRecord": "3-1",
"awayAbbr": "KC",
"awayScore": 30,
"awayRecord": "4-0",
"winnerTeamId": "12",
"attendance": 62593,
"broadcasts": ["CBS"]
}

A standings line, with every stat ESPN lists kept in stats:

{
"rowType": "standing",
"league": "nfl",
"season": 2026,
"group": "American Football Conference",
"teamId": "12",
"team": "Kansas City Chiefs",
"abbr": "KC",
"rank": 1,
"wins": 4,
"losses": 0,
"ties": 0,
"winPercent": 1,
"gamesBehind": 0,
"pointsFor": 118,
"pointsAgainst": 77,
"pointDifferential": 41,
"streak": "W4",
"stats": { "W": "4", "L": "0", "PCT": "1.000", "SEED": "1", "Home": "2-0", "Road": "2-0", "vs. Div.": "2-0", "…": "…" }
}

A team:

{
"rowType": "team",
"league": "nba",
"teamId": "1",
"team": "Atlanta Hawks",
"abbr": "ATL",
"location": "Atlanta",
"nickname": "Hawks",
"slug": "atlanta-hawks",
"color": "c8102e",
"logoUrl": "https://a.espncdn.com/i/teamlogos/nba/500/atl.png",
"url": "https://www.espn.com/nba/team/_/name/atl/atlanta-hawks"
}

A headline:

{
"rowType": "article",
"league": "nfl",
"articleId": "50112291",
"headline": "McVay: Rams coaches will 'learn' from incident with Eagles fans",
"summary": "Rams coach Sean McVay says his assistant coaches will learn from their viral interaction with Eagles fans during Sunday's win over Philadelphia.",
"publishedAt": "2026-10-05T22:32:59.000Z",
"url": "https://www.espn.com/nfl/story/_/id/50112291/mcvay-rams-coaches-learn-incident-eagles-fans",
"imageUrl": "https://a.espncdn.com/photo/2026/1005/r1726550_600x400_3-2.jpg",
"categories": ["NFL", "Los Angeles Rams", "Philadelphia Eagles"]
}

And one free league row per league per run, saying what ESPN listed and what came back: leagueName, season, seasonType, week, games (on the slate, before any filter) and returned.


Quick start

You wantInput
Today's games in the four US majors{ "leagues": ["nfl", "nba", "mlb", "nhl"] }
Saturday's Premier League and LALIGA fixtures{ "leagues": ["eng.1", "esp.1"], "dates": ["2026-10-10"] }
A week of MLB results{ "leagues": ["mlb"], "dates": ["2026-09-21..2026-09-27"] }
Only what is still to play today{ "leagues": ["nfl"], "includeCompleted": false }
The Lakers' season{ "mode": "schedule", "leagues": ["nba"], "teams": ["LAL"] }
Two teams in two leagues{ "mode": "schedule", "leagues": ["nba", "nfl"], "teams": ["nba:LAL", "nfl:KC"] }
The NFL and NBA tables{ "mode": "standings", "leagues": ["nfl", "nba"] }
Last season's Premier League table{ "mode": "standings", "leagues": ["eng.1"], "season": 2025 }
Every WNBA team, with logos and colours{ "mode": "teams", "leagues": ["wnba"] }
The latest ten NFL headlines{ "mode": "news", "leagues": ["nfl"], "maxArticles": 10 }
This weekend's Grand Prix{ "leagues": ["f1"] }

League slugs

The ones people use most. The full table (51 leagues) is bundled in src/leagues.json.

SlugLeagueAlso accepts
nflNFLfootball/nfl
college-footballNCAA Football (FBS)ncaaf, cfb
nbaNBAbasketball/nba
wnbaWNBA
mens-college-basketballNCAA Men's Basketballncaab, ncaam
womens-college-basketballNCAA Women's Basketballncaaw
mlbMLB
college-baseballNCAA Baseball
nhlNHL
eng.1English Premier Leagueepl, premier-league
eng.2English League Championshipchampionship
esp.1Spanish LALIGAlaliga
ger.1German Bundesligabundesliga
ita.1Italian Serie Aserie-a
fra.1French Ligue 1ligue-1
usa.1MLSmls
mex.1Liga MXliga-mx
uefa.championsUEFA Champions Leaguechampions-league, ucl
uefa.europaUEFA Europa Leagueeuropa-league
fifa.worldFIFA World Cupworld-cup
f1Formula 1formula-1
nascar-premierNASCAR Cup Seriesnascar
pgaPGA Tour
atp / wtaATP and WTA tennis
ufcUFC

Every soccer slug in the table is one ESPN itself lists — it lists 219 soccer competitions in all. Any ESPN path works too: soccer/arg.1, soccer/bra.1, or anything else of the form <sport>/<league> for a sport in the table is passed through as written. A slug the Actor does not recognise comes back as a free league-not-found row naming the three nearest ("nfll" → nfl).


Modes

modeReadsRowsRequests
scoreboard (default)Each league's current slate, or the dates you givegameone per league per day
scheduleA team's season — needs teamsgameone team list per league, then one per team
standingsThe league table, by conference or groupstandingone per league
teamsEvery team in the leagueteamone per league
newsThe latest headlinesarticleone per league

Races and tournaments. An F1, NASCAR or IndyCar weekend is one row with eventType: "race"; a golf or tennis tournament or a UFC card is one row with eventType: "tournament". Their name, dates, status, venue (the circuit, for a race) and broadcasts are there; their competitors are individual athletes and are not — see No player data. For the same reason schedule, standings and teams are not available for those sports, and asking for them returns a free unsupported row.

Teams for schedule are ids or abbreviations (13, LAL; full names like Los Angeles Lakers work too), resolved through the league's team list, which is read once. With more than one league, scope an entry as nba:LAL — 13 is the Lakers in the NBA and the Raiders in the NFL, and an unscoped entry is tried in every league you list. A game between two teams you asked for is returned once. The schedule is the season type ESPN currently serves for that team — during the preseason, the preseason games.


Input

FieldTypeDefaultWhat it does
leaguesstring[]["nfl", "nba"]Slugs, aliases or ESPN paths (see League slugs)
modeenumscoreboardscoreboard, schedule, standings, teams, news
datesstring[][]scoreboard only. 2026-10-05 or 2026-10-01..2026-10-07; at most 31 days per range; empty = the current slate
teamsstring[][]schedule only. 13, LAL, nba:LAL
seasonint—schedule and standings: ?season= as ESPN numbers it (2026-27 NBA is 2027, 2026-27 Premier League is 2026)
includeOddsbooltruespread, overUnder, oddsProvider on game rows
includeCompletedbooltrueOff = upcoming and live games only; finished games are dropped before the charge
maxGamesPerLeagueint200Across all of a league's dates or teams
maxArticlesint20news: headlines per league, sent as ?limit=
maxConcurrencyint4Leagues read at once; never more than four requests in flight
maxRunSecsint240Time budget, max 3600
proxyConfigurationobject{ "useApifyProxy": true }Apify datacenter

Inputs that belong to another mode are ignored, not errors. An input with nothing usable in it — no league the Actor knows, a schedule run with no team, dates none of which is a date — finishes FAILED before a single request is sent, with a free row saying why.


Output reference

rowType is one of six. Four are charged, two are free.

rowTypeChargedWhat it is
gameyesA game, race or tournament: status, score, teams, venue, broadcasts, line
standingyesOne team's line in a league table
teamyesOne team
articleyesOne headline
leaguenoOne per league per run: what ESPN listed and what came back
diagnosticnoAnything that could not be read, and why

Every row carries ok, rowType, input, error, errorType, scrapedAt, source (site.api.espn.com), sourceUrl (the exact API URL) and null in every column that does not apply — so a CSV export is one rectangular table however the run was configured.

Game columns, normalised

  • status is one of six: scheduled, in_progress, final, postponed, canceled, delayed, mapped from ESPN's STATUS_* names (halftime and the break between periods are in_progress). statusDetail keeps ESPN's own line — "Final", "Final/OT", the period and clock.
  • Scores are numbers on scoreboards and schedules alike (ESPN writes them as "27" on one and { "value": 27 } on the other), and null before a game starts — ESPN prints 0, which would read as a nil-nil.
  • startTime is ISO 8601 UTC. startTimeLocal is ESPN's own text for a game not yet started ("Mon, October 5th at 8:15 PM EDT" — US Eastern in every capture).
  • homeRecord / awayRecord are the season record. In the MLB postseason ESPN also attaches the series record; that one is not used.
  • clock only while a game is live; period once it has started; attendance once it has been played.

The pre-game line

With includeOdds on, spread, overUnder and oddsProvider are the public pre-game line ESPN lists, exactly as it lists it — informational, not advice. spread is ESPN's own line text: a point spread for football and basketball ("NO -1.5"), and for hockey and soccer the moneyline ESPN shows in the same place ("TB -218", "ARS -260"). Finished games usually carry no line at all.

Standings columns

rank is ESPN's rank where it gives one, else the playoff seed, else the line's place in the table — the NFL table is sorted by win percentage, not by seed. wins, losses, ties, winPercent, gamesBehind, pointsFor, pointsAgainst, pointDifferential and streak are pulled out as columns; stats keeps every stat ESPN lists, abbreviation to display value, with the splits that have no abbreviation (Home, Road, vs. Div.) keyed by name. group is the conference, division or table.

Diagnostic rows

errorType is one of league-not-found, team-not-found, no-games, no-results, not-found, unsupported, invalid-input, truncated, blocked, timeout, unavailable. None of them is ever charged for. A run whose input was usable but produced only these — a league out of season, a team ESPN does not list, a request ESPN refused — finishes SUCCEEDED with zero results, a status message that says so, and a free row per problem telling you why; nothing is charged, start fee included.


No player data

This Actor returns teams, games, scores, venues, records, odds and standings only — no player data. ESPN's payloads are full of people: game leaders with names, headshots and jersey numbers, winning and losing pitchers, goal scorers, the drivers in a race, rosters, and a byline on every article. None of it is emitted. Every column is built field by field from a named path and nothing is passed through, so there is no athlete, leaders or byline field anywhere in the dataset; the offline suite collects every athlete name in every captured payload and checks that none of them reaches a row.

The one place a person can still be named is ESPN's own editorial text: a headline such as "Cowboys' Tyler Smith hoping to be ready to face Bucs" is the headline, and it is returned as written. An article's categories list the leagues and teams it is filed under, never the players or the writer.


Being a good citizen

The feed is free and public, which is a reason to be careful with it:

  • Each scoreboard is polled once per run. ESPN serves it with cache-control: max-age=6; asking twice in one run would be asking for the same answer. Two spellings of one league, overlapping date ranges and a team list needed by several team entries are all one request.
  • Never more than four requests in flight, started at least 200 ms apart, across the whole run, whatever maxConcurrency says; and a 250–600 ms pause between successive requests for one league (the days of a range, the teams of a schedule).
  • Requests carry an ordinary browser User-Agent: ESPN's CDN answers HTTP 403 to any other string, so a self-naming one is not an option here.
  • A 403 or 429 is a block: the proxy session is rotated once, and a second refusal becomes a free blocked row rather than a retry loop.

What you are never charged for

  • The league row, one per league per run.
  • Every diagnostic row.
  • A league with no games on the dates asked for — out of season, an off day.
  • Finished games dropped by includeCompleted: false, games past maxGamesPerLeague, and a game already returned for another date or team.
  • Leagues and teams the Actor or ESPN does not know.
  • Requests that were blocked, failed or timed out.
  • A run that returns nothing at all: it finishes SUCCEEDED with zero results and a "0 results" status message, and bills nothing, start fee included.

Pricing

EventWhat it isFREEBRONZESILVERGOLD
actor-startOnce per run, only after the run has returned a paid row$0.001$0.001$0.001$0.001
gameOne game, race or tournament$0.0008$0.0008$0.00064$0.00048
standingOne standings line$0.0004$0.0004$0.00032$0.00024
teamOne team$0.0004$0.0004$0.00032$0.00024
articleOne headline$0.0003$0.0003$0.00024$0.00018

$0.80 per 1,000 games.

RunCost
Today's NFL + NBA slates, about 30 games≈ $0.025 ($0.0266 for 32)
One team's full NFL regular season, 17 games$0.0146
NFL + NBA standings, 62 lines$0.0258
All 30 NBA teams$0.013
20 NFL headlines$0.007
A league that is out of season$0.00

Charging is charge-after-push: rows are in your dataset before the event is recorded. ACTOR_MAX_TOTAL_CHARGE_USD is respected — the run stops fetching when it is reached and finishes SUCCEEDED saying so.


Proxy

The default is { "useApifyProxy": true } — Apify's datacenter pool, which is what the capture this Actor was built on came through. You can switch to residential if your runs start getting blocked; you will not get different data.


Use it from an AI agent, or from code

One JSON object in, one flat array out — the shape agent runtimes want. The Actor runs with limited permissions, uses pay-per-event pricing and never enters Standby, so it works over the Apify MCP server and with x402 agentic payments. The Integrations tab pushes results to Slack, a webhook, Zapier, Make, Google Sheets, Snowflake or BigQuery.

curl -X POST "https://api.apify.com/v2/acts/insight.solutions~sports-scores-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"leagues":["nfl","eng.1"],"mode":"scoreboard","includeCompleted":false}'
# pip install apify-client
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("insight.solutions/sports-scores-api").call(run_input={
"leagues": ["nba", "nhl"],
"dates": ["2026-10-20..2026-10-26"],
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row.get("ok") and row["rowType"] == "game":
print(row["startTime"], row["shortName"], row["status"],
row["awayScore"], row["homeScore"], row["venue"], sep=" | ")

Schedule it hourly on game days for a cheap scores feed: each run reads every scoreboard once, and an idle league costs nothing.


FAQ

Do I need an API key? No. The feed is public and keyless, and this Actor sends no cookies and no credentials of anybody's.

Is this real-time? Each run reads ESPN's scoreboard as it stands at that moment, which ESPN itself caches for six seconds. A run is a snapshot; for a live ticker, schedule runs.

Why is homeScore null for a game that has not started? Because it has no score. ESPN prints 0 there, and a column that read 0-0 before kickoff would be wrong in every aggregate you built on it.

Why is spread a moneyline for hockey and soccer? Because that is what ESPN shows in that place for those sports, and the column is "the line ESPN lists", copied as-is rather than reinterpreted.

Why can't I get F1 standings or a tennis draw? Because they are tables of drivers and players, and this Actor returns no player data. Race weekends and tournaments are on the scoreboard as rows of their own.

What happens out of season? The league's scoreboard is empty, and you get a free no-games row for it and nothing billed. The run still SUCCEEDS. Pass dates to read a past or future day.

What happens if the format changes? The feed is unofficial and can change. If a response stops parsing, the run returns a free diagnostic row naming the request rather than a wrong number. A run that returned no real row finishes SUCCEEDED with zero results and a "0 results" status message pointing at those rows, and bills nothing.

Is this affiliated with ESPN or the leagues? No. It reads a public feed and links back to every game and story on espn.com. It is not affiliated with, endorsed by or connected to ESPN, any league or any team.


Limitations

  • The upstream format may change. ESPN's site API is undocumented and unofficial; it has been stable for years, and that is all anyone can say.
  • No player data, by design — no leaders, box scores, rosters, player stats, drivers or bylines. Headlines are ESPN's text and may name people.
  • One snapshot per run. Each scoreboard is read once; nothing is polled.
  • A schedule is the season type ESPN currently serves. During the preseason a team's schedule is its preseason games.
  • College football is the FBS slate. The scoreboard is asked for FBS (groups=80, up to 100 games a day); FCS and lower divisions are not included.
  • Soccer outside the table needs its ESPN path (soccer/arg.1). ESPN lists 219 competitions; 33 of them have a slug here.
  • Standings and team lists for the less-covered leagues (college sports, minor soccer competitions) have not been checked against a capture; if ESPN has none, the row says so for free.

Our other Actors

Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.

Video, audio & social

News, documents & the web

Business, finance & jobs

Apps & games