Transfermarkt Football Transfers & Fees Scraper
Pricing
from $1.50 / 1,000 transfer scrapeds
Transfermarkt Football Transfers & Fees Scraper
Scrape football transfers from Transfermarkt: every arrival and departure for a club, a whole league's transfer window, or the global latest-transfers feed. Fee, market value, position, age and both clubs on every row. Any season. No API key, no login, no proxy needed.
Pricing
from $1.50 / 1,000 transfer scrapeds
Rating
0.0
(0)
Developer
Elena Vance
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
5 days ago
Last modified
Categories
Share
Scrape football transfers from Transfermarkt three ways — a club's window, a whole league's window, or the global latest-transfers feed — and get one row per transfer with the fee, the player's market value at the time, and both clubs named on every row.
No API key, no login, no residential proxy, no browser. It runs on the Apify free plan. Export to JSON, CSV, Excel, XML or pull it from the API.
What you get
One row per transfer. Real, unedited output from {"clubIds": ["281"], "season": 2025}:
| Field | Value |
|---|---|
transferId | 6107087 |
source / direction | club / in |
season / seasonStartYear | 25/26 / 2025 |
playerId / playerName | 583255 / Antoine Semenyo |
position / age | Right Winger / 26 |
nationality | ["Ghana", "England"] |
marketValue | 65000000 |
fee | 72000000 |
feeType / feeText | fee / €72.00m |
fromClubId / fromClubName | 989 / AFC Bournemouth |
toClubId / toClubName | 281 / Manchester City |
clubIncome / clubExpenditure | 107280000 / 301800000 |
clubBalance | -194520000 |
playerUrl | https://www.transfermarkt.com/x/profil/spieler/583255 |
Both ends of the move are on every row. Transfermarkt's own pages name only
one — a club's page tells you who arrived and leaves "arrived where" implicit in
which table the row was in. Here fromClub* and toClub* are always filled, so
a club run and a league run stack into one table without a single join.
A loan is not a zero-euro transfer
fee is null, never 0, for a free transfer, a loan, an end of loan or an
undisclosed fee — because none of those is a fee of zero, and averaging a column
where they became zero quietly halves every number in it. feeType says which
of the five cases it was, and feeText keeps Transfermarkt's own wording so an
unusual one is never silently rounded into a number.
feeType | fee | Transfermarkt's wording |
|---|---|---|
fee | the amount | €72.00m |
loan | the loan fee, or null | loan transfer, Loan fee: €2.00m |
free | null | free transfer |
end_of_loan | null, plus loanEndDate | End of loan 30/06/2025 |
undisclosed | null | ? |
transferId makes repeat runs safe
Every row carries Transfermarkt's own transfer id, so a daily run against the latest feed de-duplicates on one integer instead of on a name-and-fee guess.
Three ways to ask
| Input | What it scrapes | Rows | Requests |
|---|---|---|---|
clubIds | one club's arrivals and departures | 20–45 per club | 1 per club |
competitionCodes | every club in a league, in and out | ~740 | 1 per code |
includeLatestTransfers | the worldwide latest-confirmed feed | 25 per page, 250 max | 1 per page |
They combine freely in one run, and all three produce exactly the same columns, so the dataset is one table however you asked for it.
⚠️ A league window is around 740 rows
{"competitionCodes": ["GB1"]} is twenty clubs' complete transfer business —
around 740 charged rows from one three-character code. That is the single thing
to know before setting maxItems, and it is why maxItems defaults to 1000
rather than the smaller default the rest of this suite uses.
You are never charged for the same transfer twice. A move between two clubs
of the same league is on Transfermarkt's page twice — the buyer's arrival and
the seller's departure — and the Premier League's 25/26 window is 819 table
rows for 740 actual transfers. Those 79 are collapsed before billing, keeping
the arrival, so direction is in and clubId is the buying club for every
move inside the league. fromClubId still names the seller. The same applies
across sources: ask for a club and its league and the overlap is charged
once.
Input
{"clubIds": ["281", "https://www.transfermarkt.com/real-madrid/startseite/verein/418"],"competitionCodes": ["GB1"],"includeLatestTransfers": false,"season": 2025,"maxItems": 1000}
| Input | What it does |
|---|---|
clubIds | Numeric IDs or any Transfermarkt club URL. The squad, profile and transfers tabs all work — the name in the URL is ignored. |
competitionCodes | League codes (GB1, ES1, L1, IT1, FR1) or any Transfermarkt competition URL. ~820 rows each. |
includeLatestTransfers | The global feed, newest first. It is ten pages — 250 transfers — deep, and stops on its own when a page repeats. |
season | The year a season starts — 2025 is the 2025/26 window. Leave empty for the current one. Does not apply to the latest feed. |
maxItems | Hard cap on charged rows across every source. Default 1000. |
proxyConfiguration | Leave it off — see below. |
Cups have no transfer window
Transfermarkt publishes transfer windows per league, not per cup. Ask for
CL or EL and you get an unbilled COMPETITION_HAS_NO_TRANSFERS row saying
so, rather than an empty dataset you have to diagnose yourself.
To get Champions League clubs' transfers, take their clubIds from the
Transfermarkt League Table & Club Values Scraper and pass those here.
What this Actor does NOT return
- Transfer dates. Transfermarkt's transfer tables do not carry them —
not the club page, not the league page, not the latest feed. Transfers are
grouped by season, and the only date anywhere on them is
loanEndDateon an end-of-loan row. Per-player dated transfer history exists on a different endpoint, and it is what the Transfermarkt Player & Market Value Scraper returns. - Rumours. Transfermarkt's rumour pages are not reachable at any documented URL; treat the Rumour Mill as unavailable rather than missing.
- All-time transfer records, which are a different table with a different shape.
- Contract details or wages. Not published.
- Anything requiring a Transfermarkt login.
Why this is cheap to run
Transfermarkt is fronted by AWS WAF, and the usual answer — a residential proxy at $8/GB — is what makes scrapers of this site expensive. It turns out Apify's own egress is not challenged, so this Actor calls Transfermarkt directly and pays nothing for proxy. A residential IP is fetched only if a request is genuinely challenged, and most runs never touch one.
A league window is one request for ~820 rows, which is the best rows-per-request ratio anywhere in this suite.
The Actor start fee is $0.00005 and the default memory is 512 MB — under the 1 GB line, so Apify's per-gigabyte start fee is charged once. Several Transfermarkt scrapers on the Store run at 4 GB with a $0.10 start fee, which bills $0.40 before returning a single row.
Do not turn on Apify's datacenter proxy. Transfermarkt's WAF challenges Apify's datacenter pool while leaving its direct egress alone — verified, same URLs, same second — so switching it on takes a working run and breaks it.
What makes this different
- Both clubs on every row, from three sources that each name only one.
- A loan is null, not zero. The difference is invisible until someone averages the column.
- Market value alongside the fee, so "did they overpay" is a subtraction rather than a second scrape. The club view only carries it in Transfermarkt's detailed layout, which this Actor always requests.
- The season returned is the season reported.
seasonis read back off the page, never echoed from your input, andrequestedSeasonsits beside it — Transfermarkt silently serves its newest season for a future year. - A cup, a missing club and a missing code are three different answers, each an unbilled row that says which, rather than silence.
- You are never billed for a failure, or for the same transfer twice, and
bad input returns an
INVALID_INPUTrow instead of failing the run. - A weekly canary runs the deployed build against live Transfermarkt across all three sources and opens a GitHub issue if field coverage drops.
Example runs
One club's window:
{ "clubIds": ["281"] }
A whole league, past season:
{ "competitionCodes": ["GB1"], "season": 2024, "maxItems": 1000 }
Who did Europe's big five buy from:
{ "competitionCodes": ["GB1", "ES1", "L1", "IT1", "FR1"], "maxItems": 4000 }
A daily feed of the world's confirmed transfers:
{ "includeLatestTransfers": true, "maxItems": 200 }
Related Actors
Part of a Transfermarkt suite:
- Transfermarkt Player & Market Value Scraper — one player in depth, with the full market-value and dated transfer histories.
- Transfermarkt Club Squad Scraper — every player in a club's squad, with the fee each was signed for.
- Transfermarkt League Table & Club Values Scraper — league tables merged with what every club's squad is worth.
- Transfermarkt Football Transfers & Fees Scraper — this one.
They chain: the competition scraper's clubId is this Actor's input, and the
playerId this Actor returns is the player scraper's input.