World Cup 2026 Player Stats API: Golden Boot, Assists & Minutes
World Cup 2026 player stats API. Live goals, assists, xG, shots, minutes, and cards for every player. Track the Golden Boot race in real time.
Player stats are what fans actually care about during the World Cup — the Golden Boot race, who's racking up assists, who's getting cards, who's playing 90 minutes every match. If you're building a fantasy app, a leaderboard widget, or a player-comparison tool, you need fast access to live per-player stats.
This guide covers what the player stats endpoint returns, how to fetch the Golden Boot leaderboard, and how to surface useful player-level views in your app.
What player stats are available
For every player who features in a World Cup match:
- Per-match stats — goals, assists, shots, shots on target, xG, xA, minutes, yellow cards, red cards, pass accuracy, tackles, interceptions, fouls, passes, key passes
- Per-tournament aggregates — totals across all matches in the World Cup
- Per-90 metrics — goals per 90, xG per 90, etc.
- Position-specific — clean sheets and saves for goalkeepers
- Substitution data — minute on, minute off
Plus player metadata: name, age, position, shirt number, club, height, foot, captaincy status.
Build the Golden Boot leaderboard
curl 'https://api.thestatsapi.com/api/football/matches?competition_id={COMPETITION_ID}&season_id={SEASON_ID}&status=finished&per_page=100' \
-H 'Authorization: Bearer YOUR_API_KEY'
Start by fetching the finished World Cup matches. For each finished match, call the per-match player stats endpoint:
curl 'https://api.thestatsapi.com/api/football/matches/{MATCH_ID}/player-stats' \
-H 'Authorization: Bearer YOUR_API_KEY'
Each row has the player, team, minutes, passing, shooting, defending, goalkeeping, and discipline fields. A real player row looks like this:
{
"player_id": "pl_57255528",
"player_name": "Kylian Mbappé",
"team_id": "tm_28952",
"position": "F",
"started": true,
"played": true,
"minutes_played": 120,
"shooting": {
"goals": 3,
"total_shots": 8,
"shots_on_target": 5,
"expected_goals": 2.25
},
"passing": {
"assists": 0,
"key_passes": 2
},
"general": {
"yellow_cards": 0,
"red_cards": 0
}
}
Aggregate those rows by player_id, then sort by goals, assists, and minutes played for a Golden Boot table.
Golden Boot tiebreakers
If two players are tied on goals, FIFA applies tiebreakers in order:
- Most assists (assist credit follows FIFA's strict definition)
- Fewest minutes played (rewards efficiency)
- Drawing of lots if still tied
Your code should apply those tiebreakers after aggregating the per-match rows.
Build a live leaderboard widget
import useSWR from 'swr';
function GoldenBootLeaderboard() {
const { data } = useSWR(
'/api/wc-top-scorers',
(url) => fetch(url).then((r) => r.json()),
{ refreshInterval: 30000 }
);
if (!data) return <div>Loading...</div>;
return (
<ol>
{data.map((p, i) => (
<li key={p.player.id}>
<span className="rank">{i + 1}.</span>
<span className="name">{p.player.name}</span>
<span className="team">({p.player.team})</span>
<span className="goals">{p.goals} goals, {p.assists} assists</span>
</li>
))}
</ol>
);
}
Polling every 30 seconds is plenty — Golden Boot positions don't change between goals.
Aggregate per-match rows in JavaScript
function addStat(total, row) {
const current = total.get(row.player_id) ?? {
player_id: row.player_id,
player_name: row.player_name,
team_id: row.team_id,
minutes: 0,
goals: 0,
assists: 0,
shots: 0,
shots_on_target: 0,
xg: 0,
yellow_cards: 0,
red_cards: 0,
};
current.minutes += row.minutes_played ?? 0;
current.goals += row.shooting?.goals ?? 0;
current.assists += row.passing?.assists ?? 0;
current.shots += row.shooting?.total_shots ?? 0;
current.shots_on_target += row.shooting?.shots_on_target ?? 0;
current.xg += row.shooting?.expected_goals ?? 0;
current.yellow_cards += row.general?.yellow_cards ?? 0;
current.red_cards += row.general?.red_cards ?? 0;
total.set(row.player_id, current);
}
function sortGoldenBoot(rows) {
return [...rows].sort((a, b) => (
b.goals - a.goals ||
b.assists - a.assists ||
a.minutes - b.minutes ||
a.player_name.localeCompare(b.player_name)
));
}
Compare two players
const COMPETITION_ID = 'YOUR_COMPETITION_ID'; // look up via /football/competitions?search=world%20cup
const SEASON_ID = 'YOUR_SEASON_ID'; // look up via /football/competitions/{id}/seasons
async function comparePlayers(playerA, playerB) {
const [a, b] = await Promise.all([
fetch(`/api/football/players/${playerA}/stats?competition_id=${COMPETITION_ID}&season_id=${SEASON_ID}`).then((r) => r.json()),
fetch(`/api/football/players/${playerB}/stats?competition_id=${COMPETITION_ID}&season_id=${SEASON_ID}`).then((r) => r.json()),
]);
return {
[playerA]: a.data,
[playerB]: b.data,
};
}
// Use player IDs from /football/players?search=Mbappe
const comparison = await comparePlayers('pl_57255528', 'pl_1954708');
Render side by side for a player-comparison widget.
Position-specific stats
Goalkeepers have additional fields:
saves— total savesclean_sheets— matches without concedingpenalties_saved— penalty saves in regulation or shootoutssave_percentage— saves / shots on target faced
Get goalkeeper rows from /football/matches/{match_id}/player-stats, aggregate goalkeeping.saves, and sort by saves or clean-sheet logic in your own table.
Player metadata: ages, clubs, heights
Useful for narrative-driven cards:
curl 'https://api.thestatsapi.com/api/football/players/pl_57255528' \
-H 'Authorization: Bearer YOUR_KEY'
Returns player metadata: full name, date of birth, position, nationality, height, current team, market value where available, and identifiers you can reuse in stats calls.
Common UI patterns
- Player card — photo + key stats (goals/assists/minutes) + comparison to tournament average
- Hot players widget — sorted by xG (under-performers are bets to score next match)
- All-star XI — top scorer + top assister + top defender by position
- Daily MVP — best individual performance on a given match-day
- Per-team leaderboard — top scorers within a single team
Caching strategy
- Outside match-days: cache 1 hour
- Match-day, no live matches: cache 5 minutes
- During a match: cache 30-60 seconds
- Live in a high-stakes match: poll directly
Free alternatives
For just basic per-player stats during the World Cup, API-Football on their free tier covers it (100 requests/day). It's enough for a hobby project but the daily cap will hurt during a busy match-day. For production use, TheStatsAPI Starter at $50/month covers the full tournament without rate-limit anxiety.
Frequently Asked Questions
How fast do player stats update during a match?
Within seconds of an event in our source feed. Player goals, assists, and cards appear in the stats endpoint immediately after they happen on the pitch.
What is xG per 90?
Expected goals normalised to a full 90-minute match. Useful for comparing players with different minutes played. A striker with 0.7 xG per 90 is generating high-quality chances at a regular rate.
How is the Golden Boot decided?
Most goals wins. Tiebreakers: most assists, then fewest minutes played, then drawing of lots.
Can I get per-match player stats (not just tournament totals)?
Yes — /football/matches/{match_id}/player-stats returns every player's per-match line. Useful for "best 11 of match-day" widgets.
Does the API include player photos?
Player metadata includes a photo_url field where the right has been licensed. For unlicensed players, you'll need to source images separately (Wikimedia Commons is a common fallback).
How do I detect the tournament MVP?
There's no single "MVP" metric — FIFA awards the Golden Ball based on a media vote. Proxy with weighted goals + assists + minutes + xG and you'll be close.
Can I get historical player stats?
Yes — every World Cup back to 1930 has player-level stats at varying depth. Modern editions (post-1990) have detailed per-match data; older editions have tournament-level summaries.
Ready to Power Your Sports App?
Start your 7-day free trial. All endpoints included on every plan.