Scrape Transfermarkt (transfermarkt.com) football data into clean JSON or CSV. It covers player profiles (market value, contract, agent, citizenship, positions, foot, height, international caps) and the full market value history and transfer history with fees. It also reads a player's game-by-game log (minutes, goals, assists, cards, substitutions, shots, passes) with per-season totals, club squads, league clubs with squad market values and league tables. Add the season's transfers of a whole league with fees, loan flags and each club's net spend, the most valuable players (with position, age, nationality and continent filters) and search to find any id. It's built for football analytics, scouting databases, fantasy football tools, transfer-market research and data journalism, and ships as a CLI and a Python library.
Powered by ScrapeUnblocker. Every page is fetched through the ScrapeUnblocker getPageSource API, so you don't have to handle blocks, rate limits or proxies yourself.
transfermarkt-scraper search haaland -f table
transfermarkt-scraper player 418560 --history --transfers
transfermarkt-scraper games 418560 --season 2025 --summary competition -f table
transfermarkt-scraper squad 281 -f csv -o man-city.csv
transfermarkt-scraper table ES1 --season 2024 -f table
transfermarkt-scraper market-values --age-group u21 --position-group forwards -f table
transfermarkt-scraper transfers GB1 --season 2025 --balances -f table- Player profiles (
player): name, full name, shirt number, date of birth and age, place and country of birth, every citizenship, height in cm, foot, main and other positions, current club and league, joined date, contract end, contract option and last extension, loan details (parent club and its contract), agent, outfitter, current market value and its update date, national team with caps and goals, honours (titles with counts), social media links and portrait URL. Retired players get their last club and retirement date. - Market value history (
market-value-history, orplayer --history): every value Transfermarkt has published, with date, club and age. - Transfer history (
transfer-history, orplayer --transfers): date, season, from and to club (with ids), market value at the time and the fee as a number plus its kind:fee,free,loan,loan_fee,end_of_loanorunknown. - Game log (
games): every game of a player's career, club and national team, newest first. Each game has the date and kickoff time, competition, matchday, home/away, opponent, score and result (W/D/L, extra time or penalty shoot-out), whether the player started, was a sub, was unused, injured or not in the squad, minutes, goals, assists, penalty and own goals, yellow and red card minutes, substitution minutes, captaincy, shirt number, position played, and where Transfermarkt has them shots, shots on target, passes, tackles, fouls and offsides. Filter by season and competition, or--summary competition|seasonfor appearances, starts, minutes, goals, assists, cards and minutes per goal per season. - Squads (
squad): shirt number, position, date of birth, age, nationalities, height, foot, joined date, previous club and the fee paid, contract end, market value, captain, injury status and recent loan returns, for the current or any past season (past seasons show each player's current club). - Leagues (
competition): league level, reigning and record champion, UEFA coefficient, number of teams and players, foreigners, average age, average and total market value, most valuable player, and every club with squad size, average age, foreigners and squad market value. Cups such as the Champions League list their participating clubs. - League tables (
table): position, played, won, drawn, lost, goals for and against, goal difference, points and last week's position, for any season. Leagues split into groups (MLS conferences) get a group name. - Most valuable players (
market-values): worldwide with Transfermarkt's filters (position or position group, age group, year of birth, nationality, continent), or within one league, 25 per page up to 500 players. - League transfers (
transfers): every arrival and departure of every club in a league season: player, age, nationalities, position, market value, the other club and its country, fee and fee kind, transfer id. Filter by window (summer/winter), loans, internal moves, club and direction.--balancesgives each club's spending, income and net balance. - Search (
search): players, clubs or competitions by name, with their ids. - Robust: retries with backoff on timeouts, transient errors and error pages, and raises
NotFoundErrorfor unknown ids. - Output as JSON, JSON Lines, CSV or a terminal table; typed dataclasses in Python. All amounts are integers in EUR.
Transfermarkt pages are server-rendered HTML tables. The scraper reads them with BeautifulSoup and finds each column by its header label rather than by position. This matters because the same table has different columns on different pages: a current squad has a contract column, while a past season has a "current club" column instead. Amounts such as €1.50bn, dates such as 25/02/1999 (27) and heights such as 1,96m are turned into numbers and ISO dates.
Three datasets come from the JSON endpoints Transfermarkt's own pages load:
- the market value chart (
/ceapi/marketValueDevelopment/graph/<id>) - the transfer history (
/ceapi/transferHistory/list/<id>) - the game log behind the stats pages (
/ceapi/performance-game/<id>)
The game log only holds club and competition ids, so their names are looked up in batches of 100 and cached.
Each page or JSON response is one request to ScrapeUnblocker's getPageSource endpoint; no browser rendering is needed.
git clone https://github.com/ScrapeUnblocker/transfermarkt-scraper.git
cd transfermarkt-scraper
python -m pip install .Get an API key at scrapeunblocker.com and expose it as SCRAPEUNBLOCKER_KEY (see .env.example):
export SCRAPEUNBLOCKER_KEY=your_key_hereRequires Python 3.9+. Runtime dependencies are the official scrapeunblocker SDK and beautifulsoup4.
transfermarkt-scraper [--proxy-country CC] [--retries 3] [-v] COMMAND ...
search QUERY [--type players|clubs|competitions] [--pages N] [--max N]
player PLAYER ... [--history] [--transfers]
market-value-history PLAYER
transfer-history PLAYER
games PLAYER [--season S] [--competition ID] [--played-only]
[--summary competition|season] [--no-names]
squad CLUB [--season S]
competition COMPETITION [--season S]
table COMPETITION [--season S]
market-values [--competition ID] [--position ID|NAME] [--position-group GROUP]
[--age-group AGE] [--born YEAR] [--country-id ID]
[--continent NAME] [--pages N] [--max N]
transfers COMPETITION [--season S] [--window summer|winter]
[--loans all|only|no-returns|exclude] [--internal]
[--direction in|out] [--club CLUB] [--balances]
every command: [-f json|jsonl|csv|table] [-o FILE]
Ids. PLAYER, CLUB and COMPETITION are Transfermarkt ids or URLs. The id is in every Transfermarkt URL (.../profil/spieler/418560, .../startseite/verein/281, .../startseite/wettbewerb/GB1), and search finds it for you. Some common competition ids:
| Id | Competition | Id | Competition |
|---|---|---|---|
GB1 |
Premier League | GB2 |
Championship |
ES1 |
LaLiga | L1 |
Bundesliga |
IT1 |
Serie A | FR1 |
Ligue 1 |
NL1 |
Eredivisie | PO1 |
Liga Portugal |
BE1 |
Jupiler Pro League | TR1 |
Süper Lig |
SC1 |
Scottish Premiership | GR1 |
Super League 1 |
MLS1 |
Major League Soccer | BRA1 |
Brasileirão Série A |
SA1 |
Saudi Pro League | JAP1 |
J1 League |
CL |
UEFA Champions League | EL |
UEFA Europa League |
Seasons. A season is the year it starts in: --season 2025 (or 2025/26, 25/26) is the 25/26 season. For leagues played in one calendar year (MLS, Brasileirão) it is that year. Without --season you get the current season.
| Option | Meaning |
|---|---|
games --summary |
competition: totals per season and competition; season: per season over all competitions. |
games --no-names |
Skip the club/competition name lookups (ids only, one request). |
market-values --position |
A position id or name: goalkeeper, centre-back, left-back, right-back, defensive-midfield, central-midfield, attacking-midfield, left-winger, right-winger, second-striker, centre-forward, ... |
market-values --position-group |
goalkeepers, defenders, midfielders, forwards. |
market-values --age-group |
u17 ... u23, 23-30, o30, o32, o34. |
market-values --continent |
uefa, conmebol, concacaf, caf, afc, ofc. |
market-values --competition |
One league's most valuable players (the other filters don't apply). |
transfers --loans |
all (default), only loans, no-returns (drop players coming back from loan), exclude loans. |
transfers --internal |
Include moves inside a club (e.g. from its U21 or B team). |
Requests per command:
| Command | Requests |
|---|---|
search, market-values |
One per result page. |
player |
One, plus one for --history and one for --transfers. |
games |
One, plus usually two name lookups (none with --no-names). |
competition |
One (two for a cup). |
squad, table, transfers |
One each. |
The CLI prints the request count to stderr.
from transfermarkt_scraper import TransfermarktScraper, summarize
tm = TransfermarktScraper() # reads SCRAPEUNBLOCKER_KEY
# Find ids
hit = tm.search("haaland")[0] # PlayerHit(player_id='418560', ...)
club = tm.search("real madrid", "clubs")[0] # ClubHit: club_id, league, squad value
# A player with market value and transfer history
player = tm.player(hit.player_id, market_value_history=True, transfer_history=True)
print(player.market_value_eur, player.contract_until, player.agent)
for move in player.transfer_history:
print(move.date, move.from_club, "->", move.to_club, move.fee_eur, move.fee_kind)
# Game log and season totals
games = tm.games(hit.player_id, season=2025) # 25/26, all competitions
league_games = tm.games(hit.player_id, competition="GB1", played_only=True)
for row in summarize(games, by="competition"):
print(row.season, row.competition, row.appearances, row.goals, row.assists, row.minutes)
# Clubs and leagues
squad = tm.squad(281) # Manchester City, current season
old_squad = tm.squad(281, season=2020)
league = tm.competition("GB1") # overview + clubs with squad values
table = tm.table("ES1", season=2024)
# Market values
top = tm.market_values(max_pages=4) # top 100 worldwide
wonderkids = tm.market_values(age_group="u21", position=14, max_pages=2)
bundesliga = tm.market_values(competition="L1", max_results=50)
# A league's transfer window
moves, balances = tm.transfers_page("GB1", 2025, window="summer")
big_buys = [m for m in moves if m.direction == "in" and (m.fee_eur or 0) >= 50_000_000]Every result is a dataclass with to_dict(). tm.requests counts the API requests made. NotFoundError is raised for an unknown id.
transfermarkt-scraper games 418560 --season 2025 --summary competition -f table:
SEASON COMPETITION CLUB APPS MIN G A YC RC
2026 World Cup Norway 5 465 7 1 0 0
25/26 International Friendlies Norway 3 180 1 0 0 0
25/26 Premier League Manchester City 35 2959 27 8 2 0
25/26 FA Cup Manchester City 4 231 3 1 0 0
25/26 EFL Cup Manchester City 3 199 0 0 0 0
25/26 UEFA Champions League Manchester City 10 755 8 0 0 0
6 rows, 3 requests
transfermarkt-scraper squad 281 -f table (first rows):
# ID NAME POSITION AGE CONTRACT VALUE
1 315858 Gianluigi Donnarumma Goalkeeper 27 2030-06-30 €45.00m
28 229604 Gerónimo Rulli Goalkeeper 34 2028-06-30 €5.00m
13 116648 Marcus Bettinelli Goalkeeper 34 2027-06-30 €300k
6 392757 Marc Guéhi Centre-Back 26 2031-06-30 €75.00m
3 258004 Rúben Dias (C) Centre-Back 29 2029-06-30 €50.00m
transfermarkt-scraper transfers GB1 --season 2025 --balances -f table (first rows):
CLUB IN OUT SPENT RECEIVED BALANCE
Arsenal FC 15 16 €297.57m €13.88m -€283.69m
Aston Villa 23 22 €83.50m €57.85m -€25.65m
AFC Bournemouth 21 21 €181.10m €310.41m €129.31m
Brentford FC 17 19 €108.70m €159.61m €50.91m
Brighton & Hove Albion 30 30 €82.00m €149.95m €67.95m
20 clubs, 1 requests
transfermarkt-scraper market-values --age-group u21 --position-group forwards -f table (first rows):
# ID NAME POSITION AGE CLUB VALUE
1 937958 Lamine Yamal Right Winger 19 FC Barcelona €220.00m
2 1390649 Yan Diomande Right Winger 19 Real Madrid €120.00m
3 1056993 Estêvão Right Winger 19 Chelsea FC €80.00m
4 955357 Junior Kroupi Centre-Forward 20 AFC Bournemouth €70.00m
transfermarkt-scraper player 418560 --history --transfers (abridged):
{
"player_id": "418560",
"name": "Erling Haaland",
"full_name": "Erling Braut Håland",
"date_of_birth": "2000-07-21",
"age": 26,
"citizenship": ["Norway"],
"height_cm": 195,
"foot": "left",
"position": "Centre-Forward",
"club": "Manchester City",
"club_id": "281",
"league": "Premier League",
"joined": "2022-07-01",
"contract_until": "2034-06-30",
"last_contract_extension": "2025-01-17",
"agent": "Rafaela Pimenta",
"market_value_eur": 220000000,
"market_value_updated": "2026-07-22",
"national_team": "Norway",
"caps": 59,
"international_goals": 65,
"market_value_history": [
{"date": "2026-06-03", "market_value_eur": 200000000, "club": "Manchester City", "age": 25},
{"date": "2026-07-22", "market_value_eur": 220000000, "club": "Manchester City", "age": 26}
],
"transfer_history": [
{
"date": "2022-07-01",
"season": "22/23",
"from_club": "Dortmund",
"from_club_id": "16",
"to_club": "Man City",
"to_club_id": "281",
"market_value_eur": 150000000,
"fee_eur": 60000000,
"fee_kind": "fee",
"fee": "€60.00m",
"upcoming": false,
"url": "https://www.transfermarkt.com/erling-haaland/transfers/spieler/418560/transfer_id/3845482"
}
]
}One game from transfermarkt-scraper games 418560 (some fields omitted):
{
"game_id": "4899313",
"date": "2026-10-17",
"kickoff_utc": "2026-10-17T14:00:00+00:00",
"season": "26/27",
"competition_id": "GB1",
"competition": "Premier League",
"matchday": 7,
"venue": "home",
"club": "Manchester City",
"opponent": "Ipswich Town",
"goals_for": 3,
"goals_against": 2,
"result": "W",
"participation": "played",
"started": true,
"minutes": 90,
"goals": 2,
"assists": 0,
"yellow_card_minute": null,
"position": "Centre-Forward",
"shots": 5,
"shots_on_target": 2,
"passes": 15,
"passes_completed": 9,
"url": "https://www.transfermarkt.com/spielbericht/index/spielbericht/4899313"
}examples/player_report.py: find a player by name and save profile, market value history, transfers and per-season totals as one JSON report.examples/league_snapshot.py: join a league table with each club's squad market value in one CSV and print the clubs far above or below their value rank.examples/net_spend.py: rank every club of the Big Five leagues by transfer balance for a season.
- Transfermarkt's market values are its own estimates, and its pages are in English on transfermarkt.com with amounts in EUR.
- Data on transfermarkt.com belongs to Transfermarkt. Respect its terms of use, keep request volumes reasonable and cache what you have already fetched. This project is not affiliated with Transfermarkt.
src/transfermarkt_scraper/
__init__.py package exports and version
scraper.py TransfermarktScraper: fetching, retries, paging, name lookups
parsing.py HTML and JSON parsers (BeautifulSoup) and the season summary
models.py dataclasses for every result type
urls.py URL builders, id/season parsing and filter vocabularies
values.py money, fee, date, height and number parsing
export.py JSON / JSONL / CSV / table output
cli.py the transfermarkt-scraper command
examples/ runnable scripts (JSON report, CSV exports)
tests/ offline tests with real, trimmed Transfermarkt pages (no API calls)
python -m pip install -e ".[dev]"
make lint # ruff check + format check
make test # pytest, fully offline: the API client is mockedpre-commit install runs ruff on every commit (see .pre-commit-config.yaml).
- ScrapeUnblocker: web scraping API that gets past blocks and CAPTCHAs
- ScrapeUnblocker documentation
- Python SDK
MIT © 2026 ScrapeUnblocker