# Sofascore Match Stats, xG & Lineups Scraper (`incognito_mode/sofascore-match-analytics-scraper`) Actor

Deep Sofascore match data: xG shotmaps with pitch coordinates, lineups with 33 per-player stats and ratings, attack-momentum graphs, average-position maps, incident timelines, team statistics and odds. Feed it match IDs or URLs. No API key, no login, and no residential proxy needed.

- **URL**: https://apify.com/incognito\_mode/sofascore-match-analytics-scraper.md
- **Developed by:** [Elena Vance](https://apify.com/incognito_mode) (community)
- **Categories:** Developer tools, Automation, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 analysed matches

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Sofascore Match Analytics Scraper

Deep per-match data from Sofascore: **xG shotmaps with pitch coordinates**,
**lineups with 33 per-player stats and ratings**, **attack-momentum graphs**,
**average-position maps**, incident timelines, team statistics, bookmaker odds
and more. No Sofascore API key, no login.

Give it match IDs or Sofascore URLs and pick exactly which sub-resources you
want — every switch is one extra request per match, so a narrow run is a cheap
run.

Results are stored in an Apify dataset you can export as **JSON, CSV, Excel,
XML or HTML**.

***

### Why this is cheap to run

**No proxy required, and no premium plan required.**

Sofascore is called **directly** — no headless browser, and no proxy at all
unless a request is actually blocked. If an IP does get blocked, the Actor
escalates on its own, cheapest first: a **datacenter proxy** (included in
*every* Apify plan, free tier included) with a few IPs rotated, and only then
a residential one if your plan has it. **There is no residential-proxy add-on
requirement.**

### Pairs with the Live & Daily Scores Scraper

This Actor consumes match IDs. The
[Sofascore Live Scores & Fixtures Scraper](https://apify.com/incognito_mode/sofascore-live-scores-scraper) emits
`eventId` for every match it finds, so the usual pattern is: find matches with
that Actor, analyse them with this one. You can also paste match URLs straight
in.

### What makes this different

- **Eight sub-resources, each independently switchable.** Shotmap, lineups,
  average positions, attack momentum, incidents, team statistics, odds and
  heatmaps are separate flags, and each is one extra request per match. You
  pay for the shape of data you asked for and nothing else.
- **Heatmaps are billed only when they actually arrive.** The surcharge keys
  on the field written to the record, not on the input flag, so asking for
  heatmaps on a match that has none costs nothing extra.
- **xG shotmaps carry pitch coordinates**, not just shot totals — enough to
  redraw the map or compute your own spatial metrics.
- **33 per-player stats with ratings**, per player, per match.
- **Failures are never billed**, and unusable input returns an unbilled
  diagnostic row rather than failing the run.
- **No API key, no login, no headless browser, no residential-proxy add-on** —
  see [Why this is cheap to run](#why-this-is-cheap-to-run).

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `eventIds` | array of integers | – | Sofascore match IDs, e.g. `[16363246]`. |
| `matchUrls` | array of strings | – | Match URLs, e.g. `https://www.sofascore.com/football/match/newcastle-liverpool#id:16363246`. |
| `includeStatistics` | boolean | `true` | Team stats per period. |
| `includeLineups` | boolean | `true` | Formations plus 33 stats per player. |
| `includeIncidents` | boolean | `true` | Goals, cards, subs timeline. |
| `includeShotmap` | boolean | `true` | Shots with coordinates and xG. |
| `includeGraph` | boolean | `true` | Attack momentum per minute. |
| `includeAveragePositions` | boolean | `false` | Average pitch position per player. |
| `includeBestPlayers` | boolean | `false` | Best player per side. |
| `includeTeamStreaks` | boolean | `false` | Form and head-to-head streaks. |
| `includeVotes` | boolean | `false` | Crowd predictions. |
| `includeWinProbability` | boolean | `false` | Home / draw / away probability. |
| `includeManagers` | boolean | `false` | Both managers. |
| `includeH2H` | boolean | `false` | Historical win/draw/loss tally. |
| `includeOdds` | boolean | `false` | Bookmaker markets, fractional odds. |
| `includeComments` | boolean | `false` | Text commentary (largest payload). |
| `includeHeatmaps` | boolean | `false` | Touch heatmap per player — **one request per player**, ~28 per football match. |
| `maxItems` | integer | `100` | Cap on matches stored. |
| `proxyConfiguration` | object | off | Leave off; see above. |

At least one of `eventIds` / `matchUrls` is required.

### Sport coverage — read this before running

**Only football has every sub-resource.** Other sports return a useful subset,
and anything a sport doesn't support is set to `null` rather than failing the
run.

| Sub-resource | football | basketball | am. football | baseball | volleyball | ice-hockey | tennis | handball |
| --- | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
| statistics | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| h2h / votes | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| team streaks | ✅ | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ |
| odds | ✅ | ✅ | ✅ | ✅ | ✅ | — | ✅ | — |
| incidents | ✅ | ✅ | ✅ | — | ✅ | ✅ | — | — |
| lineups | ✅ | ✅ | ✅ | ✅ | — | — | — | — |
| graph / best players | ✅ | ✅ | ✅ | — | — | — | — | — |
| average positions | ✅ | — | ✅ | ✅ | — | — | — | — |
| **shotmap (xG)** | ✅ | — | — | — | — | — | — | — |
| **win probability** | ✅ | — | — | — | — | — | — | — |

**A note on xG:** Sofascore only models expected goals for major competitions.
A Premier League match carries xG on every shot; a minor cup tie carries none.
Where it isn't modelled, `xg` and `xgot` are `null` rather than `0`, so you can
tell "not modelled" from "genuinely zero".

### Output

One row per match. Requested-but-unavailable sub-resources are `null`.

```json
{
  "eventId": 16363246,
  "sport": "football",
  "tournament": { "name": "Premier League", "uniqueTournamentId": 17 },
  "homeTeam": { "id": 39, "name": "Newcastle United", "shortName": "Newcastle" },
  "awayTeam": { "id": 44, "name": "Liverpool FC", "shortName": "Liverpool" },
  "homeScore": 2,
  "awayScore": 2,
  "status": { "code": 100, "description": "Ended", "type": "finished" },
  "venue": { "name": "St James' Park", "city": "Newcastle upon Tyne" },
  "referee": "Stuart Attwell",
  "statistics": {
    "ALL": {
      "ballPossession": { "home": "39%", "away": "61%", "homeValue": 39, "awayValue": 61 },
      "expectedGoals": { "home": "1.43", "away": "2.73", "homeValue": 1.43, "awayValue": 2.73 }
    }
  },
  "shotmap": [
    {
      "player": { "id": 869856, "name": "Dominik Szoboszlai" },
      "shotType": "goal", "situation": "penalty", "bodyPart": "right-foot",
      "xg": 0.7884, "xgot": 0.9887,
      "playerCoordinates": { "x": 88, "y": 50 }, "time": 90
    }
  ],
  "lineups": {
    "home": { "formation": "4-2-3-1", "players": [{ "id": 979232, "name": "Anthony Elanga", "rating": 7.7, "minutesPlayed": 90 }] }
  },
  "graph": [{ "minute": 1, "value": -7 }, { "minute": 2, "value": -6 }]
}
```

`statistics` is keyed by Sofascore's own stat key, so you can pull
`statistics.ALL.expectedGoals.homeValue` directly instead of walking nested
group arrays.

### Example: one match, everything cheap

```json
{ "eventIds": [16363246] }
```

### Example: xG and tactical shape for several matches

```json
{
  "eventIds": [16363246, 16248441],
  "includeShotmap": true,
  "includeAveragePositions": true,
  "includeLineups": true,
  "includeStatistics": false,
  "includeIncidents": false
}
```

### Example: from a URL you pasted

```json
{ "matchUrls": ["https://www.sofascore.com/football/match/newcastle-liverpool#id:16363246"] }
```

***

### The Sofascore Actor suite

Six Actors from the same publisher, built on one shared HTTP layer and one ID
space — so the output of one is the input of the next.

| Actor | What it returns |
| --- | --- |
| [Live Scores & Fixtures](https://apify.com/incognito_mode/sofascore-live-scores-scraper) | Fixtures and live results by date across 19 sports. Emits `eventId`. |
| **Match Stats, xG & Lineups** | *This Actor.* |
| [League Standings & Tables](https://apify.com/incognito_mode/sofascore-standings-scraper) | League tables, group stages, conferences and divisions. Takes `uniqueTournamentId`. |
| [Team & Player Data](https://apify.com/incognito_mode/sofascore-team-player-scraper) | Squads, bios, market values, transfers, match logs. Takes team and player IDs. |
| [Season Stats](https://apify.com/incognito_mode/sofascore-season-stats-scraper) | Season leaderboards and complete team statistics. Takes `uniqueTournamentId`. |
| [Rankings & Search](https://apify.com/incognito_mode/sofascore-rankings-scraper) | FIFA, ATP/WTA, UEFA and UFC tables, plus name-to-ID search. |

**How they chain.** Live Scores emits the `eventId` that Match Stats takes.
Rankings' search turns a name into the team, player and tournament IDs the
other five accept. Standings and Season Stats share `uniqueTournamentId`, and
Team & Player emits that same ID as `primaryTournamentId`.

All six need no API key, no login and no headless browser; none requires a
residential-proxy add-on, and all run on the Apify free plan.

# Actor input Schema

## `eventIds` (type: `array`):

Sofascore event IDs, e.g. \[16363246, 16248441]. These are the `eventId` values produced by the Sofascore Live & Daily Scores Scraper, so you can chain the two Actors. You can supply IDs here, URLs below, or both.

## `matchUrls` (type: `array`):

Sofascore match URLs, e.g. https://www.sofascore.com/football/match/newcastle-liverpool#id:16363246 . The ID is read from the #id: fragment.

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

Side-by-side team stats per period (possession, shots, xG, passes, duels, defending, goalkeeping). Flattened into a lookup keyed by Sofascore's own stat key. Available for most sports.

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

Formations, starters, bench, and 33 per-player statistics including match rating, expected assists and ball-carry distance. Football, basketball, American football and baseball only.

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

Chronological goals, cards, substitutions, period markers and injury time. Not available for tennis, baseball or handball.

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

Every shot with pitch coordinates, expected goals (xG), xGOT, body part, situation and goalkeeper. Football only -- this is the headline dataset.

## `includeGraph` (type: `boolean`):

Minute-by-minute momentum from -100 (away dominance) to +100 (home). Football, basketball and American football.

## `includeAveragePositions` (type: `boolean`):

Each player's average X/Y position on the pitch with a touch count -- a tactical shape map. Football, American football and baseball.

## `includeBestPlayers` (type: `boolean`):

Best home and away player plus a rating leaderboard for the match.

## `includeTeamStreaks` (type: `boolean`):

Running form streaks for both sides and head-to-head (e.g. "more than 2.5 goals, 4/5"). Useful for betting models.

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

Sofascore users' predictions: 1X2, both teams to score, first team to score, and who should have won.

## `includeWinProbability` (type: `boolean`):

Home win / draw / away win probabilities. Football only.

## `includeManagers` (type: `boolean`):

Home and away manager for the match.

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

Historical win/draw/loss tally between the two teams. Note this is a tally only -- it does not include past match records.

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

Betting markets with fractional odds and the winning selection (full time 1X2, over/under, both teams to score, handicaps).

## `includeComments` (type: `boolean`):

Text commentary for the match. The largest payload by far (~23 KB per match), so it is off by default.

## `includeHeatmaps` (type: `boolean`):

Touch-coordinate heatmap for every player who took part. This costs one extra request PER PLAYER -- around 28 per football match -- so it is much slower and pricier than any other option. Requires lineups, which are switched on automatically if needed.

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

Hard cap on how many matches are stored. The run stops as soon as it is reached.

## `proxyConfiguration` (type: `object`):

Leave off. Every request calls Sofascore directly first, which costs nothing. If an IP is blocked the Actor escalates on its own: first to a datacenter proxy (included in every Apify plan, free tier included), then to residential only if your plan has it. No residential add-on is required.

## Actor input object example

```json
{
  "eventIds": [
    16363246
  ],
  "includeStatistics": true,
  "includeLineups": true,
  "includeIncidents": true,
  "includeShotmap": true,
  "includeGraph": true,
  "includeAveragePositions": false,
  "includeBestPlayers": false,
  "includeTeamStreaks": false,
  "includeVotes": false,
  "includeWinProbability": false,
  "includeManagers": false,
  "includeH2H": false,
  "includeOdds": false,
  "includeComments": false,
  "includeHeatmaps": false,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing every analysed match.

# 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 = {
    "eventIds": [
        16363246
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("incognito_mode/sofascore-match-analytics-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 = {
    "eventIds": [16363246],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("incognito_mode/sofascore-match-analytics-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 '{
  "eventIds": [
    16363246
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call incognito_mode/sofascore-match-analytics-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,incognito_mode/sofascore-match-analytics-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/WPJMM6ewL57PiaSva/builds/eldlTindj0cP5KCx9/openapi.json
