# Transfermarkt Scraper - Players, Clubs & Market Values (`solidcode/transfermarkt-scraper`) Actor

\[💰 $1.00 / 1K] Scrape football/soccer data from Transfermarkt: player profiles with market value history, club squads, competition tables, and transfer records. Paste any Transfermarkt URL — the scraper detects the page type automatically. Up to 20+ languages supported.

- **URL**: https://apify.com/solidcode/transfermarkt-scraper.md
- **Developed by:** [SolidCode](https://apify.com/solidcode) (community)
- **Categories:** Developer tools, Automation, Other
- **Features:** Limited permissions, Multi-tenant, Server interface
- **Stats:** 104 total users, 19 monthly users, 100.0% runs succeeded, 1 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

## Transfermarkt Scraper

Pull football data from Transfermarkt at scale — player profiles with market values in EUR, club squads with total and average squad value, competition tables with tier and total league valuation, and paginated transfer-record lists with fee, season, and from/to clubs. Built for scouts, football analysts, sports-media editors, and fantasy-league operators who need a clean structured Transfermarkt dataset across players, clubs, competitions, and transfers in a single run — without stitching four separate scrapers together.

### Why This Scraper?

- **Four record types in one actor — player, club, competition, and transfer** — every competing Transfermarkt scraper on Apify is player-only. Paste a club URL and get the club row; paste a competition URL and get the competition plus its league-table clubs; paste `/statistik/topabloesen` and get paginated transfer rows. The output dataset auto-routes by `recordType`.
- **Auto-detects the page type from the URL** — mix player, club, competition, and transfer-record URLs in the same `startUrls` array. The scraper inspects the path (`/profil/spieler/`, `/startseite/verein/`, `/startseite/wettbewerb/`, `/transfers/transferrekorde/`) and runs the right parser for each.
- **13 regional Transfermarkt mirrors** — pick from `.com`, `.us`, `.de`, `.world`, `.es`, `.fr`, `.it`, `.nl`, `.com.br`, `.pl`, `.com.tr`, `.jp`, and `.co.uk`. Player and club IDs are identical across mirrors, so the scraper rewrites your URLs to the mirror you select — position labels, country names, and competition names come back in the language of that region.
- **Full market-value history per player** — every market-value snapshot Transfermarkt has ever published for a player, with season ID, the club they were at, their age, the compact label ("€180.00m"), and the EUR integer ready for charting.
- **Career transfer history with fee, market value, and direction** — for every transfer in a player's career: date, from-club, to-club, fee in EUR, market value at the time, transfer type (loan / permanent / free), and the "fee description" Transfermarkt prints next to the fee.
- **Achievements (trophies) and injury absences as opt-in enrichments** — toggle `includeAchievements` to get the player's full trophy cabinet (league titles, cups, individual awards) and `includeInjuries` to get every injury absence with type, start/end date, duration in days, and games missed.
- **Nested expansion of leagues into squads** — flip `includeCompetitionClubs` and `includeClubSquad` to walk a whole league: one competition URL becomes 18–20 club rows, and each club row expands into 25–35 player profile rows. Fetch the entire Premier League's player base from a single input URL.
- **Robust EUR fee normalizer** — Transfermarkt prints values in five different formats: `€222.00m`, `€1.50bn`, `€500Th.`, `222,00 Mio. €`, `ablösefrei`. We normalize every fee into a plain integer EUR field (`feeEur`, `marketValueEur`) alongside the compact display string, so downstream pipelines can sort, filter, and aggregate without parsing currency text.
- **Real-time API for live lookups** — call one URL and get a player, club, or competition back in the response within seconds, with no run to start and no dataset to download. Built for apps, chatbots, and dashboards that need Transfermarkt data on demand.

### Use Cases

**Player Scouting & Recruitment Analytics**

- Pull every player on a competitor club's senior squad with current market value, position, contract end date, and nationality
- Build short-lists of out-of-contract players by filtering on `currentClubContractUntil`
- Compare market-value trajectories across an entire age cohort using the per-player MV history

**Transfer Market Research**

- Track all-time top transfer fees in EUR by paginating `/statistik/topabloesen`
- Monitor inbound and outbound moves for a specific club by following its transfers page
- Quantify how often loans convert to permanent deals across a league season

**Fantasy League & Game Analytics**

- Seed a fantasy database with the full Premier League player set in one run (competition URL + both expansion toggles)
- Refresh weekly market values and injury absences for every fantasy-eligible player
- Detect emerging-talent price spikes via the market-value history delta

**Sports Media & Editorial Pipelines**

- Generate match-preview cards with both clubs' squad sizes, average ages, and total values
- Produce localized transfer-window articles by switching `language` to Italian, German, or Brazilian Portuguese
- Build a "biggest signings of the season" leaderboard from the transfer-records list

**Football Data Science & Modeling**

- Train transfer-fee prediction models against the full historical transfer history
- Study career mobility patterns using `transferHistory` (date, age, fee, market value at the time)
- Benchmark league strength via aggregated `totalMarketValueEur` across competitions

**Agency & Player-Representation Workflows**

- Watch contract expiries across 50+ leagues by pulling competition pages quarterly
- Track injury history (type, days out, games missed) for risk profiling
- Pair `formerClubsNote` with `transferHistory` to reconstruct a complete player career timeline

### Getting Started

#### Single Player Profile

The simplest possible run — one player, full enrichment defaults (market-value history and transfer history on):

```json
{
    "startUrls": [
        "https://www.transfermarkt.com/erling-haaland/profil/spieler/418560"
    ]
}
```

#### Multiple Players With All Enrichments

Pull four players with every optional sub-resource turned on — achievements and injuries included:

```json
{
    "startUrls": [
        "https://www.transfermarkt.com/erling-haaland/profil/spieler/418560",
        "https://www.transfermarkt.com/kylian-mbappe/profil/spieler/342229",
        "https://www.transfermarkt.com/jude-bellingham/profil/spieler/581678",
        "https://www.transfermarkt.com/vinicius-junior/profil/spieler/371998"
    ],
    "includeMarketValueHistory": true,
    "includeTransferHistory": true,
    "includeAchievements": true,
    "includeInjuries": true
}
```

#### Whole Premier League — Competition + Clubs

One competition URL expanded into every club on the league table (20 rows total):

```json
{
    "startUrls": [
        "https://www.transfermarkt.com/premier-league/startseite/wettbewerb/GB1"
    ],
    "includeCompetitionClubs": true,
    "maxResults": 500
}
```

#### Full League Walk — Competition → Clubs → Squads

One competition URL fans out to ~20 clubs and each club's full squad (~500–700 player rows):

```json
{
    "startUrls": [
        "https://www.transfermarkt.com/laliga/startseite/wettbewerb/ES1"
    ],
    "includeCompetitionClubs": true,
    "includeClubSquad": true,
    "language": "es",
    "maxResults": 600
}
```

#### Top All-Time Transfer Fees

Paginate the global transfer-records page with a row cap:

```json
{
    "startUrls": [
        "https://www.transfermarkt.com/statistik/topabloesen"
    ],
    "recordType": "transfers",
    "maxResults": 250
}
```

### Input Reference

#### What to Scrape

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `startUrls` | string\[] | `[Haaland profile, Manchester City club]` | One or more Transfermarkt URLs. Supports player profiles (`/profil/spieler/<id>`), club squad pages (`/startseite/verein/<id>`), competitions (`/startseite/wettbewerb/<code>`), and transfer-records pages (`/transfers/transferrekorde/...`, `/statistik/topabloesen`). The scraper detects the page type automatically. |
| `recordType` | select | `Auto-detect from URL (recommended)` | Optional filter: `Auto-detect from URL (recommended)`, `Players only`, `Clubs only`, `Competitions only`, or `Transfer records only`. Use auto-detect unless you want to ignore URLs of other types in the input. |

#### How Much to Scrape

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `maxResults` | integer | `1000` | Maximum number of result rows to return across the whole run. For club URLs this caps the number of player rows; for competition URLs it caps club rows; for transfer-list URLs it caps transfer rows. Set to `0` for unlimited. |
| `language` | select | `English (.com)` | Which Transfermarkt regional mirror to use. Options: `English (.com)`, `English (US)`, `German (.de)`, `English (.world)`, `Spanish (.es)`, `French (.fr)`, `Italian (.it)`, `Dutch (.nl)`, `Portuguese - Brazil (.com.br)`, `Polish (.pl)`, `Turkish (.com.tr)`, `Japanese (.jp)`, `English (.co.uk)`. Affects language of position labels, country names, and competition names. |

#### Player Enrichments

Optional extras fetched only for player URLs. Each toggle adds one request per player.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `includeMarketValueHistory` | boolean | `true` | Full historical market-value timeline per player (date, value, age, club at the time). |
| `includeTransferHistory` | boolean | `true` | Every transfer in the player's career with date, from-club, to-club, fee, and market value at the time. |
| `includeAchievements` | boolean | `false` | Trophies and titles (league titles, cups, individual awards). |
| `includeInjuries` | boolean | `false` | Injury history table (injury type, dates, games missed). |

#### Nested Expansion

Turning these on multiplies result count — a typical first-division squad has 25–35 players and a top-flight league has 18–20 clubs.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `includeClubSquad` | boolean | `false` | For club URLs, also fetch a full profile for every player on the squad — not just the club summary row. |
| `includeCompetitionClubs` | boolean | `false` | For competition URLs, also fetch a full club page for every club in the competition. Combine with `includeClubSquad` to walk an entire league down to player level. |

### Output

Every row carries a `recordType` field — `player`, `club`, `competition`, or `transfer` — so you can split the dataset cleanly downstream. The Apify dataset viewer ships with one tab per record type out of the box.

#### Player (`recordType: "player"`)

```json
{
    "recordType": "player",
    "sourceUrl": "https://www.transfermarkt.com/erling-haaland/profil/spieler/418560",
    "id": 418560,
    "name": "Erling Haaland",
    "shortName": "E. Haaland",
    "displayName": "Erling Haaland",
    "portraitUrl": "https://img.a.transfermarkt.technology/portrait/header/418560-1709108116.jpg",
    "age": 25,
    "dateOfBirth": "2000-07-21",
    "placeOfBirth": "Leeds",
    "countryOfBirthId": 189,
    "nationalityId": 125,
    "secondNationalityId": null,
    "heightMeters": 1.95,
    "preferredFootId": 3,
    "positionGroup": 1,
    "positionGroupName": "Attack",
    "positionId": 7,
    "contractUntil": "2034-06-30",
    "marketValue": { "value": 200000000, "currency": "EUR", "compact": "€200.00m" },
    "currentClubId": 281,
    "currentClubJoinedDate": "2022-07-01",
    "isCaptain": false,
    "nationalTeamCountryId": 125
}
```

##### Player — Core Fields

| Field | Type | Description |
|-------|------|-------------|
| `recordType` | string | Always `"player"` |
| `sourceUrl` | string | The Transfermarkt URL this row came from |
| `id` | number | Transfermarkt player ID |
| `name` | string | Display name |
| `shortName` | string | Short display name |
| `displayName` | string | Long display name |
| `artistName` | string | Stage name (used for Brazilian players like "Pelé") |
| `portraitUrl` | string | Headshot image URL |
| `relativeUrl` | string | Path on transfermarkt.com |

##### Player — Biography

| Field | Type | Description |
|-------|------|-------------|
| `age` | number | Current age in years |
| `dateOfBirth` | string | ISO date of birth |
| `dateOfDeath` | string | ISO date of death (if applicable) |
| `placeOfBirth` | string | City of birth |
| `countryOfBirthId` | number | Transfermarkt country ID |
| `gender` | string | Gender |
| `passportName` | string | Name as on passport |
| `nationalityId` | number | Primary nationality (Transfermarkt country ID) |
| `secondNationalityId` | number | Second nationality if dual |

##### Player — Physical & Position

| Field | Type | Description |
|-------|------|-------------|
| `heightMeters` | number | Height in metres |
| `preferredFootId` | number | Right / left / both |
| `positionGroup` | number | Group ID (Attack, Midfield, Defence, Goalkeeper) |
| `positionGroupName` | string | Group label |
| `positionId` | number | Specific position (e.g. Centre-Forward) |
| `firstSidePositionId` | number | Secondary position |
| `secondSidePositionId` | number | Tertiary position |
| `outfitterId` | number | Boot sponsor |

##### Player — Club & Contract

| Field | Type | Description |
|-------|------|-------------|
| `currentClubId` | number | Current club's Transfermarkt ID |
| `currentClubJoinedDate` | string | When they joined the current club |
| `currentClubContractUntil` | string | Contract expiry at the current club |
| `contractUntil` | string | Overall contract end date |
| `lastContractRenewal` | string | Date of the most recent renewal |
| `isCaptain` | boolean | Captain flag |
| `nationalTeamCountryId` | number | National team country |
| `formerClubsNote` | string | Free-text former-clubs note |
| `consultantAgencyId` | number | Agency representing the player |

##### Player — Market Value

| Field | Type | Description |
|-------|------|-------------|
| `marketValue` | object | `{ value, currency, compact, determined }` — current market value in EUR |
| `previousMarketValue` | object | Previous valuation snapshot |
| `marketValueHistory` | object\[] | Full history `[{ seasonId, clubId, age, value, currency, compact, determined }]` — only when `includeMarketValueHistory` is on |
| `currentMarketValueSnapshot` | object | Latest snapshot from the history endpoint |

##### Player — Transfer History, Achievements, Injuries

| Field | Type | Description |
|-------|------|-------------|
| `transferHistory` | object\[] | All career transfers `[{ id, date, seasonId, fromClubId, toClubId, fromCompetitionId, toCompetitionId, fee, marketValue, age, type, typeName, feeDescription, contractUntilDate }]` |
| `mostRecentTransfer` | object | Single most recent transfer entry |
| `achievements` | object\[] | Trophies `[{ title, group }]` — only when `includeAchievements` is on |
| `absences` | object\[] | All absences (injuries + national-team call-ups) |
| `injuries` | object\[] | Absences with the national-team call-ups filtered out `[{ absenceId, name, competitionId, seasonId, start, end, missedGamesCount, durationDays }]` |

#### Club (`recordType: "club"`)

```json
{
    "recordType": "club",
    "sourceUrl": "https://www.transfermarkt.com/manchester-city/startseite/verein/281",
    "id": 281,
    "name": "Manchester City",
    "shortName": "Man City",
    "abbreviation": "MCI",
    "isNationalTeam": false,
    "countryId": 189,
    "primaryCompetitionId": "GB1",
    "crestUrl": "https://tmssl.akamaized.net/images/wappen/head/281.png",
    "addressStreet": "Etihad Campus, Etihad Stadium",
    "addressCity": "Manchester",
    "addressPostcode": "M11 3FF",
    "addressCountryId": 189,
    "squadSize": 26,
    "squadAverageAge": 26.4,
    "squadMarketValueTotal": { "value": 1110000000, "currency": "EUR", "compact": "€1.11bn" },
    "squadMarketValueAverage": { "value": 42700000, "currency": "EUR", "compact": "€42.70m" },
    "squadAcquisitionValue": { "value": 1230000000, "currency": "EUR", "compact": "€1.23bn" }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `recordType` | string | Always `"club"` |
| `sourceUrl` | string | The Transfermarkt URL this row came from |
| `id` | number | Transfermarkt club ID |
| `name` | string | Full club name |
| `shortName` | string | Short club name |
| `abbreviation` | string | Three-letter abbreviation |
| `identifier` | string | URL slug |
| `isNationalTeam` | boolean | True for national teams |
| `isSpecialClub` | boolean | Flag for academy / B teams |
| `crestUrl` | string | Crest image URL |
| `relativeUrl` | string | Path on transfermarkt.com |
| `countryId` | number | Transfermarkt country ID |
| `primaryCompetitionId` | string | Primary league code (e.g. `GB1`) |
| `addressStreet` | string | Street address |
| `addressCity` | string | City |
| `addressPostcode` | string | Postcode |
| `addressCountryId` | number | Country ID of the registered address |
| `squadSize` | number | Number of senior squad players |
| `squadAverageAge` | number | Mean age of the squad |
| `squadMarketValueTotal` | object | Total squad market value `{ value, currency, compact }` |
| `squadMarketValueAverage` | object | Average per-player market value |
| `squadAcquisitionValue` | object | Sum of fees paid to assemble the squad |

#### Competition (`recordType: "competition"`)

```json
{
    "recordType": "competition",
    "sourceUrl": "https://www.transfermarkt.com/premier-league/startseite/wettbewerb/GB1",
    "id": "GB1",
    "name": "Premier League",
    "shortName": "Premier League",
    "identifier": "premier-league",
    "logoUrl": "https://tmssl.akamaized.net/images/logo/header/gb1.png",
    "typeId": 1,
    "currentSeasonId": 2025,
    "currentSeason": "25/26",
    "countryId": 189,
    "confederationId": 6,
    "tier": 1,
    "gameDayCount": 38,
    "isOngoing": true,
    "isTournament": false,
    "totalMarketValue": { "value": 11400000000, "currency": "EUR", "compact": "€11.40bn" }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `recordType` | string | Always `"competition"` |
| `sourceUrl` | string | The Transfermarkt URL this row came from |
| `id` | string | Competition code (e.g. `GB1`, `ES1`, `IT1`, `L1`, `FR1`) |
| `name` | string | Competition full name |
| `shortName` | string | Short name |
| `identifier` | string | URL slug |
| `relativeUrl` | string | Path on transfermarkt.com |
| `logoUrl` | string | Competition logo URL |
| `typeId` | number | League / cup / international type ID |
| `currentSeasonId` | number | Numeric season ID (e.g. `2025`) |
| `currentSeason` | string | Season label (e.g. `25/26`) |
| `countryId` | number | Host-country Transfermarkt ID |
| `confederationId` | number | UEFA / CONMEBOL / etc. ID |
| `associationId` | number | Football association ID |
| `tier` | number | League tier (1 = top flight) |
| `gameDayCount` | number | Match days in a season |
| `isOngoing` | boolean | True if the season is in progress |
| `isTournament` | boolean | True for knockout tournaments |
| `totalMarketValue` | object | Aggregate market value of every club in the league `{ value, currency, compact }` |

#### Transfer (`recordType: "transfer"`)

```json
{
    "recordType": "transfer",
    "rank": "1",
    "playerId": "342229",
    "playerName": "Kylian Mbappé",
    "position": "Centre-Forward",
    "season": "17/18",
    "joinedClubId": "583",
    "joinedClubName": "Paris Saint-Germain",
    "joinedCompetitionCode": "FR1",
    "joinedCompetitionName": "Ligue 1",
    "feeText": "€180.00m",
    "feeEur": 180000000
}
```

| Field | Type | Description |
|-------|------|-------------|
| `recordType` | string | Always `"transfer"` |
| `rank` | string | Position in the transfer-records list |
| `playerId` | string | Transfermarkt player ID |
| `playerName` | string | Player name as displayed |
| `position` | string | Playing position |
| `season` | string | Season label (e.g. `17/18`) |
| `joinedClubId` | string | Destination club's Transfermarkt ID |
| `joinedClubName` | string | Destination club name |
| `joinedCompetitionCode` | string | Destination league code (e.g. `FR1`) |
| `joinedCompetitionName` | string | Destination league name |
| `feeText` | string | Raw Transfermarkt fee string (e.g. `€180.00m`, `ablösefrei`) |
| `feeEur` | number | Fee normalized to integer EUR (`0` for free transfers, `null` when unknown) |

### Tips for Best Results

- **Mix record types in one run.** `startUrls` can hold player, club, competition, and transfer-list URLs at the same time — the auto-detector routes each one to the right parser. Pay for one start, get four datasets.
- **Set `maxResults` before flipping nested toggles.** A single competition URL with `includeCompetitionClubs` and `includeClubSquad` both on can produce 500–700 player rows. Cap your spend by setting `maxResults` to the figure you actually need.
- **Use competition codes you already know.** Premier League is `GB1`, La Liga is `ES1`, Serie A is `IT1`, Bundesliga is `L1`, Ligue 1 is `FR1`. Paste the URL ending in those codes to skip search.
- **Switch `language` to localize labels.** Picking `German (.de)` returns position labels like "Mittelstürmer" and country names in German — handy for regional sports-media pipelines. Player and club IDs stay identical across all 13 mirrors.
- **Combine `includeCompetitionClubs` with `includeClubSquad`** to fetch an entire top-flight league down to every player in one input URL. The output dataset then carries all three record types and joins cleanly on `currentClubId` and `primaryCompetitionId`.
- **Leave achievements and injuries off unless you need them.** Each adds one extra request per player. For a squad-wide refresh of market values, the defaults (MV history + transfer history) already give you the most-requested fields.
- **For transfer-records lists, prefer competition-scoped URLs.** A URL like `/transfers/transferrekorde/statistik/topabloesen?wettbewerb_id=GB1` returns Premier-League-only transfers — much more targeted than the global all-time list.

### Pricing

**From $1.00 per 1,000 results** — billed per row in the output dataset, with lower prices on Apify's loyalty discount tiers.

| Results | No discount | Bronze | Silver | Gold |
|---------|-------------|--------|--------|------|
| 100 | $0.120 | $0.113 | $0.107 | $0.100 |
| 1,000 | $1.20 | $1.13 | $1.065 | $1.00 |
| 10,000 | $12.00 | $11.30 | $10.65 | $10.00 |
| 100,000 | $120.00 | $113.00 | $106.50 | $100.00 |

Tiered prices take effect on October 25, 2026 — until then every plan pays the Gold rate of $1.00 per 1,000 results. A "result" is any row in the output dataset — a `player`, `club`, `competition`, or `transfer`. **No compute charges — you only pay per result returned.**

Results returned through the [Real-time API](#real-time-api) are free during early access. After that, they will cost the same per-result price as the table above.

### Real-time API

Need Transfermarkt data inside your own app, bot, or dashboard? Call the scraper like any web API and get the results back in the response — no run to start, no dataset to fetch afterwards.

```
GET https://solidcode--transfermarkt-scraper.apify.actor/scrape?url=https://www.transfermarkt.com/erling-haaland/profil/spieler/418560
Authorization: Bearer <YOUR_APIFY_API_TOKEN>
```

The response is JSON:

```json
{
  "results": [{ "recordType": "player", "name": "Erling Haaland", "marketValueEur": 220000000, "...": "..." }],
  "count": 1,
  "partial": false,
  "message": "Done! 1 results from 1 items.",
  "skipped": []
}
```

- **Same inputs as a normal run.** Repeat `url=` to look up several pages at once, and add `maxResults`, `language`, `recordType`, or any of the `include…` options as query parameters (`includeInjuries=true`). You can also `POST` a JSON body shaped exactly like the Actor input.
- **Same rows as a normal run.** Every row in `results` matches the dataset format documented above.
- **Sized for live lookups.** Each call returns up to 100 results by default and at most 500. For whole leagues or big batches, start a normal run instead.
- **Try it without writing code.** The **Endpoints** tab on this page lets you send a request from your browser and see the response.

Your API token is on the **Settings → API & Integrations** page of your Apify account.

### Integrations

Export data in JSON, CSV, Excel, XML, or RSS. Connect to 1,500+ apps via:

- **Zapier** / **Make** / **n8n** — Workflow automation
- **Google Sheets** — Direct spreadsheet export
- **Slack** / **Email** — Notifications on new results
- **Webhooks** — Trigger custom APIs on run completion
- **Apify API** — Full programmatic access
- **Real-time API** — Live lookups straight from your app (see above)

### Legal & Ethical Use

This actor is designed for legitimate football research, scouting, sports analytics, and editorial use against publicly available Transfermarkt pages. Users are responsible for complying with applicable laws and Transfermarkt's terms of service, including attributing Transfermarkt as the data source where appropriate and making reasonable-rate requests. Do not use extracted data for spam, harassment, or any illegal purpose.

# Actor input Schema

## `startUrls` (type: `array`):

Paste one or more Transfermarkt URLs. Supported page types: player profile (`/.../profil/spieler/<id>`), club squad (`/.../startseite/verein/<id>`), competition (`/.../startseite/wettbewerb/<code>`), and transfer records (`/transfers/transferrekorde/...`, `/statistik/topabloesen`). The scraper detects the page type automatically and routes to the right parser.

## `recordType` (type: `string`):

Override automatic page-type detection. Use `Auto-detect` unless you specifically want to ignore URLs of other types in the input.

## `maxResults` (type: `integer`):

Maximum number of result rows to return across the whole run (shared across every input URL, not per URL). For club URLs this caps the number of player rows; for competition URLs it caps club rows; for transfer-list URLs it caps transfer rows. Set to 0 for unlimited. Note: Transfermarkt's all-time transfer-records list contains roughly 100,000 rows — when set to unlimited on transfer URLs, pagination stops there and the run completes early.

## `language` (type: `string`):

Which Transfermarkt regional site to scrape from. Affects language of position labels, country names, and competition names. `English (.com)` is the global default.

## `includeMarketValueHistory` (type: `boolean`):

For player pages, include the full historical market value timeline (date, value, age, club at the time). Adds one extra request per player.

## `includeTransferHistory` (type: `boolean`):

For player pages, include every transfer in the player's career with date, from-club, to-club, fee, and market value at the time.

## `includeAchievements` (type: `boolean`):

For player pages, include trophies and titles (league titles, cups, individual awards). Adds one extra request per player.

## `includeInjuries` (type: `boolean`):

For player pages, include the injury history table (injury type, dates, games missed). Adds one extra request per player.

## `includeClubSquad` (type: `boolean`):

For club URLs, also fetch a full profile for every player on the squad (not just the squad-row summary). WARNING: a typical first-division squad has 25-35 players — this multiplies your result count and run cost roughly 25-35x per club URL. Leave off if you only need the squad summary.

## `includeCompetitionClubs` (type: `boolean`):

For competition URLs, also fetch a full club page for every club in the competition (not just the league-table summary). WARNING: a typical top-flight league has 18-20 clubs — this multiplies your result count by roughly 18-20x per competition URL. If you also enable 'Expand Club Squad to Player Profiles', the multipliers stack (18 clubs x 25-35 players = 450+ rows per competition). Leave off if you only need the competition summary.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.transfermarkt.com/erling-haaland/profil/spieler/418560",
    "https://www.transfermarkt.com/manchester-city/startseite/verein/281"
  ],
  "recordType": "auto",
  "maxResults": 1000,
  "language": "com",
  "includeMarketValueHistory": true,
  "includeTransferHistory": true,
  "includeAchievements": false,
  "includeInjuries": false,
  "includeClubSquad": false,
  "includeCompetitionClubs": false
}
```

# Actor output Schema

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

Player profiles with market value, position, nationality, club, and contract.

## `clubs` (type: `string`):

Club records with squad size, average age, total market value, and league.

## `competitions` (type: `string`):

League / cup competition records with country, tier, and aggregated market value.

## `transfers` (type: `string`):

Transfer records — player moves with from/to clubs, fee, and date.

# 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 = {
    "startUrls": [
        "https://www.transfermarkt.com/erling-haaland/profil/spieler/418560",
        "https://www.transfermarkt.com/manchester-city/startseite/verein/281"
    ],
    "recordType": "auto",
    "maxResults": 1000,
    "language": "com",
    "includeMarketValueHistory": true,
    "includeTransferHistory": true,
    "includeAchievements": false,
    "includeInjuries": false,
    "includeClubSquad": false,
    "includeCompetitionClubs": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidcode/transfermarkt-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 = {
    "startUrls": [
        "https://www.transfermarkt.com/erling-haaland/profil/spieler/418560",
        "https://www.transfermarkt.com/manchester-city/startseite/verein/281",
    ],
    "recordType": "auto",
    "maxResults": 1000,
    "language": "com",
    "includeMarketValueHistory": True,
    "includeTransferHistory": True,
    "includeAchievements": False,
    "includeInjuries": False,
    "includeClubSquad": False,
    "includeCompetitionClubs": False,
}

# Run the Actor and wait for it to finish
run = client.actor("solidcode/transfermarkt-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 '{
  "startUrls": [
    "https://www.transfermarkt.com/erling-haaland/profil/spieler/418560",
    "https://www.transfermarkt.com/manchester-city/startseite/verein/281"
  ],
  "recordType": "auto",
  "maxResults": 1000,
  "language": "com",
  "includeMarketValueHistory": true,
  "includeTransferHistory": true,
  "includeAchievements": false,
  "includeInjuries": false,
  "includeClubSquad": false,
  "includeCompetitionClubs": false
}' |
apify call solidcode/transfermarkt-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,solidcode/transfermarkt-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/p24cwRyUdWtBqLDxT/builds/YNBD75oemuJX7NXdM/openapi.json
