# Sofascore Team & Player Data Scraper (`incognito_mode/sofascore-team-player-scraper`) Actor

Scrape football, basketball and hockey clubs and athletes from Sofascore. Squads with market values and contracts, player bios, radar attributes, transfer history, match logs with ratings and full season statistics. Expand a club into every player in one run. No API key, no login.

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

## Pricing

from $2.50 / 1,000 player records

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 Team & Player Data Scraper

Scrapes clubs and athletes from Sofascore's public API — squads with market
values and contracts, player bios, radar attributes, transfer history, match
logs with per-match ratings and full season statistics. Paste a team URL and
get the club plus every player in it.

No API key, no login, no browser, and **no proxy unless a request is actually
blocked**.

### What you get

One dataset row per team or player. An `entityType` column says which.

**Team rows** — name, country, founded year, manager, stadium with capacity
and coordinates, club colours, main competition, plus (optionally) the full
squad, transfers in and out, recent results, upcoming fixtures, the
competition-season catalogue, and season statistics.

**Player rows** — name, position, shirt number, height, weight, preferred
foot, date of birth and age, contract expiry, market value, current club and
the competition it plays in, plus
(optionally) radar attributes, career transfers, a match log carrying their
rating and minutes for each game, national-team caps and season statistics.

### What makes this different

- **Squad expansion: one club in, every player out.** Paste a team URL and get
  the club row plus a full record for each player in the squad, in a single
  run, without collecting player IDs by hand first.
- **A 28-man squad is one team charge, not 28.** The per-player charge applies
  only to players expanded into their own full records.
- **Market values, contract expiry and transfer history**, alongside radar
  attributes and a match log carrying each player's rating and minutes per
  game.
- **`primaryTournamentId` on every row** — the same ID the Standings and
  Season Stats Actors take as input, so a team row chains straight into them.
- **Season statistics are bounded before the run.** "All seasons" multiplies
  per competition and a top player has 19 of them, so `maxSeasons` caps the
  cost up front rather than surprising you afterwards.

### Input

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `teams` | array | — | Team IDs or Sofascore team URLs |
| `players` | array | — | Player IDs or Sofascore player URLs |
| `includeSquad` | bool | `true` | Teams only |
| `includeResults` | bool | `true` | Last 30 matches; players get their own rating per match |
| `includeTransfers` | bool | `true` | Club ins/outs, or a player's career moves |
| `includeAttributes` | bool | `true` | Radar percentiles. Players, football only |
| `includeFixtures` | bool | `false` | Next 30 matches. Teams only |
| `includeCompetitions` | bool | `false` | Every competition-season, with the IDs to fetch them |
| `includeStatistics` | bool | `false` | **Expensive** — one request per season |
| `includeNationalTeam` | bool | `false` | Caps, goals, debut. Players, football only |
| `includeCharacteristics` | bool | `false` | Unlabelled style codes — see below |
| `expandSquad` | bool | `false` | Scrape every squad player as a full record too |
| `seasonScope` | enum | `latest` | `latest` or `all` |
| `maxSeasons` | int | `5` | Cap for `seasonScope: all` |
| `maxEventPages` | int | `1` | 30 matches per page |
| `maxItems` | int | `100` | Total records, squad expansion included |
| `proxyConfiguration` | object | off | Only to pin a specific proxy |

At least one of `teams` or `players` is required. A URL is read as whatever it
says it is, so a player URL pasted into the team box still works.

Both URL spellings parse:

```
https://www.sofascore.com/football/team/manchester-city/17      # current
https://www.sofascore.com/team/football/manchester-city/17      # older, 301s
https://www.sofascore.com/football/player/erling-haaland/839956
```

### Squad expansion

`expandSquad` is what turns this from a lookup into a database: after scraping
a club, every player in its squad is queued as a full player record. One
Premier League club becomes about 28 records. It is bounded by `maxItems`,
deduplicated (clubs in one league share loan players), and **off by default**,
because it multiplies both the run time and the bill by the size of a squad.

### Sport coverage

Every sport Sofascore covers has teams and players, but they do not carry the
same fields. Nothing is dropped or faked — a field a sport does not report
simply arrives as `null`.

| Field | Football | Basketball |
| --- | :-: | :-: |
| Name, position, height, date of birth, country | ✅ | ✅ |
| First / last name split | ✅ | ❌ |
| Preferred foot, market value | ✅ | ❌ |
| Weight, salary, roster status | ❌ | ✅ |
| Radar attributes (`attributes`) | ✅ | ❌ 404 |
| National-team record | ✅ | ❌ |
| Season statistics | `overall` | `regularSeason` |

That last row is the one to know about. **Whether a competition's statistics
are called `overall` or `regularSeason` is a property of the competition, not
of the sport** — the same basketball player has `regularSeason` for the NBA
and `overall` for FIBA EuroBasket. Player records read the right spelling out
of Sofascore's own `typesMap`; team records probe for it once per
competition-season and cache the answer for the rest of the run. Either way
the `statisticsType` field on each block records which was used.

### Season statistics

Off by default, because they cost a discovery request plus **one request per
season**. With `seasonScope: latest` that is one season — the newest of the
team's or player's main competition. With `all` it walks every competition
they have played in, newest first, capped by `maxSeasons`.

Sofascore lists a season as soon as it is *scheduled* but only publishes
statistics once it has started, so in the summer the newest entry is routinely
empty. `latest` falls back to the two previous seasons **of the same
competition** rather than returning nothing, and the `seasonId` / `seasonYear`
on each block says which season you actually got.

### Notes

