World Cup 2026 Group Stage API: Standings, Tiebreakers & Qualification
World Cup 2026 group stage API: live standings for all 12 groups, full FIFA tiebreakers, third-place qualification logic, and code examples.
The group stage of the 2026 FIFA World Cup is bigger than ever: 12 groups, 48 teams, 72 matches, and the new 24+8 qualification format that sends 32 teams into the Round of 32. Standings change literally every 90 minutes during match-days, and you need an API that keeps up.
This guide covers how the standings endpoint works, how FIFA tiebreakers are applied, and how to handle the new "best third-placed teams" qualification logic in your app.
The 2026 group stage format
| Element | Count |
|---|---|
| Groups | 12 (A-L) |
| Teams per group | 4 |
| Matches per group | 6 |
| Total group-stage matches | 72 |
| Days | June 11 - June 27, 2026 |
| Teams advancing | 32 (top 2 from each group + 8 best 3rd-placed) |
So the third-placed teams matter — across all 12 groups, the top 8 by points/GD/GS join the 24 group winners and runners-up in the Round of 32.
Get live standings for all 12 groups
curl 'https://api.thestatsapi.com/api/football/competitions/{COMPETITION_ID}/seasons/{SEASON_ID}/standings' \
-H 'Authorization: Bearer YOUR_API_KEY'
Returns 12 group tables. Each row:
{
"team": { "id": "tm_28735", "name": "Mexico" },
"position": 1,
"matches_played": 0,
"wins": 0,
"draws": 0,
"losses": 0,
"goals_for": 0,
"goals_against": 0,
"goal_difference": 0,
"points": 0,
"group_label": "A"
}
The API applies table ordering and returns rows sorted by group and position. If your UI needs labels like "qualified", "best third", or "eliminated", calculate those display labels from the returned positions and the cross-group third-place table.
Filter to a single group
curl 'https://api.thestatsapi.com/api/football/competitions/{COMPETITION_ID}/seasons/{SEASON_ID}/standings?group=A' \
-H 'Authorization: Bearer YOUR_API_KEY'
Useful for group-specific pages or widgets.
FIFA tiebreakers explained
When teams are level on points, FIFA applies tiebreakers in strict order:
- Points in all group matches
- Goal difference in all group matches
- Goals scored in all group matches
- Points in head-to-head matches between tied teams
- Goal difference in head-to-head matches
- Goals scored in head-to-head matches
- Fair play points (yellow/red card deductions)
- Drawing of lots by FIFA
The standings API applies all of these automatically. The position field reflects the post-tiebreaker order.
The best third-placed teams logic
Across the 12 third-placed teams, the top 8 advance. The ranking criteria mirror the group tiebreakers:
- Points
- Goal difference
- Goals scored
- Fewest disciplinary points (yellow / red card deductions)
- Drawing of lots
Your app can label third-placed teams from the standings response:
- top eight third-placed rows across all groups — Round of 32 qualifier
- remaining third-placed rows — outside the qualification places
- fourth-placed rows — eliminated once the group is complete
JavaScript: render a live standings table
import useSWR from 'swr';
function GroupStandings({ group }) {
const { data } = useSWR(
`/api/world-cup-standings?group=${group}`,
(url) => fetch(url).then((r) => r.json()),
{ refreshInterval: 30000 } // poll every 30s during matches
);
if (!data) return <div>Loading...</div>;
return (
<table>
<thead>
<tr>
<th>#</th><th>Team</th><th>P</th><th>W</th><th>D</th><th>L</th>
<th>GF</th><th>GA</th><th>GD</th><th>Pts</th>
</tr>
</thead>
<tbody>
{data.map((row) => (
<tr key={row.team.id} className={qualClass(row.position)}>
<td>{row.position}</td>
<td>{row.team.name}</td>
<td>{row.matches_played}</td>
<td>{row.wins}</td>
<td>{row.draws}</td>
<td>{row.losses}</td>
<td>{row.goals_for}</td>
<td>{row.goals_against}</td>
<td>{row.goal_difference > 0 ? '+' : ''}{row.goal_difference}</td>
<td><strong>{row.points}</strong></td>
</tr>
))}
</tbody>
</table>
);
}
function qualClass(position) {
if (position <= 2) return 'bg-emerald-50';
if (position === 3) return 'bg-blue-50';
return 'bg-red-50';
}
Python: detect the best third-placed teams
If you need to compute the ranking yourself (e.g. for predictions before all matches are played):
def rank_third_placed(standings_by_group):
third_placed = []
for group_letter, table in standings_by_group.items():
if len(table) >= 3:
third = table[2]
third_placed.append({
'group': group_letter,
'team': third['team']['name'],
'points': third['points'],
'gd': third['goal_difference'],
'gf': third['goals_for'],
})
# FIFA criteria: pts, gd, gf, alphabetic
third_placed.sort(
key=lambda x: (-x['points'], -x['gd'], -x['gf'], x['team'])
)
return third_placed[:8] # top 8 advance
For the actual production game, keep the API standings as your source of truth for points, goal difference, goals scored, and group position. If you add projected standings while matches are live, keep that projection clearly separate from the official table returned by the API.
Standings during a match
Standings update as soon as a match finishes — typically within seconds of the final whistle. During a match, the table reflects the pre-match state (not "what if the current live score holds"). To project standings live, combine the standings endpoint with GET /football/matches?competition_id={COMPETITION_ID}&season_id={SEASON_ID}&status=live and recompute client-side.
Our Group Stage Simulator tool does exactly this — pure client-side projection from user-entered scores. Inspect the source for a reference implementation.
Caching guidance
- Before match-day: cache standings for 1 hour
- During match-day: cache for 30-60 seconds
- During a match: poll directly without cache, or cache for 10 seconds
The standings payload is small (12 groups × 4 rows × ~12 fields) so caching is more about reducing rate-limit pressure than payload size.
Frequently Asked Questions
How are World Cup 2026 group stage ties broken?
In order: points, goal difference, goals scored, head-to-head points, head-to-head goal difference, head-to-head goals scored, fair play points, drawing of lots. The standings API applies these automatically.
How are the 8 best third-placed teams ranked?
Points first, then goal difference, then goals scored, then fewest disciplinary points (fair play), then drawing of lots. Fetch all standings rows, take each row where position === 3, sort those rows, and mark the top eight as qualifiers.
How fast do standings update during matches?
Within seconds of a match's final whistle. During matches, the standings reflect the pre-match state (not projected live).
Can I get standings for a single group?
Yes — add ?group=A (or B, C, etc.) to /football/competitions/{competition_id}/seasons/{season_id}/standings.
What's the difference between the standings API and the matches API?
/football/matches returns individual fixtures and results. /football/competitions/{competition_id}/seasons/{season_id}/standings returns the aggregated table for each group. Use both together for a complete group view.
How do I visualise qualification status?
Colour-code positions 1-2 as direct qualifiers, position 3 as the best-third race, and position 4 as outside the qualification places. Once all groups are complete, rank all third-placed teams by points, goal difference, and goals scored to identify the eight best third-placed qualifiers.
Does standings include xG and shot data?
The standings endpoint focuses on classic table stats. For xG-based analysis, call /football/matches/{match_id}/stats or /football/matches/{match_id}/shotmap for completed matches and aggregate those rows yourself.
Ready to Power Your Sports App?
Start your 7-day free trial. All endpoints included on every plan.