Request a tool
All toolsAutomationsGuidesMCP serverRequest a toolPlatformsCategories
Sofascore Scraper icon

Sofascore Scraper

Scrape Sofascore fixtures, live scores, results and league tables across 21 sports. Filter by date, country or competition. No API key, pay per row.

20 runs on Apify $0.00065 per match ($0.65 / 1,000)
Run this in the cloudRun on Apify →

Developer & Research Tools

How it works

  1. 1
    Open it on Apify

    Hit Run on Apify — it opens the tool in the cloud, no install.

  2. 2
    Set the inputs

    Adjust sports, upcomingDays, lastDays (sensible defaults are pre-filled).

  3. 3
    Click Run

    The tool runs on Apify’s cloud and collects the data for you.

  4. 4
    Export the results

    Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.

Pricing

$0.00065 per match = $0.65 per 1,000

You are charged forWhenPrice
Match returnedOne match or league-table row returned in the dataset. Diagnostic rows and the labelled sample row are not charged.$0.00065

Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-09-20, and they are what you are actually charged.

Inputs

FieldWhat it doesType
sportsLeave empty for football. Sofascore covers all of these, though how deep the coverage goes varies a lot by sport and by country.array
upcomingDaysToday's fixtures and the days after: 1 is today only, 2 adds tomorrow. Matches that have already kicked off today come back with their live score.integer
lastDaysThe last N full days of results, ending yesterday. 1 is yesterday only, which is what a daily schedule usually wants.integer
dateFromA fixed range instead of the two counters above, as YYYY-MM-DD. Used only when it is filled in.string
dateToThe last day of the range. Leave it empty to stop at today. One run covers 60 days.string
liveAdd everything that is being played at this second, in the sports above. One request per sport, so it is cheap to leave on alongside a date range.boolean
countriesNarrows a date range to certain countries, written as Sofascore writes them: England, Spain, Italy, Brazil. "Europe", "World" and "South America" hold the international competitions. A name also matches the longer regions containing it, so "England" brings in England Amateur as well. Leave it empty and a date range reads the 40 busiest regions for that sport.array
tournamentsPaste competition links (https://www.sofascore.com/football/tournament/england/premier-league/17) or just type a name. Naming competitions replaces the country-by-country sweep: with dates you get exactly those days, without dates you get the whole current season.array
matchesIndividual matches, as links copied from Sofascore (https://www.sofascore.com/football/match/brentford-chelsea/Nsab#id:16363649) or as bare ids. These always come back with the venue and the referee.array
statusesLeave empty to keep every match. Useful for pulling only what has finished on a day that is still running.array
includeStandingsAdds the table for every competition the run touched: position, played, won, drawn, lost, goals, difference, points. One row per team, charged the same as a match. Cups and knockout rounds have no table and are skipped.boolean
standingTypesOnly read when League tables is on. Overall is the normal one; home and away split the same season.array
includeVenueAdds the stadium, its city and the referee. Sofascore only publishes these on a match's own page, so this costs one extra request per match and makes a big run several times slower. Off by default for that reason. A match that will not open is still delivered, without them.boolean
maxItemsThe most rows one run returns, matches and table rows together. Each row is one charge. A single busy football day across the default regions runs to several thousand, so this is worth setting deliberately.integer

What you get

A structured dataset — each result includes fields like:

rowTypesportcountrycompetitionseasonroundstartTimestatushomeTeamawayTeamscorewinnervenueurlstandingTypegroupNamepositionteamplayedwinsdrawslossesgoalsForgoalsAgainstgoalDifferencepointsnote

Export every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.

Related tools in Developer & Research Tools

Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.

GitHub Scraper iconDeveloper & Research Tools

GitHub Scraper

Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.

18 use cases

Stack Overflow / Stack Exchange Scraper iconDeveloper & Research Tools

Stack Overflow / Stack Exchange Scraper

Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.

2 use cases

Package Registry Scraper (npm + PyPI) iconDeveloper & Research Tools

Package Registry Scraper (npm + PyPI)

Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.

2 use cases

arXiv Scraper iconDeveloper & Research Tools

arXiv Scraper

Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.

2 use cases

OpenAlex Scholarly Works Scraper iconDeveloper & Research Tools

OpenAlex Scholarly Works Scraper

Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.

2 use cases

Crossref Scholarly Works Scraper iconDeveloper & Research Tools

Crossref Scholarly Works Scraper

Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.

2 use cases

See all Developer & Research Tools →

Sofascore Scraper: fixtures, results, live scores and league tables for 21 sports

Give it some days, a competition, a few match links or the live board, and it returns one row per match: the competition and round, both teams, kick-off in UTC, the state of play and the score. Switch on league tables and every competition the run touched adds one row per team as well. Know this before you buy: a date range reads only the 40 busiest countries for each sport unless you name others, and below football and basketball the coverage often thins to a fixture and a final score.

InputDays, Sofascore competition or match links, or the live board
OutputOne row per match, plus one per team when you ask for league tables
Ceiling50,000 rows and 60 days per run
Account neededNone
Price$0.65 per 1,000 rows, flat on every plan

🔍 What Sofascore Scraper does

There are four ways in, and one run can mix them.

  • Days. upcomingDays counts forward from today, lastDays back from yesterday, or you give a

fixed dateFrom and dateTo. Each day is read region by region: the 40 busiest per sport, or the ones you list in countries.

  • Competitions. A link or a name. This replaces the region sweep: with dates you get those days

of it, without them the current season, played matches first.

  • Matches. One link or bare id per match. These always come with the venue and referee.
  • The live board. Everything in play at that moment.

A match reached twice comes back once.

winner comes from the score. Only a level score falls back on Sofascore's own flag, which is what settles a shootout. A drawn league game reads "draw"; an unfinished match reads null.

League tables follow the competitions the run touched, one row per team. Cups have none. With tables on, half your Maximum rows (up to 400) is kept for them, so matches cannot use up the whole allowance.

Venue and referee take one extra page read per match, so they make a big run several times slower. A match whose page will not open still arrives, without them.

📥 What you give it

Yesterday's results in England and Spain, with the tables behind them:

{
  "sports": ["football"],
  "lastDays": 1,
  "countries": ["England", "Spain"],
  "includeStandings": true,
  "maxItems": 500
}
FieldPrefilled withWhat it is
sports["football"]Any of 21: football, basketball, tennis, ice-hockey, volleyball, handball, baseball, american-football, rugby, cricket, snooker, darts, table-tennis, badminton, futsal, minifootball, beach-volley, aussie-rules, floorball, waterpolo, esports. Left empty, it is football. A name not on the list is skipped.
upcomingDays1Today and the days after, 0 to 30. 1 is today only. Matches already under way come back with their live score.
lastDaysemptyThe last full days, ending yesterday, 0 to 365. 1 is yesterday. It adds to upcomingDays, so 1 and 1 is yesterday plus today.
dateFromemptyThe first day of a fixed range, as YYYY-MM-DD. Once it is filled in, the two counters above are ignored.
dateToemptyThe last day of that range. Empty means today.
livefalseAdds everything being played at that moment, in the sports above. It is one quick read per sport, so it sits fine beside a date range.
countriesemptyNarrows a date range to regions as Sofascore names them: England, Spain, Brazil. "Europe", "World" and "South America" hold the international competitions. A name also matches longer regions containing it, so "England" brings in England Amateur. Empty reads the 40 busiest regions per sport.
tournamentsemptyCompetition links, such as https://www.sofascore.com/football/tournament/england/premier-league/17, or names. The first 50 are read.
matchesemptyMatch links or bare ids. The first 50 are read.
statusesemptyKeep only matches in these states: notstarted, inprogress, finished, postponed, canceled, suspended. Empty keeps every match.
includeStandingsfalseAdds league tables, one row per team, charged the same as a match.
standingTypes["total"]total, home or away. Only read when league tables are on.
includeVenuefalseAdds venue, venueCity, venueCountry, venueCapacity and referee. Slows a big run down several times.
maxItems200The most rows one run returns, matches and table rows together, 1 to 50,000. Left out of an API call, it is 200.
proxyConfigurationuntouchedOptional. Add your own servers here if you want the run to go out through them. Otherwise leave it alone.

📤 What you get back

A real match row from a real run, with Venue and referee switched on:

{
  "rowType": "match",
  "matchId": 16363249,
  "url": "https://www.sofascore.com/football/match/chelsea-brighton-and-hove-albion/FsN#id:16363249",
  "sport": "football",
  "country": "England",
  "countryCode": "EN",
  "competition": "Premier League",
  "competitionId": 17,
  "competitionUrl": "https://www.sofascore.com/football/tournament/england/premier-league/17",
  "season": "Premier League 26/27",
  "seasonId": 96668,
  "round": 2,
  "roundName": null,
  "startTime": "2026-08-30T13:00:00.000Z",
  "startTimestamp": 1788094800,
  "status": "finished",
  "statusDetail": "Ended",
  "statusCode": 100,
  "homeTeam": "Chelsea",
  "homeTeamId": 38,
  "homeTeamCountry": "England",
  "awayTeam": "Brighton & Hove Albion",
  "awayTeamId": 30,
  "awayTeamCountry": "England",
  "homeScore": 4,
  "awayScore": 3,
  "score": "4 - 3",
  "homeScoreHalftime": 3,
  "awayScoreHalftime": 1,
  "homeScoreNormaltime": 4,
  "awayScoreNormaltime": 3,
  "homeScoreExtraTime": null,
  "awayScoreExtraTime": null,
  "homeScorePenalties": null,
  "awayScorePenalties": null,
  "homeScoreAggregate": null,
  "awayScoreAggregate": null,
  "winner": "Chelsea",
  "homeRedCards": null,
  "awayRedCards": null,
  "venue": "Stamford Bridge",
  "venueCity": "London",
  "venueCountry": "England",
  "venueCapacity": 40341,
  "referee": "Michael Oliver",
  "scrapedAt": "2026-09-20T01:11:04.535Z"
}

And a league-table row from the same run:

{
  "rowType": "standing",
  "sport": "football",
  "country": "England",
  "countryCode": "EN",
  "competition": "Premier League",
  "competitionId": 17,
  "competitionUrl": "https://www.sofascore.com/football/tournament/england/premier-league/17",
  "season": "Premier League 26/27",
  "seasonId": 96668,
  "standingType": "total",
  "groupName": "Premier League 26/27",
  "position": 1,
  "team": "Manchester City",
  "teamId": 17,
  "played": 4,
  "wins": 4,
  "draws": 0,
  "losses": 0,
  "goalsFor": 8,
  "goalsAgainst": 2,
  "goalDifference": 6,
  "points": 12,
  "note": "Champions League",
  "updatedAt": "2026-09-19T18:23:53.000Z",
  "scrapedAt": "2026-09-20T01:11:29.425Z"
}
FieldWhat it is
rowTypematch or standing. A diagnostic row says diagnostic.
matchId, urlSofascore's own id for the match, and its page.
country, countryCodeThe region Sofascore files the competition under, such as England, Europe or World. The teams' own countries are in homeTeamCountry and awayTeamCountry.
competition, season, round, roundNameWhich competition and season, the round number, and the round's name where Sofascore gives one. Each has an id beside it.
startTime, startTimestampKick-off in UTC as an ISO string, and the same moment as a Unix timestamp.
status, statusDetail, statusCodeSofascore's own word for the state of the match, the longer label it shows ("Ended", "Halftime"), and its numeric code.
homeScore, awayScore, scoreThe current or final score. Null while nothing has been played.
homeScoreHalftime and the restHalf-time, normal time, extra time, penalties and aggregate. Filled only when they apply, so a league game leaves extra time, penalties and aggregate null. That is a game that ended after ninety minutes, not a gap.
winnerThe winning team, "draw", or null until the match has finished.
homeRedCards, awayRedCardsFilled when Sofascore lists red cards, null otherwise.
venue, venueCity, venueCountry, venueCapacity, refereeNull unless Venue and referee was on or the match was pasted in.
position to pointsOn a table row: position, played, won, drawn, lost, goals for and against, goal difference and points.
groupName, standingType, noteOn a table row: the group the table belongs to, whether it is the overall, home or away table, and the label Sofascore puts beside the team ("Champions League", "Relegation").

🧾 Reading the output

The free sample row says match too, so rowType alone does not separate data from notices.

RowHow to spot itCharged
A matchrowType is match and there is no _sample fieldyes
A table rowrowType is standingyes
The sample row_sample: true and charged: false. Written only when the input asks for nothing at allno
The diagnostic rowrowType is diagnostic, _diagnostic: true, charged: false. Written only when a run delivered no rowsno

The diagnostic row explains itself in note and lists up to ten problems, each with a code:

CodeWhat it means
EMPTY_DAYSofascore lists nothing for that sport and day in the regions read.
NO_SUCH_REGIONNone of your countries match a Sofascore region for that sport.
REGIONS_UNREADABLE, REGION_DAY_UNREADABLEThe region list for a sport, or one region on one day, could not be read.
BAD_COMPETITION, COMPETITION_NOT_FOUND, SEARCH_FAILEDNot a competition link, no competition by that name, or the name lookup failed.
SEASON_UNREADABLE, COMPETITION_PAGE_UNREADABLE, COMPETITION_DAY_UNREADABLEPart of a named competition could not be read.
BAD_MATCH, MATCH_NOT_FOUND, MATCH_UNREADABLENot a match link or id, not on Sofascore, or the match could not be read.
LIVE_UNREADABLEThe live board for one sport could not be read.
NO_SEASON, TABLE_UNREADABLEA league table was skipped.
DELIVERY_FAILEDRows could not be written, so the run stopped.
BUDGET_TOO_LOWThe maximum charge you set does not cover one row, so nothing was read. This one is in the row's problem field.

Every run also writes RUN_REPORT to its key-value store: what was asked for, what came back, what was left out and why, and why the run stopped. Problems on a run that did deliver rows go there, not into the dataset, and so does NO_TABLES when nothing touched had a table.

To keep only real data, drop rows carrying _sample or _diagnostic, then split on rowType. The Matches and League tables views choose columns. They do not filter rows.

▶️ How to run it

1. Open Sofascore Scraper and click Try for free. 2. Pick your Sports. Then set Upcoming days or Last days, or a From date and To date. Or paste links into Competitions or Match links, or switch on Live now. 3. Tick League tables or Venue and referee if you want them. 4. Set Maximum rows on purpose. One busy football day across the default regions runs to several thousand. 5. Click Start, then download the dataset as JSON, CSV or Excel, or read it from the API.

💰 How much does it cost?

$0.65 per 1,000 rows, flat on every Apify plan. A match row and a league-table row cost the same. Two hundred rows, the prefilled cap, come to 13 cents.

Not charged: the sample row, the diagnostic row, matches your statuses filter drops, a match reached twice, and anything the run could not read. If you set a maximum charge for the run, it stops at the last row that fits.

💡 What people use it for

  • Posting yesterday's results somewhere every morning. "lastDays": 1 on a daily schedule gives

exactly that, with no date arithmetic to maintain.

  • Keeping one league's fixtures, results and table in a spreadsheet for a tipping competition.
  • Finding every postponed or suspended match across a weekend with Only these states.
  • Backfilling a season of results, 60 days a run, before you start collecting day by day.

🚧 What it does not do

  • No statistics. No lineups, player ratings, shot maps, possession, expected goals or odds.
  • Not every country on a date range. The 40 busiest regions per sport, unless you name others in

Countries and regions.

  • No more than 60 days in one run. A longer backfill is several runs.
  • No stream. Live scores are one reading of the board. Schedule the run for a running score.
  • Not much depth in the smaller sports. Several carry the fixture and the final score and

nothing else. Try a small Maximum rows first.

  • No tables for cups. Group stages do have them.
  • No gap filling. If a page cannot be read, the run carries on without it. Those matches are

missing and not charged, and RUN_REPORT lists what was missed.

  • No local times. Everything is UTC.

🧭 Which sports scraper do you need?

If you wantUse
Fixtures, results, live scores and league tables across 21 sportsThis one
Pre-game odds and player propsSports Odds Scraper
Tennis results with set scores, tiebreaks and average oddsTennis Results & Odds Scraper

❓ Questions people ask

Do I need an API key or a Sofascore account? No. There is nothing to sign up for.

How do I get one league and nothing else? Paste its Sofascore link into Competitions. A typed name works too, but a link is safer when a name is shared: half a dozen countries have something called a Super Cup.

Why is venue empty? Venue and referee was off, or that match's own page would not open.

Why did I get fewer rows than I asked for? Sofascore listed fewer, the run reached Maximum rows (or the match share of it, with tables on), or a page could not be read. RUN_REPORT says which.

Is it legal to use? Fixtures and scores are public, and nothing here needs a login. Check the site's terms against your plans for the data. Apify's write-up on the legality of web scraping is a fair starting point, and we are not lawyers.

🆘 If something breaks

Open the Issues tab on the actor page and send the input and the run ID. The run's RUN_REPORT usually names the reason already.