- **Playing-style codes are unlabelled.** Sofascore used to send
  `{"type": 1, "name": "Finishing", "level": 3}` and now sends
  `{"type": 5, "rank": 16}`. The labels are gone and there is no published
  mapping, so `includeCharacteristics` is off by default and ships the raw
  codes rather than pretending to know what they mean.
- **Invalid input does not fail the run.** A mistyped ID is recorded as an
  unbilled `{"error": "INVALID_INPUT"}` row with an explanation, and the run
  finishes green. One bad entry in a list never costs the good ones.
- **A run where every request failed does fail**, loudly, with the real error
  — rather than finishing green with an empty dataset.

### 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](https://apify.com/incognito_mode/sofascore-match-analytics-scraper) | Per-match shotmaps with pitch coordinates, lineups, ratings, momentum. Takes `eventId`. |
| [League Standings & Tables](https://apify.com/incognito_mode/sofascore-standings-scraper) | League tables, group stages, conferences and divisions. Takes `uniqueTournamentId`. |
| **Team & Player Data** | *This Actor.* |
| [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.

### Development

```bash
python -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
.venv/bin/python -m pytest -q          # 144 tests, no network
apify run                              # needs storage/key_value_stores/default/INPUT.json
```

See [`docs/architecture.md`](docs/architecture.md) for how it works,
[`docs/ci-cd.md`](docs/ci-cd.md) for the pipeline, and
[`docs/publishing.md`](docs/publishing.md) for the Store listing.

# Actor input Schema

## `teams` (type: `array`):

Clubs and national teams to scrape. Paste Sofascore team URLs (copy them from your browser) or team IDs. Examples: 17 = Manchester City, 2817 = Real Madrid, 3427 = LA Lakers.

## `players` (type: `array`):

Players to scrape. Paste Sofascore player URLs or player IDs. Examples: 839956 = Erling Haaland, 12994 = Lionel Messi, 861608 = Luka Dončić.

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

Every player currently at the club, with shirt number, position, height, date of birth, nationality, contract expiry and market value where the sport reports them. Basketball adds weight and salary; football adds preferred foot and market value. (Teams only.)

## `includeResults` (type: `boolean`):

The last 30 matches -- for a team its own fixtures, for a player the matches they appeared in, each with their match rating, minutes played, goals and cards. One request per page; see "Result pages".

## `includeTransfers` (type: `boolean`):

For a team, every incoming and outgoing transfer with fee, currency and date. For a player, their full career move history. Football clubs only in practice -- other sports return nothing.

## `includeAttributes` (type: `boolean`):

Sofascore's 0-100 percentile radar (attacking, technical, tactical, defending, creativity) for each of the last few years, plus the positional average to compare against. Football only. (Players only.)

## `includeFixtures` (type: `boolean`):

The next 30 scheduled matches for the club, with opponent, competition, round and kick-off time. (Teams only.)

## `includeCompetitions` (type: `boolean`):

Every competition and season this team or player has data for -- the tournament and season IDs you need to request statistics, or to feed the Sofascore League Standings Scraper.

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

Full season statistics -- 117 metrics for football, fewer elsewhere. This costs one discovery request plus one request PER SEASON, so it is the slowest and priciest option here and is billed as its own event. Use "Seasons to fetch" to control how many.

## `includeNationalTeam` (type: `boolean`):

Caps, goals and debut date for each national team the player has represented. Football only. (Players only.)

## `includeCharacteristics` (type: `boolean`):

Sofascore's strength and weakness codes with a global rank. NOTE: Sofascore stopped sending the human-readable labels for these, so they arrive as bare integer type codes. Off by default for that reason. Football only. (Players only.)

## `expandSquad` (type: `boolean`):

Turns one club into a full scouting database: after scraping a team, every player in its squad is scraped as a complete player record too. A Premier League club adds around 27 player records, each billed as a player. Requires the Squad option and respects the maximum-records limit.

## `seasonScope` (type: `string`):

Which seasons "Season statistics" covers. 'Latest' is one season -- the newest of the team's or player's main competition. 'All' walks every competition they have ever played in, newest first, and each season is a separate request and a separate charge. Haaland alone has 19 competitions.

## `maxSeasons` (type: `integer`):

Hard cap on how many seasons of statistics are fetched for one team or player when "All" is selected above. Seasons are taken newest-first, main competition first.

## `maxEventPages` (type: `integer`):

How many pages of matches to read for "Recent results" and "Upcoming fixtures". One page is 30 matches and one request. Raise it for a longer history.

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

Stop after this many team and player records in total. Squad expansion counts against it.

## `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
{
  "teams": [
    "https://www.sofascore.com/football/team/manchester-city/17"
  ],
  "includeSquad": true,
  "includeResults": true,
  "includeTransfers": true,
  "includeAttributes": true,
  "includeFixtures": false,
  "includeCompetitions": false,
  "includeStatistics": false,
  "includeNationalTeam": false,
  "includeCharacteristics": false,
  "expandSquad": false,
  "seasonScope": "latest",
  "maxSeasons": 5,
  "maxEventPages": 1,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Dataset containing every team and player record.

# 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 = {
    "teams": [
        "https://www.sofascore.com/football/team/manchester-city/17"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("incognito_mode/sofascore-team-player-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 = {
    "teams": ["https://www.sofascore.com/football/team/manchester-city/17"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("incognito_mode/sofascore-team-player-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 '{
  "teams": [
    "https://www.sofascore.com/football/team/manchester-city/17"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call incognito_mode/sofascore-team-player-scraper --silent --output-dataset

```

## MCP server setup

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