GUIDE

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.

6 min read

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

ElementCount
Groups12 (A–L)
Teams per group4
Matches per group6
Total group-stage matches72
DaysJune 11 – June 28, 2026
Teams advancing32 (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/standings?competition_id={COMPETITION_ID}&season=2026' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Returns 12 group tables. Each row:

{
  "group": "A",
  "position": 1,
  "team": { "id": "tm_mex", "name": "Mexico", "slug": "mexico" },
  "played": 3,
  "won": 2,
  "drawn": 1,
  "lost": 0,
  "goals_for": 5,
  "goals_against": 2,
  "goal_difference": 3,
  "points": 7,
  "qualification": "winner"  // "winner" | "runner_up" | "best_third" | "third" | "eliminated"
}

The qualification flag is the killer field — it tells you whether each team has advanced, is in the best-third race, or is eliminated. No need to recompute the logic client-side.

Filter to a single group

curl 'https://api.thestatsapi.com/api/football/standings?competition_id={COMPETITION_ID}&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:

  1. Points in all group matches
  2. Goal difference in all group matches
  3. Goals scored in all group matches
  4. Points in head-to-head matches between tied teams
  5. Goal difference in head-to-head matches
  6. Goals scored in head-to-head matches
  7. Fair play points (yellow/red card deductions)
  8. 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:

  1. Points
  2. Goal difference
  3. Goals scored
  4. Fewest disciplinary points (yellow / red card deductions)
  5. Drawing of lots

The standings endpoint flags each third-placed team's status:

  • "qualification": "best_third" — confirmed Round of 32 qualifier
  • "qualification": "third" — still in the race
  • "qualification": "eliminated" — confirmed out

JavaScript: render a live standings table

import useSWR from 'swr';

function GroupStandings({ group }) {
  const { data } = useSWR(
    `/api/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.slug} className={qualClass(row.qualification)}>
            <td>{row.position}</td>
            <td>{row.team.name}</td>
            <td>{row.played}</td>
            <td>{row.won}</td>
            <td>{row.drawn}</td>
            <td>{row.lost}</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(q) {
  if (q === 'winner' || q === 'runner_up') return 'bg-emerald-50';
  if (q === 'best_third') return 'bg-blue-50';
  if (q === 'eliminated') return 'bg-red-50';
  return '';
}

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, use the API's qualification flag instead of recomputing — fair play points are hard to get right.

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 /standings with the live /matches?status=in_progress 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. The standings endpoint flags each third-placed team's qualification status.

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 — filter ?group=A (or B, C, etc.) on the standings endpoint.

What's the difference between the standings API and the matches API?

/matches returns individual fixtures and results. /standings returns the aggregated league table for each group with tiebreakers applied. Use both together for a complete group view.

How do I visualise qualification status?

Use the qualification flag on each standings row: winner, runner_up, best_third, third, eliminated. Colour-code rows or add an icon to give fans an at-a-glance read.

Does standings include xG and shot data?

The standings endpoint focuses on classic table stats. For xG-based analysis (expected points vs actual, shooting accuracy), use /teams/{id}/stats?competition_id={COMPETITION_ID} to get the deeper metrics.

Start building today

Ready to Power Your Sports App?

Start your 7-day free trial. All endpoints included on every plan.

Cancel anytime
7-day free trial
Setup in 5 minutes