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

Scrape Transfermarkt players by name search, player links, club squads or whole leagues: market value (current, previous, highest), contract until, agency, position, foot, height, nationality, club, national team and full transfer history with fees. Error rows are free.

- **URL**: https://apify.com/scrapewise/transfermarkt-scraper.md
- **Developed by:** [Scrapewise Data](https://apify.com/scrapewise) (community)
- **Categories:** Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.19 / 1,000 player delivereds

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: players, market values, contracts, agencies and transfer history

Transfermarkt Scraper collects **football player data from Transfermarkt** without an account or cookies.
Give it player names, player links, club links or whole competitions (Premier League, LaLiga, Serie A, Bundesliga,
Ligue 1, MLS and any other league code) and each player comes back as one clean row: current, previous and highest
market value with dates, contract until, last renewal, agency, position, preferred foot, height, nationalities,
current club and league, shirt number, national team and the full transfer history with fees.

It is used by scouts and analysts to build player databases, by fantasy and betting tools to follow market values,
by journalists to check transfer fees and contracts, and by data teams to feed dashboards and AI models with
squad-level data for a whole league in one run.

### At a glance

| What | Price |
|---|---|
| Player delivered | **US$ 1.40 per 1,000 players** (US$ 0.0014 each) |
| Run start | US$ 0.005 per run |

- A full Premier League (20 clubs, around 500 players) with transfer history = **about US$ 0.71**.
- Error rows and duplicates are never charged. `maxItems` is a hard cap, and a spending limit set on the run stops it
  cleanly with everything delivered so far.
- Transfer history and market value history are included in the same price per player.

### Quick start

Search players by name:

```json
{
  "searchQueries": ["Wirtz", "Yamal"],
  "maxItems": 20
}
```

Every player of a league, club by club:

```json
{
  "competitions": ["GB1", "ES1"],
  "maxItems": 0
}
```

Specific players and squads from links:

```json
{
  "startUrls": [
    "https://www.transfermarkt.com/florian-wirtz/profil/spieler/598577",
    "https://www.transfermarkt.com/fc-barcelona/startseite/verein/131"
  ],
  "includeMarketValueHistory": true
}
```

An empty input runs the example: the first 50 players of the Premier League.

### Input

| Field | Type | What it does |
|---|---|---|
| `searchQueries` | list of text | Player names (or club names with `searchType: "clubs"`), one search each |
| `searchType` | `players` or `clubs` | `players` delivers the matching players; `clubs` delivers the current squad of each matching club |
| `competitions` | list of codes | Transfermarkt competition codes such as `GB1`, `ES1`, `IT1`, `L1`, `FR1`, `NL1`, `PO1`, `BRA1`, `MLS1`. Every club, then every player |
| `startUrls` | list of links | Player profile links, club links (`/verein/<id>`) or competition links (`/wettbewerb/<code>`), any Transfermarkt domain |
| `includeTransfers` | true/false | Adds the full transfer history of each player (default on) |
| `includeMarketValueHistory` | true/false | Adds every market value update with date and club (default off) |
| `maxItems` | number | Hard cap of players for the whole run (default 50, 0 = no cap) |
| `proxyConfiguration` | proxy | Apify datacenter proxy by default |

The competition code is the last part of the competition link on Transfermarkt: for
`https://www.transfermarkt.com/premier-league/startseite/wettbewerb/GB1` the code is `GB1`.

### Output

One row per player. Values are in EUR, as Transfermarkt publishes them.

| Field | Description |
|---|---|
| `playerId`, `name`, `shortName`, `fullName` | Transfermarkt id, display name, short name and full name |
| `url`, `imageUrl` | Profile link and portrait image |
| `age`, `dateOfBirth`, `placeOfBirth`, `countryOfBirth` | Age and birth details (left empty for players under 18) |
| `isUnder18`, `dateOfDeath` | Minor flag and date of death for former players |
| `nationalities` | One or two nationalities |
| `position`, `positionShort`, `positionGroup` | Main position (Centre-Forward, CF) and group |
| `heightM`, `preferredFoot`, `outfitter` | Height in metres, left/right/both, boot supplier |
| `agency`, `agencyVerified`, `agencyUrl` | Player agency as published on Transfermarkt |
| `clubId`, `clubName`, `clubCountry`, `clubLeagueId`, `clubUrl` | Current club and its league code |
| `shirtNumber`, `isCaptain`, `joinedClub`, `clubDebut` | Squad details at the current club |
| `contractUntil`, `lastContractRenewal` | Contract end and last renewal date |
| `formerClubsNote` | Youth clubs noted by Transfermarkt |
| `nationalTeam`, `nationalTeamDebut` | Senior or youth national team and debut |
| `marketValue`, `marketValueCurrency`, `marketValueDate` | Current market value and when it was set |
| `previousMarketValue`, `previousMarketValueDate` | The value before the latest update |
| `highestMarketValue`, `highestMarketValueDate` | Career-high value |
| `marketValueTrend` | `increased`, `decreased` or `unchanged` at the last update |
| `transfers` | List of moves: `season`, `date`, `fromClub`, `toClub` (with ids), `fee`, `feeEur`, `transferType` (transfer, loan, end of loan, free transfer), `marketValueAtTransfer`, `upcoming`, `url` |
| `transferFeeTotal` | Sum of the fees paid for the player, in EUR |
| `marketValueHistory` | With `includeMarketValueHistory`: `date`, `value`, `currency`, `clubId`, `club` |
| `sourceQuery`, `scrapedAt` | Which input produced the row and when |

Real row from a run on 21 Oct 2026 (transfer and value lists shortened):

```json
{
  "type": "player",
  "playerId": "598577",
  "name": "Florian Wirtz",
  "url": "https://www.transfermarkt.com/florian-wirtz/profil/spieler/598577",
  "age": 23,
  "dateOfBirth": "2003-05-03",
  "placeOfBirth": "Pulheim",
  "countryOfBirth": "Germany",
  "nationalities": ["Germany"],
  "position": "Attacking Midfield",
  "positionShort": "AM",
  "heightM": 1.77,
  "preferredFoot": "right",
  "outfitter": "adidas",
  "agency": "Relatives",
  "agencyVerified": false,
  "clubId": "31",
  "clubName": "Liverpool FC",
  "clubCountry": "England",
  "clubLeagueId": "GB1",
  "shirtNumber": 7,
  "joinedClub": "2025-07-01",
  "contractUntil": "2030-06-30",
  "lastContractRenewal": null,
  "nationalTeam": "Germany",
  "nationalTeamDebut": "2021-09-02",
  "marketValue": 85000000,
  "marketValueCurrency": "EUR",
  "marketValueDate": "2026-10-07",
  "previousMarketValue": 100000000,
  "highestMarketValue": 140000000,
  "highestMarketValueDate": "2025-06-05",
  "marketValueTrend": "decreased",
  "transfers": [
    {"season": "20/21", "date": "2020-07-01", "fromClub": "Leverkusen U19", "toClub": "Leverkusen", "fee": null,
     "transferType": null, "upcoming": false}
  ],
  "marketValueHistory": [
    {"date": "2020-07-08", "value": 10000000, "currency": "EUR", "club": "Bayer 04 Leverkusen"}
  ],
  "sourceQuery": "Florian Wirtz",
  "scrapedAt": "2026-10-21T20:19:04Z"
}
```

A paid transfer looks like this (from the same row):

```json
{"season": "25/26", "date": "2025-07-01", "fromClub": "Leverkusen", "toClub": "Liverpool", "fee": "€125.00m",
 "feeEur": 125000000, "transferType": "transfer", "marketValueAtTransfer": 140000000}
```

### Players under 18

Transfermarkt lists academy players too. For anyone under 18 the row keeps the football data (club, position,
market value, transfers) but leaves `age`, `dateOfBirth`, `fullName`, `placeOfBirth` and `countryOfBirth` empty and
sets `isUnder18: true`.

### Errors you may see

Every error is a row with `errorCode` and is never charged:

| errorCode | Meaning |
|---|---|
| `INVALID_URL` | Not a Transfermarkt player, club or competition link |
| `INVALID_INPUT` | A field has a wrong value (for example an unknown `searchType`) |
| `NOT_FOUND` | The player, club or competition code does not exist on Transfermarkt |
| `NO_RESULTS` | The name search found nothing; check the spelling or try the last name only |
| `BLOCKED` | Transfermarkt refused every attempt for this input; run it again in a few minutes |
| `UNEXPECTED` | Something else stopped the run; the message says what |

### Integrations, scheduling and AI agents

- **Schedule** a league weekly in [Apify schedules](https://docs.apify.com/platform/schedules) to follow market
  value updates, contract renewals and new transfers during the window.
- **Export** as JSON, CSV, Excel or XML, or read the dataset from the [Apify API](https://docs.apify.com/api/v2).
- **Connect** it to Make, n8n, Zapier or Google Sheets through
  [Apify integrations](https://docs.apify.com/platform/integrations).
- **AI agents:** call it as a tool through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp)
  in Claude, ChatGPT, Cursor or any MCP client, for example "list every LaLiga player whose contract ends in 2027
  with a market value above 20 million".

### FAQ

**Do I need a Transfermarkt account?** No. Everything comes from public pages.

**How long does a full league take?** A 20-club league with transfer history is around 500 players and takes a few
minutes. Turn off `includeTransfers` for the fastest run.

**Which competitions work?** Any league or cup that has a `/wettbewerb/<code>` page on Transfermarkt. The squad is
the club's current squad.

**Can I get former players?** Yes, by name search or profile link. Retired players show `Retired` as the club.

**Why is `agency` empty for some players?** Transfermarkt has no agency on file for them, or lists the player's
relative instead of a company; the row only carries what the site publishes as the agency.

**Is it legal?** The Actor reads public pages only and returns no emails or phone numbers. You are responsible for
how you use the data under the laws that apply to you.

**Something broke or a field is missing?** Open an issue on the Actor page; issues are answered within a day.

### Limitations

- Squads are the current ones; past seasons are not supported yet.
- Match statistics (goals, minutes, cards) are not collected.
- Market values are in EUR only, as Transfermarkt sets them.
- Manager, referee and agent search results are not collected.

### Changelog

- **16 Oct 2026:** first version: name search, player, club and competition links, competition codes, transfer
  history and market value history.

# Actor input Schema

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

Player names (or club names when Search type is Clubs), one per line, for example Wirtz or Bellingham. Each line is one Transfermarkt search. Leave empty when you use links or competitions.

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

What the name search looks for. Players: delivers the matching players. Clubs: finds the matching clubs and delivers every player of their current squads.

## `competitions` (type: `array`):

Transfermarkt competition codes, one per line, for example GB1 (Premier League), ES1 (LaLiga), IT1 (Serie A), L1 (Bundesliga), FR1 (Ligue 1), BRA1 (Serie A Brazil), MLS1. Delivers every player of every club in the competition, club by club.

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

Player profile links (https://www.transfermarkt.com/florian-wirtz/profil/spieler/598577), club links (any page with /verein/<id>, delivers the current squad) or competition links (any page with /wettbewerb/<code>). Any Transfermarkt domain works (.com, .de, .co.uk...).

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

On: adds each player's full transfer history (season, date, from, to, fee, market value at the time, loan/free flags) and the total fees. One extra request per player.

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

On: adds every market value update of the player (date, value in EUR, club at the time). One extra request per player.

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

Hard cap for the whole run, across all searches, links and competitions. 0 = no cap.

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

Apify datacenter proxy by default.

## Actor input object example

```json
{
  "searchQueries": [
    "Wirtz",
    "Yamal"
  ],
  "searchType": "players",
  "includeTransfers": true,
  "includeMarketValueHistory": false,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `resultsCsv` (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 = {
    "searchQueries": [
        "Wirtz",
        "Yamal"
    ],
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapewise/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 = {
    "searchQueries": [
        "Wirtz",
        "Yamal",
    ],
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapewise/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 '{
  "searchQueries": [
    "Wirtz",
    "Yamal"
  ],
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call scrapewise/transfermarkt-scraper --silent --output-dataset

```

## MCP server setup

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