SofaScore Scraper ⚽ Live Scores, Stats, Players & Odds
Pricing
from $1.00 / 1,000 results
SofaScore Scraper ⚽ Live Scores, Stats, Players & Odds
From $1/1K. Pull structured sports data from SofaScore across football, basketball, tennis, and 20+ sports. Search by keyword, paste URLs, fetch live matches, or download fixtures by date. Returns scores, stats, lineups, incidents, odds, teams, players, tournaments, and more.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
2
Bookmarked
549
Total users
94
Monthly active users
12 hours
Issues response
4 days ago
Last modified
Categories
Share
SofaScore Scraper: Live Scores, Stats, Lineups & Odds
SofaScore Scraper turns SofaScore into a clean sports data API. Get live scores, full match statistics, lineups with player ratings, goals and cards, betting odds, league tables, team squads and player profiles for football, basketball, tennis and 20+ other sports. Football matches can also carry the full shotmap (every shot's xG, coordinates, type and outcome) and the touch heatmap of every player who took part. Search by name, paste links, grab every live match, or download a whole day of fixtures, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.
Why This Scraper?
- Four ways to find data. Search by keyword, paste SofaScore links, pull every live match, or list all fixtures on a date. No need to build URLs by hand.
- Four record types, fully expanded. Matches, teams, players and tournaments each come with 100+ named fields, plus the complete source object so nothing is lost.
- Match depth on demand. Switch statistics, lineups, incidents, odds, fan votes, head-to-head history, the shot-level xG map and per-player touch heatmaps on or off per run, and pay only for what you use.
- League tables and squads. Tournament records can carry full standings; team records can carry the whole squad.
- 20+ sports. Football, basketball, tennis, ice hockey, American football, baseball, cricket, rugby, MMA and more.
- Built for schedules. Incremental mode returns only new and changed matches on recurring runs, and a refused run fails loudly instead of returning an empty dataset.
Use Cases
- Sports betting and odds research: collect pre-match odds, fan prediction votes and head-to-head records to build and backtest models.
- Live score apps and dashboards: feed live matches, incidents and statistics into your own site, bot or alerting tool.
- Fantasy sports and scouting: track player ratings, positions, market values and squad changes across leagues.
- Sports analytics and data science: build historical datasets of results, xG-bearing statistics and lineups for machine learning. The shotmap gives shot-level xG with pitch coordinates, and player heatmaps give raw touch coordinates for spatial models.
- Media and content teams: generate match previews, reports and league tables automatically.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
type | "match" (also "team", "player", "tournament") |
id | 10000001 |
sport | "football" |
name | "Home Team - Away Team" |
homeTeam / awayTeam | "Home Team" / "Away Team" |
homeScore / awayScore | 2 / 1 |
homeScoreHalftime / awayScoreHalftime | 1 / 0 |
statusType | "finished" |
startTimestamp | 1700000000 |
tournament / season / round | "Sample League" / "24/25" / 12 |
venue / venueCity | "Sample Stadium" / "Sample City" |
referee / attendance | "Referee Name" / 40000 |
url | SofaScore link to the match, team, player or tournament |
statistics | possession, shots, passes, xG (when enabled) |
lineups | starters, subs, formations, player ratings (when enabled) |
incidents | goals, cards, substitutions (when enabled) |
odds / votes | pre-match markets; fan prediction poll home / draw / away (when enabled) |
headToHead | past meetings and managers' record (when enabled) |
shotmap | football shot-by-shot map (when enabled): each shot's xG and xGoT, pitch coordinates, situation (open play, assisted, corner, set piece, fast break, penalty or direct free kick), outcome (goal, saved, blocked, miss), body part, shooter and goalkeeper |
playerHeatmaps | football touch heatmaps (when enabled): per player who took part, the total touch count and every touch's pitch coordinate |
standings | full league table on tournament records (when enabled) |
currentRound | current round of the tournament's latest season on tournament records (round, plus name and slug for cup rounds when the site supplies them); null when the season has no rounds |
squad | full player list on team records (when enabled) |
position / shirtNumber / marketValue | player records: "F" / 10 / 50000000 |
raw | the complete source object |
Team records also carry the manager, venue, capacity, colours and crest; player records carry the photo, preferred foot, contract end and market value currency; tournament records carry the tier, title holder and record title winner.
How to Use
- Pick a mode:
search(by name),url(paste links),live(every live match now) orscheduled(all fixtures on a date). - Fill in the fields for that mode, and switch on the match details you need.
- Set Max items to control run size and cost, then click Start.
- Download the dataset as JSON, CSV or Excel, or read it through the API.
Search for a team:
{"mode": "search","searchQueries": ["Real Madrid"],"searchType": "team","maxItems": 5}
Every live football and tennis match right now:
{"mode": "live","sports": ["football", "tennis"],"includeStatistics": true,"includeIncidents": true}
All fixtures for a date, plus the next two days:
{"mode": "scheduled","sports": ["football"],"date": "2024-06-15","daysAhead": 2,"maxItems": 200}
Paste links (match, team, player, tournament):
{"mode": "url","urls": ["https://www.sofascore.com/team/football/real-madrid/2829","https://www.sofascore.com/player/lionel-messi/12994"]}
Match links must include the numeric id: copy the address bar on a match page (it ends with #id:<number>, with or without ?tab=...), or use an /event/<number> link. A match link with only the short code and no #id: is skipped with a warning.
Supported link types (any host: www.sofascore.com, m.sofascore.com, sofascore.com, sofascore.app):
| Link | Example |
|---|---|
Match with #id: | https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc#id:15526334 |
| Event | https://www.sofascore.com/event/15526334 |
| Team | https://www.sofascore.com/team/football/real-madrid/2829 or /football/team/real-madrid/2829 |
| Tournament | https://www.sofascore.com/football/tournament/england/premier-league/17 or /tournament/football/england/premier-league/17 (returns the tournament record; turn on expandTournamentMatches to get its matches, see below) |
| Tournament season | https://www.sofascore.com/football/tournament/england/championship/18#id:77347 (the trailing #id:<number> is the season the page shows) |
| Player | https://www.sofascore.com/player/erling-haaland/839956 |
| Daily fixtures | https://www.sofascore.com/football/2026-10-15 (every fixture that day, up to Max items) |
All matches of a league or season: paste the tournament link and turn on expandTournamentMatches. The actor then lists every match of that tournament and returns each one as a normal match record, with statistics, lineups, incidents and the other detail options applied to every match. No need to paste match links one by one.
{"mode": "url","urls": ["https://www.sofascore.com/football/tournament/england/championship/18#id:77347"],"expandTournamentMatches": true,"maxItems": 0}
- A link ending in
#id:<number>lists that one season (the number is the season id the page shows). The example above is the whole 2025/26 season. - A link without it lists the season set by
tournamentSeasons:current(latest season, the default) orall(every season the site holds for the tournament, newest first). - Both played and upcoming matches are listed, plus postponed and cancelled fixtures with their
statusType(a postponed match that was replayed appears twice, once per site entry). A match appears once even if several links cover it. - Each match is a billed record like any other, so set Max items to the number you want;
0means no limit. Switch off the detail options you do not need (each one is an extra request per match) to make a large run faster and cheaper. - If the platform time limit gets close, the run stops cleanly and keeps what it collected. Run again with Resume to continue.
Default change: if you run URL mode without setting urls, the actor now scrapes one sample each of a match, team, tournament, player and daily fixtures page instead of nothing. The full list of shapes is pre-filled in the form; the match link without #id: is skipped with a warning. If you send only startUrls, those links replace the sample list (they are never added to it).
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/sofascore-scraper").call(run_input={"mode": "live", "sports": ["football"]})for match in client.dataset(run["defaultDatasetId"]).iterate_items():print(match["name"], match["homeScore"], match["awayScore"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/sofascore-scraper').call({ mode: 'live', sports: ['football'] });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
How maxItems shapes a scheduled run
A single day of football holds several hundred fixtures across roughly a hundred competitions. Scheduled mode collects a day competition by competition, most important first (world and continental, then major national leagues, then the long tail), and stops once maxItems is reached. A capped run therefore returns the day's most prominent fixtures. To get the entire day, raise maxItems or set it to 0. Competitions are taken whole, so a run can return slightly more than maxItems.
Resume and recurring updates
- Resume (
resumeFromRunId) continues one interrupted run: paste its run or dataset ID and the actor skips everything already collected there, so you don't pay twice. - Incremental mode (
incrementalMode) is for scheduled runs over the same scope. Each record is classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on),REAPPEAREDorEXPIRED(only after a run that scanned the whole scope, and only withemitExpired).stateKeynames or shares the stored state. A record isUPDATEDwhen any returned field changed (score, status, venue, incidents, statistics, standings, squad and so on). Odds, fan votes and the live-clock and change-feed bookkeeping inside the raw event are excluded from the comparison and fromchangedFields(they are still returned in the row), so they alone never produceUPDATED. Add more withignoreFieldsForChanges(dotted paths such asraw.venuework). Partial scans (Max items reached, Resume, refused targets) never mark recordsEXPIRED. With incremental mode off, output is exactly as before.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search, url, live, or scheduled. |
searchQueries | array | ["Real Madrid"] | Names to look up (search mode). |
searchType | string | all | Restrict a search to team, player, tournament, or match. |
urls | array | one sample per link type | SofaScore links (url mode). |
expandTournamentMatches | boolean | false | URL mode: list every match of each tournament link instead of the tournament record. |
tournamentSeasons | string | current | current or all; which seasons to list for a tournament link without #id:<season>. |
sports | array | ["football"] | Sports for live and scheduled modes. |
date | string | today | YYYY-MM-DD for scheduled mode. |
daysAhead | integer | 0 | Extra days after date. |
includeStatistics | boolean | true | Match and player statistics. |
includeLineups | boolean | true | Match lineups and ratings. |
includeIncidents | boolean | true | Goals, cards, substitutions. |
includeOdds | boolean | false | Pre-match odds markets. |
includeVotes | boolean | false | Fan prediction poll per match. |
includeH2H | boolean | false | Head-to-head history per match. |
includeShotmap | boolean | false | Football shotmap: xG, coordinates, situation and outcome per shot. |
includePlayerHeatmaps | boolean | false | Football touch heatmaps for every player who took part (one request per player, about 25-30 per match). |
includeStandings | boolean | true | Tournament league tables. |
includeSquad | boolean | false | Full team squads. |
maxItems | integer | 20 | Stop after this many records (0 = no limit). |
resumeFromRunId | string | (none) | Continue one interrupted run. |
incrementalMode | boolean | false | Return only new and changed records on scheduled runs. |
ignoreFieldsForChanges | array | [] | Incremental mode only: field names (dotted paths allowed) excluded from change detection. |
stateKey | string | (none) | Name or share an incremental-mode state. |
emitUnchanged | boolean | false | Also return (and bill) unchanged records. |
emitExpired | boolean | false | Also return (and bill) expired records. |
proxy | object | Apify Proxy | Connection settings. |
mcpConnectors | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | (none) | Notion connector only: page under which records are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. |
Output Example
The Output tab offers one table per record kind: Matches, Teams, Players, Tournaments, plus a Changes table (change type, changed fields, first and last seen) for incremental runs. Every record carries a type field (match, team, player, tournament). Each run also saves a run summary to the key-value store under OUTPUT (records by type, targets scanned and refused).
Sample shape: values are illustrative placeholders, not from a live record.
{"type": "match","id": 10000001,"sport": "football","name": "Home Team - Away Team","homeTeam": "Home Team","awayTeam": "Away Team","homeTeamLogo": "https://img.sofascore.com/api/v1/team/2000001/image","awayTeamLogo": "https://img.sofascore.com/api/v1/team/2000002/image","homeScore": 2,"awayScore": 1,"statusType": "finished","winnerCode": 1,"startTimestamp": 1700000000,"tournament": "Sample League","season": "24/25","round": 12,"venue": "Sample Stadium","referee": "Referee Name","attendance": 40000,"country": "Country","url": "https://www.sofascore.com/football/match/home-team-away-team/AbCdEf#id:10000001","statistics": [{ "period": "ALL", "groups": [] }],"incidents": [{ "incidentType": "goal", "time": 23, "player": { "name": "Player Name" } }],"raw": {}}
Plan Requirement
Datacenter connections are often refused by this site's API. The actor retries automatically on other connections and, when the default connection is refused, falls back to a residential or backup connection (it logs "Served over the backup connection (the default connection was unavailable)." when that happens). For reliable runs choose the residential group under Proxy & connection; residential traffic is billed to your Apify plan. Apify proxy access needs a paid Apify plan. On a plan without it, the run stops with a clear message instead of an empty result: "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required". If you choose residential on a free plan, the message says residential proxies are unavailable and asks you to upgrade or supply your own proxy URLs. If nothing could be loaded for another reason you get "Could not load any results page ... This is a connection problem, not an empty search."
FAQ
How much does it cost?
Pay per event. The current rates are shown on the Pricing tab (paid Apify plans get lower tiered rates). The events and when each one is charged:
| Event | When it is charged |
|---|---|
Result (apify-default-dataset-item) | once for each record pushed to the dataset |
Detail enrichment (detail-enrichment) | once per returned record that needed at least one extra detail lookup, no matter how many: match statistics, lineups, incidents, odds, votes, head-to-head or the shotmap; a team squad (includeSquad); player season statistics (includeStatistics); or the tournament season lookup, which every tournament record makes. A match record with the player heatmaps section delivered bills this event once for every 10 players mapped (the record's own lookups fold into the first block, so a typical match with about 30 players mapped is charged 3 events, and a record whose heatmaps could not be read stays at 1). Records that needed no extra lookup (for example a team without squad, or a match with all detail switches off) are not charged this event |
Actor Start (apify-actor-start) | once per run, per GB of memory |
Unchanged or suppressed records in incremental mode are not billed. Use Max items to cap the cost of any run.
Is it legal to scrape SofaScore?
This actor collects only publicly available sports data. You are responsible for how you use it: follow SofaScore's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Sports results and statistics are generally facts, but logos, photos and odds may be subject to third-party rights.
Can I get only new or changed matches on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated records, and unchanged ones are not billed.
Why did a run return fewer matches than a full day?
Scheduled mode stops at Max items, most important competitions first. Set it higher, or to 0, to collect the whole day.
How do I get all matches of a league?
Use URL mode, paste the league page link, turn on expandTournamentMatches and set Max items to 0. Add #id:<season> to the link (copy it from the address bar) for one particular season, or set tournamentSeasons to all for every season. Cup rounds and league rounds are both included.
Why did my run fail instead of returning an empty dataset?
If the site refuses every request, the run stops with a clear message so "no matches today" is never confused with "nothing could be read". Run it again in a few minutes. If only some sports or dates could be read, you still get what was found.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
🔗 Want more sports data?
Pair this actor with these related scrapers from the same team:
| ⚽ FotMob Scraper ⚽ Match Details, Stats, Lineups & Players Pull structured football data from FotMob: match details with scores, team stats, events... | ⚽ OneFootball Scraper Scrape public OneFootball data including football news, upcoming fixtures, match results... |
| ⚽ Sportsbook Odds Scraper (1xBet, Melbet, Linebet, Paripulse) Collect live and prematch betting odds from 1xBet, Melbet, Linebet and Paripulse. Give it... | ⚽ Globo Esporte Scraper Scrape public sports content from ge.globo.com, including news, videos, matches and feed... |
| ⚽ Eventim Scraper Scrape CTS Eventim Germany and EU event catalogue: concerts, festivals, comedy, sports... | ⚽ JioHotstar Scraper Scrape the JioHotstar catalog by content type or URL. Extract shows, movies, episodes... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.