SofaScore Sports Scraper: Live Scores, Timeline, Lineups avatar

SofaScore Sports Scraper: Live Scores, Timeline, Lineups

Pricing

from $0.35 / 1,000 matches

Go to Apify Store
SofaScore Sports Scraper: Live Scores, Timeline, Lineups

SofaScore Sports Scraper: Live Scores, Timeline, Lineups

Scrape SofaScore football, tennis and basketball matches, one row per match: teams, score, period scores, status, minute, start time and winner. Live, by round, by match link or by day. Football adds referee, venue, goals, cards, substitutions and lineups. JSON, CSV or Excel. By MrBridge

Pricing

from $0.35 / 1,000 matches

Rating

0.0

(0)

Developer

MrBridge

MrBridge

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Categories

Features

Limited permissions

Share

SofaScore Sports Scraper

SofaScore Sports Scraper collects football, tennis and basketball matches from SofaScore (https://www.sofascore.com), one clean row per match, ready for JSON, CSV or Excel. It reads the public pages and responses of the site, so no account and no API key are needed. Each row gives the teams or players, the tournament, the season, the round, the start time, the status, the score, the half-time score, the winner and a link to the match page. You can add the match sheet, the timeline of goals, cards and substitutions, and the line-ups.

Pick football, keep the live mode and set maxItems to 20: the Actor returns up to 20 matches in play right now, each with its tournament, elapsed minute and current score. Switch to the date mode with a league such as premier-league to get the fixtures and results of a weekend, and turn on the details to get the referee, the venue, each goal with its scorer and the starting line-ups.

What is SofaScore Sports Scraper?

SofaScore is a live-score website that lists matches of thousands of tournaments, with scores updated during play. The Actor reads those pages for you. It picks the matches according to the mode you choose, applies your league filter, removes duplicates and returns one structured row per match. The first 23 columns follow a fixed sports-matches layout, documented below, so the rows are easy to compare with other sources that use the same layout, such as Flashscore Matches Scraper.

What data can I extract with it?

One row per match: source, match ID, match link, sport, country, tournament and its ID, season, round, start time, normalized status and status as SofaScore writes it, elapsed minute of a live football match, home and away side with their IDs, score, half-time score, winner and scrape time. Then the SofaScore columns: page code and slug, tournament slug, category, season ID, score of each period, tennis surface, red cards, and the optional details.

How can I use the results?

  • Follow the matches in play right now across a sport, with their score and minute.
  • Build a results archive of the leagues you follow, one day or one round at a time.
  • Feed a dashboard or a newsletter with the fixtures of the coming days and yesterday's scores.
  • Study how often home sides win in a tournament, using the winner and half-time columns.
  • Collect the goals, cards, substitutions and line-ups of finished football matches.

How to use it

  1. Open the Actor in Apify Console.
  2. Pick a mode, the sports and, if you want to narrow the list, the leagues, the dates, the rounds or the match links.
  3. Set maxItems, then start the run. Rows appear in the dataset as they are delivered.

Input

FieldMeaningDefault
modelive, tournament, matches or date (see below)live
sportsfootball, tennis, basketball. Used by the live and date modes["football"]
tournamentsRounds to read in the tournament mode, up to 500empty
matchUrlsMatch links or IDs to read in the matches mode, up to 500empty
datesDays as YYYY-MM-DD in the date modeempty, which means today (UTC)
leaguesKeep only these leagues in the date mode, up to 100. Empty keeps allempty
maxItemsHard cap on match rows returned and billed, 1 to 10000100
includeDetailsAdd the details column to each matchfalse

The Console form pre-fills mode with live, sports with football and maxItems with 20. Example input:

{
"mode": "live",
"sports": [
"football"
],
"maxItems": 20
}

The four modes:

  • live: the matches in play now, for each sport of sports.
  • tournament: the matches of the rounds listed in tournaments. An entry is { "uniqueTournamentId": 17, "seasonId": 96668, "round": 7 } or { "url": "https://www.sofascore.com/football/tournament/england/premier-league/17#id:96668", "round": 7 }. The round is required.
  • matches: the matches named in matchUrls, as SofaScore match links that end with #id:<number> or as match IDs.
  • date: the matches of the days listed in dates, for each sport of sports, optionally limited by leagues.

A field that does not fit the mode is refused, except sports, which the tournament and matches modes ignore because the sport comes from the match.

dates accepts real calendar days from 7 days before to 7 days after the UTC day on which the run starts. A match is kept when its start falls on one of these UTC days.

leagues is a local filter. Case is ignored and a match is kept when it fits any entry. An entry takes one of these forms:

  • the SofaScore ID of the unique tournament, for example 17
  • the slug of the tournament, for example premier-league
  • the name or the slug of the category, which is usually the country, for example England or england
  • a key prefixed with a sport, for example football:17 or basketball:england, which applies to that sport only

The sport of a prefix must be football, tennis or basketball and must be in sports, otherwise the input is refused. A number alone only matches a tournament ID. A category name that contains a colon is given by its slug.

Invalid input ends the run successfully with one BAD_INPUT row in the errors dataset that lists every problem, and no match row.

Output

Matches go to the default dataset, one row per match. Problems go to a separate errors dataset and are never billed. The OUTPUT record of the key-value store summarizes the run. The dataset has two views: overview and details.

The first 23 columns are the common layout, in this order:

FieldTypeMeaning
sourcestringAlways SofaScore
matchIdstringSofaScore match ID, the key of the row
urlstring or nullSofaScore page of the match
sportstringfootball, tennis or basketball
countrystring or nullCountry of the tournament category
tournamentstring or nullTournament name
tournamentIdstring or nullSofaScore ID of the unique tournament
seasonstring or nullSeason name
roundstring or nullRound name, or Round N when only the number is known
startTimestring or nullISO 8601 in UTC
statusstringscheduled, live, finished, postponed, cancelled, abandoned, interrupted or unknown
statusRawstring or nullThe status as SofaScore writes it
minuteinteger or nullElapsed minute of a live football match in the first or second half. Null otherwise
homeTeam, awayTeamstring or nullTeam, or player in tennis
homeTeamId, awayTeamIdstring or nullSofaScore IDs
homeScore, awayScoreinteger or nullFinal or current score. Sets in tennis
homeScoreHalfTime, awayScoreHalfTimeinteger or nullHalf-time score in football, first two quarters added in basketball, null in tennis
winnerstring or nullhome, away or draw, from the winner code of the source
scrapedAtstringTime the row was built, ISO 8601 in UTC

Then the SofaScore columns:

FieldTypeMeaning
customIdstring or nullShort code of the match page link
slugstring or nullMatch slug used in the page link
tournamentSlugstring or nullSlug of the unique tournament
categorystring or nullCategory of the tournament, usually the country
seasonIdstring or nullSofaScore season ID
periodScoresarray or nullScore of each period (half, set or quarter), up to five, objects { period, home, away }. Null when absent
groundTypestring or nullTennis surface
homeRedCards, awayRedCardsinteger or nullRed cards of each side, football
detailsobject or nullMatch sheet, timeline and line-ups, see below

Every field is always present, with null when SofaScore gives no value.

This is one real row from the example input above, built by the Actor from a stored SofaScore response:

{
"source": "SofaScore",
"matchId": "16604724",
"url": "https://www.sofascore.com/football/match/cd-inca-isidro-metapan/HMjsyyVd#id:16604724",
"sport": "football",
"country": "El Salvador",
"tournament": "Primera División, Apertura",
"tournamentId": "11537",
"season": "Primera Division 26/27",
"round": "Round 19",
"startTime": "2026-11-07T00:00:00.000Z",
"status": "live",
"statusRaw": "1st half",
"minute": 11,
"homeTeam": "C.D. Inca Aruba",
"homeTeamId": "488673",
"awayTeam": "Isidro Metapan",
"awayTeamId": "24382",
"homeScore": 0,
"awayScore": 0,
"homeScoreHalfTime": 0,
"awayScoreHalfTime": 0,
"winner": null,
"scrapedAt": "2026-11-07T00:15:00.000Z",
"customId": "HMjsyyVd",
"slug": "cd-inca-isidro-metapan",
"tournamentSlug": "primera-division-apertura",
"category": "El Salvador",
"seasonId": "99021",
"periodScores": [
{
"period": 1,
"home": 0,
"away": 0
}
],
"groundType": null,
"homeRedCards": null,
"awayRedCards": null,
"details": null
}

The example comes from a test fixture captured on 2026-11-07, so the scores and statuses are those of that day.

Details

Set includeDetails to true to fill details. It depends on the sport:

SportMatch sheettimelinelineups
Footballyesyesyes
Basketballyesnullyes
Tennisnone, details is null

details holds these keys:

FieldMeaning
venue{ name, city, country, capacity }
referee{ name, shortName, id, country }
homeManager, awayManager{ name, shortName, id }
injuryTime1, injuryTime2Added minutes announced for each half, football
timelineIncidents of a football match, oldest first, see below
lineups{ confirmed, home, away } where each side is { formation, players }
{
"venue": {
"name": "Craven Cottage",
"city": "London",
"country": "England",
"capacity": 29600
},
"referee": {
"name": "Chris Kavanagh",
"shortName": null,
"id": "107059",
"country": "England"
},
"homeManager": {
"name": "Álvaro Arbeloa",
"shortName": "Á. Arbeloa",
"id": "798940"
},
"awayManager": {
"name": "Sergej Jakirović",
"shortName": "S. Jakirović",
"id": "790280"
},
"injuryTime1": 1,
"injuryTime2": 3
}

Each element of timeline is one incident:

FieldMeaning
idIncident ID
period1st Half, 2nd Half or Extra Time, null for a period mark
sidehome or away
minute, addedTime, minuteTextMinute, added time and the minute as shown, for example 90+4'. A period mark carries its label, such as HT or FT
typeGoal, Yellow Card, Yellow Red Card, Red Card, Substitution - Out or Period
typeCode, playerUrlAlways null, kept for the same layout as the Flashscore timeline
player, playerIdThe player concerned. For a substitution, the player going off
reasonReason of a card, or Injury for an injury substitution
homeScore, awayScoreScore after a goal or at a period mark
relatedLinked player: the assist of a goal (Assistance) or the player coming on (Substitution - In)
classClass given by SofaScore, for example regular, penalty or ownGoal
rescinded, injuryFlags given by SofaScore, null when absent
{
"id": "365726690",
"period": "1st Half",
"side": "home",
"minute": 33,
"addedTime": null,
"minuteText": "33'",
"type": "Goal",
"typeCode": null,
"player": "Alex Iwobi",
"playerId": "352770",
"playerUrl": null,
"reason": null,
"homeScore": 1,
"awayScore": 0,
"related": [
{
"type": "Assistance",
"player": "Sander Berge",
"playerId": "793167",
"playerUrl": null
}
],
"class": "regular",
"rescinded": null,
"injury": null
}

Each player of a line-up has a name, a short name, an ID, a shirt number, a position, a substitute flag and a captain flag:

{
"name": "Bernd Leno",
"shortName": "B. Leno",
"id": "103335",
"shirtNumber": 1,
"position": "G",
"substitute": false,
"captain": false
}
  • timeline is [] for a football match that is scheduled, postponed or cancelled, and no request is made for it.
  • Inside a non-null details, timeline is null for basketball rows, and for a football match whose incidents request failed or could not be read.
  • A person comes out with a name, a short name and an ID only. Birth dates, heights, market values and user counts are never output.
  • A line-up that SofaScore has not published gives an error row NOT_FOUND in the errors dataset, and the match row still comes out.
  • details is null when the option is off, for tennis rows, and when nothing could be read for the match.

Billing

One match event is billed per delivered row. One match-details event is billed on top for a row whose details hold a timeline with at least one goal, card or substitution, or a line-up with at least one player. The venue, the referee, the managers and the added time alone are not billed, and neither are the period marks (HT, FT) of a timeline. Errors are never billed: a missing line-up gives a NOT_FOUND row in the errors dataset and no charge. Duplicates, filtered-out matches and details with nothing billable are not billed either. The Pricing tab of the Actor shows the current prices.

When the platform moves a run to another server or restarts it while it works, the run picks up where it was. A match already returned is neither returned nor billed again.

Limits

  • Details cover football (sheet, timeline and line-ups), basketball (sheet and line-ups, timeline null) and no tennis detail.
  • The Actor does not output match statistics or player statistics.
  • minute is computed for football only, and only while the match is in its first or second half.
  • dates covers 7 days before to 7 days after the UTC day of the launch.
  • In the date mode, one football day without a filter costs about 150 requests, so a run covers about 6 such days. A leagues filter cuts this sharply, one league of one country can take 2 requests. The categories that would exceed the request cap of the run are left out and counted in categoriesDropped of the OUTPUT record.
  • In the date mode, the basketball list of a day may be refused, and so may a single category. The run then writes a TARGET_BLOCKED row and goes on, so a date run can return no basketball match at all. For basketball, the live mode or the matches mode stay available. In the matches mode, basketball details come out with the venue and the line-ups, while the referee and the formation are usually null. The timeline is always null for basketball.
  • The Actor does not output betting odds. For odds, use Flashscore Matches Scraper.
  • A match close to midnight can appear in two day lists. It is returned once.
  • A run with includeDetails makes up to three more requests per match, so it lasts longer. For large runs, raise the run timeout.
  • Each run is a snapshot of SofaScore at that moment. Scores move, so two runs a minute apart can differ.

Run summary and early stops

The OUTPUT record counts requests, pages, matches, errors and the details: detailRequests (the detail requests really made), rowsWithDetails and detailsCharged. categoriesDropped counts the categories left out of a date plan.

stoppedBy says why a run ended early. It keeps the first reason only.

  • null: all the requested work was done.
  • maxItems: the maxItems cap was reached.
  • chargeLimit: the maximum charge set for the run was reached.
  • deadline: the run reached its internal time limit.
  • circuitOpen: too many blocked responses in a row.
  • pageBudget: the request budget of the run was spent.
  • badInput: the Actor's access to the data source was refused. This is a configuration problem on the Actor's side and not in your input. Please report it in the Issues tab.
  • fetchFailed: the last walk ended on a page that could not be fetched. failedWalks lists the first 20 failed walks.

When a run stops for deadline, circuitOpen or pageBudget in the middle of a page, the rest of that page gets no details and the rows already read are kept.

Every business error is written to the errors dataset with a kind: BAD_INPUT, TARGET_BLOCKED, RATE_LIMITED, FETCH_FAILED, UPSTREAM_ERROR, SCHEMA_CHANGED, LISTING_INVALID or NOT_FOUND. The OUTPUT record counts errors in errorsByKind.

Frequently asked questions

Why do I get fewer matches than maxItems?

The mode, the sports and the days you chose hold fewer matches than the cap, or your leagues narrow the list. When no more matching matches exist, the run ends with stoppedBy set to null.

Why is details null on some rows?

Tennis rows get no details, and a row also stays null when the option is off or when nothing could be read for the match. Basketball rows have a sheet and line-ups but a timeline of null.

Does it return match statistics or player statistics?

No. A row gives the score, the half-time score and the periods, and with includeDetails the match sheet, the timeline of a football match and the line-ups. Possession, shots and other match statistics are not output, and neither are player statistics.

Does it return betting odds?

No. For odds, use Flashscore Matches Scraper, which adds the prices that operators listed in the United Kingdom show for a match.

Can I combine it with Flashscore Matches Scraper?

Yes. Both Actors share the same 23 first columns, in the same order, so their rows can be merged into a single table. Keep the source column to tell which site each row came from.

Is there a similar Actor for another site?

Yes, Flashscore Matches Scraper returns the same 23 first columns for football, tennis and basketball, with a timeline and odds.

Can I use SofaScore Sports Scraper via MCP?

Yes. Any Apify Actor can be called from an MCP client through the Apify MCP server. See the Apify MCP documentation for the setup.

Your feedback

We are always working on improving our Actors. If you have technical feedback for SofaScore Sports Scraper or found a bug, please create an issue on the Actor's Issues tab.

More free tools, scrapers, MCP servers and data studies from the same team: mr-bridge.com.

The data comes from the public pages and responses of SofaScore (https://www.sofascore.com) and belongs to its owners. SofaScore does not publish an API for it and has no link with this Actor. It has its own terms of use, and it may restrict automated access or commercial reuse. Before you rely on the output, in particular for commercial use, confirm that your use is permitted by the terms that apply. You remain responsible for how you use the results and for complying with the source's terms and with the laws that apply to you.