# SofaScore Scraper ⚽ Live Scores, Stats, Players & Odds (`abotapi/sofascore-scraper`) Actor

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.

- **URL**: https://apify.com/abotapi/sofascore-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Other, Developer tools, News
- **Features:** Limited permissions
- **Stats:** 590 total users, 95 monthly users, 80.9% runs succeeded, 2 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## 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

1. Pick a **mode**: `search` (by name), `url` (paste links), `live` (every live match now) or `scheduled` (all fixtures on a date).
2. Fill in the fields for that mode, and switch on the match details you need.
3. Set **Max items** to control run size and cost, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Search for a team:**

```json
{
  "mode": "search",
  "searchQueries": ["Real Madrid"],
  "searchType": "team",
  "maxItems": 5
}
```

**Every live football and tennis match right now:**

```json
{
  "mode": "live",
  "sports": ["football", "tennis"],
  "includeStatistics": true,
  "includeIncidents": true
}
```

**All fixtures for a date, plus the next two days:**

```json
{
  "mode": "scheduled",
  "sports": ["football"],
  "date": "2024-06-15",
  "daysAhead": 2,
  "maxItems": 200
}
```

**Paste links (match, team, player, tournament):**

```json
{
  "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.

```json
{
  "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) or `all` (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; `0` means 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:

```python
from apify_client import ApifyClient

client = 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:

```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 classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED` or `EXPIRED` (only after a run that scanned the whole scope, and only with `emitExpired`). `stateKey` names or shares the stored state. A record is `UPDATED` when 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 from `changedFields` (they are still returned in the row), so they alone never produce `UPDATED`. Add more with `ignoreFieldsForChanges` (dotted paths such as `raw.venue` work). Partial scans (Max items reached, Resume, refused targets) never mark records `EXPIRED`. 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**.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to 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.

```json
{
  "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:

<table>
<tr><td>⚽ <a href="https://apify.com/abotapi/fotmob-scraper"><b>FotMob Scraper ⚽ Match Details, Stats, Lineups &amp; Players</b></a><br>Pull structured football data from FotMob: match details with scores, team stats, events...</td><td>⚽ <a href="https://apify.com/abotapi/onefootball-com-scraper"><b>OneFootball Scraper</b></a><br>Scrape public OneFootball data including football news, upcoming fixtures, match results...</td></tr>
<tr><td>⚽ <a href="https://apify.com/abotapi/sportsbook-odds-scraper"><b>Sportsbook Odds Scraper (1xBet, Melbet, Linebet, Paripulse)</b></a><br>Collect live and prematch betting odds from 1xBet, Melbet, Linebet and Paripulse. Give it...</td><td>⚽ <a href="https://apify.com/abotapi/globo-ge"><b>Globo Esporte Scraper</b></a><br>Scrape public sports content from ge.globo.com, including news, videos, matches and feed...</td></tr>
<tr><td>⚽ <a href="https://apify.com/abotapi/eventim-de-event-scraper"><b>Eventim Scraper</b></a><br>Scrape CTS Eventim Germany and EU event catalogue: concerts, festivals, comedy, sports...</td><td>⚽ <a href="https://apify.com/abotapi/hotstar-com-scraper"><b>JioHotstar Scraper</b></a><br>Scrape the JioHotstar catalog by content type or URL. Extract shows, movies, episodes...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/sofascore-scraper/issues/open). 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.

# Actor input Schema

## `mode` (type: `string`):

How to discover what to scrape. SEARCH looks up teams/players/tournaments/matches by keyword. URL takes SofaScore links you paste. LIVE pulls every in-play match for the chosen sports. SCHEDULED pulls all fixtures for a date.

## `searchQueries` (type: `array`):

Keywords to look up: team, player, tournament, or match names. Each query resolves to the top matching entities, which are then scraped in full.

## `searchType` (type: `string`):

Limit which entity types a query resolves to. 'all' returns the best mix of teams, players, tournaments and matches.

## `urls` (type: `array`):

Paste SofaScore links: match pages (with the #id: part, or /event/<id>), team, player or tournament pages, or a daily fixtures page like /football/2026-10-15. To get every match of a league, turn on 'List every match of a tournament' below and paste the tournament link. Any host works (www, m., sofascore.app). Left untouched, one sample each of a match, team, tournament, player and daily fixtures page is scraped. The match link without the #id: part cannot be resolved and is skipped with a warning.

## `expandTournamentMatches` (type: `boolean`):

URL mode only. Turn a tournament link into all of that tournament's matches instead of the single tournament record. Paste the league page, e.g. .../football/tournament/england/championship/18. A link that ends in #id:<number> (the season the page shows) lists that one season; otherwise the Seasons option below decides. Each match is returned as a normal match record, so the detail options (statistics, lineups, incidents and so on) apply to every match. A full season is a few hundred matches, so set Max items to 0 for no limit.

## `tournamentSeasons` (type: `string`):

Which seasons to list when a tournament link names no season and 'List every match of a tournament' is on. 'Latest season' lists the season the site currently shows. 'All seasons' lists every season the site holds for that tournament, newest first, which can be thousands of matches. Ignored when the link ends in #id:<season>.

## `sports` (type: `array`):

Which sports to pull for Live and Scheduled modes.

## `date` (type: `string`):

Day to pull fixtures for, in YYYY-MM-DD. Leave blank to use today (UTC). A busy day holds several hundred fixtures across roughly a hundred competitions, so Max items decides how much of it this run collects.

## `daysAhead` (type: `integer`):

Also pull fixtures for this many days after the chosen date. 0 = just the one day.

## `includeStatistics` (type: `boolean`):

Include full match statistics (possession, shots, passes, xG, etc.) for match records. Adds one request per match.

## `includeLineups` (type: `boolean`):

Include starting XI, substitutes, formations and per-player ratings for match records. Adds one request per match.

## `includeIncidents` (type: `boolean`):

Include goals, cards, substitutions and period scores for match records. Adds one request per match.

## `includeOdds` (type: `boolean`):

Include pre-match betting odds markets for match records. Adds one request per match.

## `includeVotes` (type: `boolean`):

Include the fan prediction poll for match records: how many fans voted home win / draw / away win, both-teams-to-score, and first-to-score. The community sentiment signal. Adds one lightweight request per match.

## `includeH2H` (type: `boolean`):

Include the head-to-head record between the two teams (past meeting win/draw/loss totals, plus the two managers' record) for match records. Adds one request per match.

## `includeShotmap` (type: `boolean`):

Include the shot-by-shot map for football match records: every shot's xG and xGoT value, pitch coordinates, situation (open play, penalty, direct free kick, set piece, corner), outcome (goal, saved, blocked, off target, post), body part, shooter and goalkeeper. Adds one request per match.

## `includePlayerHeatmaps` (type: `boolean`):

Include the touch heatmap for every football player who took part in the match (the map the site draws on a player's card): every touch's pitch coordinate plus the total touch count per player. Football only; needs the lineups data and adds one request per player (typically 25-30 per match), so enable it only when you need it. Billed as one detail-enrichment event for every 10 players mapped.

## `includeStandings` (type: `boolean`):

Include the league table for tournament records. Adds one request per tournament.

## `includeSquad` (type: `boolean`):

Include the full player squad for team records. Adds one request per team.

## `maxItems` (type: `integer`):

The single cap: stop after this many entity records (0 = no limit). In Search mode it is spread fairly across your queries (each query returns up to 20, the SofaScore maximum). Add more queries to raise the realistic total. In Scheduled mode a day is collected competition by competition, most important first, and stops here, so a low cap returns the day's most prominent fixtures and a whole day needs a high cap or 0. Competitions are taken whole, so a run can return slightly more than this.

## `proxy` (type: `object`):

Datacenter connections are often refused by this site; the scraper retries on other connections and falls back to a residential or backup connection when the default one is refused. For reliable runs choose the residential group; residential traffic is billed to your Apify plan.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue an interrupted crawl without returning or charging for entities already collected there. For recurring monitoring of the same search/URLs, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching entities as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED entities. An entity is UPDATED when any returned field changed (score, status, teams, venue, standings, squad, incidents, statistics and so on); odds, fan votes and live-clock/change-feed bookkeeping are excluded and never trigger UPDATED, nor do fields you list in "Ignore fields when detecting changes" below. Turn on "Emit unchanged" or "Emit expired" only when you also want those entities returned (and billed). State is kept separately for each mode/scope/detail-toggle setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the mode, search/URL scope, and detail toggles — different setups then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return entities that have not changed since the last run, marked UNCHANGED. This returns — and bills — extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return entities that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked scope — not in Search mode (which returns only top relevance-ranked matches, never a full listing), and not when Max items capped the run or Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Optional. Field names to exclude from UPDATED change detection, in addition to the built-in handling (odds, votes and the live-clock/change-feed bookkeeping inside the raw event never trigger UPDATED). Nested fields use dots, e.g. raw.venue. Ignored fields are still returned in the row, just not compared.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "searchQueries": [
    "Real Madrid"
  ],
  "searchType": "all",
  "urls": [
    "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc#id:15526334",
    "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc?tab=statistics#id:15526334",
    "https://www.sofascore.com/event/15526334",
    "https://www.sofascore.com/arsenal-chelsea/RsR#id:12436911",
    "https://www.sofascore.com/team/football/real-madrid/2829",
    "https://www.sofascore.com/football/team/real-madrid/2829",
    "https://www.sofascore.com/football/tournament/england/premier-league/17",
    "https://www.sofascore.com/football/tournament/england/championship/18#id:77347",
    "https://www.sofascore.com/tournament/football/england/premier-league/17#id:61627",
    "https://www.sofascore.com/player/erling-haaland/839956",
    "https://www.sofascore.com/football/player/kylian-mbappe/826643",
    "https://www.sofascore.com/football/2026-10-15",
    "https://m.sofascore.com/team/football/real-madrid/2829",
    "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc",
    "https://www.sofascore.com/tournament/football/england/premier-league/17"
  ],
  "expandTournamentMatches": false,
  "tournamentSeasons": "current",
  "sports": [
    "football"
  ],
  "date": "",
  "daysAhead": 0,
  "includeStatistics": true,
  "includeLineups": true,
  "includeIncidents": true,
  "includeOdds": false,
  "includeVotes": false,
  "includeH2H": false,
  "includeShotmap": false,
  "includePlayerHeatmaps": false,
  "includeStandings": true,
  "includeSquad": false,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `matches` (type: `string`):

No description

## `teams` (type: `string`):

No description

## `players` (type: `string`):

No description

## `tournaments` (type: `string`):

No description

## `changes` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "search",
    "searchQueries": [
        "Real Madrid"
    ],
    "urls": [
        "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc#id:15526334",
        "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc?tab=statistics#id:15526334",
        "https://www.sofascore.com/event/15526334",
        "https://www.sofascore.com/arsenal-chelsea/RsR#id:12436911",
        "https://www.sofascore.com/team/football/real-madrid/2829",
        "https://www.sofascore.com/football/team/real-madrid/2829",
        "https://www.sofascore.com/football/tournament/england/premier-league/17",
        "https://www.sofascore.com/football/tournament/england/championship/18#id:77347",
        "https://www.sofascore.com/tournament/football/england/premier-league/17#id:61627",
        "https://www.sofascore.com/player/erling-haaland/839956",
        "https://www.sofascore.com/football/player/kylian-mbappe/826643",
        "https://www.sofascore.com/football/2026-10-15",
        "https://m.sofascore.com/team/football/real-madrid/2829",
        "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc",
        "https://www.sofascore.com/tournament/football/england/premier-league/17"
    ],
    "date": "",
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/sofascore-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "search",
    "searchQueries": ["Real Madrid"],
    "urls": [
        "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc#id:15526334",
        "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc?tab=statistics#id:15526334",
        "https://www.sofascore.com/event/15526334",
        "https://www.sofascore.com/arsenal-chelsea/RsR#id:12436911",
        "https://www.sofascore.com/team/football/real-madrid/2829",
        "https://www.sofascore.com/football/team/real-madrid/2829",
        "https://www.sofascore.com/football/tournament/england/premier-league/17",
        "https://www.sofascore.com/football/tournament/england/championship/18#id:77347",
        "https://www.sofascore.com/tournament/football/england/premier-league/17#id:61627",
        "https://www.sofascore.com/player/erling-haaland/839956",
        "https://www.sofascore.com/football/player/kylian-mbappe/826643",
        "https://www.sofascore.com/football/2026-10-15",
        "https://m.sofascore.com/team/football/real-madrid/2829",
        "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc",
        "https://www.sofascore.com/tournament/football/england/premier-league/17",
    ],
    "date": "",
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/sofascore-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "searchQueries": [
    "Real Madrid"
  ],
  "urls": [
    "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc#id:15526334",
    "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc?tab=statistics#id:15526334",
    "https://www.sofascore.com/event/15526334",
    "https://www.sofascore.com/arsenal-chelsea/RsR#id:12436911",
    "https://www.sofascore.com/team/football/real-madrid/2829",
    "https://www.sofascore.com/football/team/real-madrid/2829",
    "https://www.sofascore.com/football/tournament/england/premier-league/17",
    "https://www.sofascore.com/football/tournament/england/championship/18#id:77347",
    "https://www.sofascore.com/tournament/football/england/premier-league/17#id:61627",
    "https://www.sofascore.com/player/erling-haaland/839956",
    "https://www.sofascore.com/football/player/kylian-mbappe/826643",
    "https://www.sofascore.com/football/2026-10-15",
    "https://m.sofascore.com/team/football/real-madrid/2829",
    "https://www.sofascore.com/football/match/athletic-club-londrina/xPsAfMc",
    "https://www.sofascore.com/tournament/football/england/premier-league/17"
  ],
  "date": "",
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/sofascore-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/sofascore-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7ZDL3V6L8AMNKOvc3/builds/6jEAvBz62M2idcJUN/openapi.json
