World Cup 2026 Groups & Schedule: How to Pull Fixtures via a Football API
Contents

Why Structure Matters for World Cup 2026

Unlike traditional league seasons, the World Cup has a complex multi-stage structure:
Group Stage: 12 groups of 4 teams each (48 teams total)
Round of 32: Top 2 from each group plus 8 best third-place teams
Round of 16, Quarter-finals, Semi-finals, and Final

Each stage has different characteristics. Group stages have standings tables, while knockout stages have elimination brackets. Understanding how the API represents this structure is key to building a robust application.

Understanding the API’s Tournament Structure

The Sportmonks API uses a hierarchical structure to represent tournaments:

League (World Cup)

└── Season (2026)

    ├── Stage (Group Stage)

    │   ├── Group A

    │   ├── Group B

    │   └── … (12 groups total)

    │   └── Rounds (Matchday 1, 2, 3)

    ├── Stage (Round of 32)

    ├── Stage (Round of 16)

    ├── Stage (Quarter-finals)

    ├── Stage (Semi-finals)

    └── Stage (Final)

Key Concepts

Stages represent major phases of the competition (e.g., “Group Stage,” “Quarter-finals”). Each stage has:
type_id: Indicates the stage type (223 = Group Stage, 224 = Knockout, 225 = Qualifying)
is_current: Boolean showing if this is the active stage
finished: Boolean showing if the stage is complete

Groups are subdivisions within stages, typically used in group stages. The World Cup 2026 will have 12 groups labeled A through L.

Rounds are match-days within a stage (e.g., “Matchday 1,” “Matchday 2”). In group stages, each round typically consists of one set of matches.

Fixtures are individual matches, each connected to a season, stage, group (if applicable), and round.

Prerequisites

Before we begin, you’ll need:
– A Sportmonks API account with access to World Cup 2026 data
– Your API token from MySportmonks
– Basic knowledge of JavaScript and making API requests

Important: The World Cup 2026 data may not be fully populated until closer to the tournament. This guide teaches you the methodology that will work when the data is available.

Step 1: Finding the World Cup League and Season

First, we need to identify the World Cup league and the 2026 season.

Finding the League

const CONFIG = {
  API_BASE: 'https://api.sportmonks.com/v3/football',
  API_TOKEN: 'YOUR_TOKEN_HERE'
};

// Helper function for API requests
async function makeAPIRequest(endpoint, params = {}) {
  const queryParams = new URLSearchParams({
    api_token: CONFIG.API_TOKEN,
    ...params
  });
  
  const url = `${CONFIG.API_BASE}${endpoint}?${queryParams}`;
  
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`API Error: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error('API Request failed:', error);
    throw error;
  }
}

// Search for World Cup league
async function findWorldCupLeague() {
  try {
    const query = 'World Cup';
    const data = await makeAPIRequest(
      `/leagues/search/${encodeURIComponent(query)}`,
      { include: 'seasons' }
    );
    
    const leagues = data.data || [];
    
    // Look for FIFA World Cup
    const worldCupLeague =
      leagues.find(l => (l.name || '').toLowerCase() === 'fifa world cup') ||
      leagues.find(l => (l.name || '').toLowerCase().includes('world cup'));
    
    if (worldCupLeague) {
      console.log('World Cup League ID:', worldCupLeague.id);
      return worldCupLeague;
    }
    
    throw new Error('World Cup league not found');
  } catch (error) {
    console.error('Failed to find World Cup:', error);
    throw error;
  }
}

Finding the 2026 Season

Once you have the league, find the 2026 season

// Find the World Cup 2026 season for a given World Cup league id
async function findWorldCup2026Season(leagueId) {
  try {
    const res = await makeAPIRequest(`/leagues/${leagueId}`, {
      include: 'seasons'
    });

    const seasons = res?.data?.seasons || [];

    
    const season =
      seasons.find(s => s?.is_current === true) ||
      // Then fall back to explicit 2026 matching
      seasons.find(s => String(s?.name || '') === '2026') ||
      seasons.find(s => String(s?.name || '').includes('2026')) ||
      seasons.find(s => typeof s?.starting_at === 'string' && s.starting_at.startsWith('2026'));

    if (!season) {
      throw new Error('2026 season not found');
    }

    console.log('League ID:', leagueId); // e.g. 732 for World Cup league
    console.log('2026 Season ID:', season.id); // e.g. 26618 for the 2026 season
    console.log('Tournament dates:', season.starting_at, 'to', season.ending_at);

    return season;
  } catch (error) {
    console.error('Failed to find 2026 season:', error);
    throw error;
  }
}

 

Step 2: Getting the Complete Schedule

There are two main approaches to retrieving fixtures: using the fixtures included in the season, or using the schedules endpoint.

Approach 1: Season with Fixtures Include (Recommended)

This is the most straightforward way to get all tournament fixtures:

async function getWorldCup2026FixturesFlat(seasonId) {
  try {
    const { data } = await makeAPIRequest(`/seasons/${seasonId}`, {
      include: "fixtures",
    });

    const season = data?.data ?? null;
    const fixtures = season?.fixtures ?? [];

    return { season, fixtures };
  } catch (error) {
    console.error(
      "[getWorldCup2026FixturesFlat] Failed to fetch fixtures:",
      error
    );

    return {
      season: null,
      fixtures: [],
    };
  }
}

Key fields in each fixture:

{
        "id": 19609149,
        "sport_id": 1,
        "league_id": 732,
        "season_id": 26618,
        "stage_id": 77478590,
        "group_id": 253030,
        "aggregate_id": null,
        "round_id": 395361,
        "state_id": 1,
        "venue_id": 72372,
        "name": "England vs Croatia",
        "starting_at": "2026-06-17 20:00:00",
        "result_info": null,
        "leg": "1\/1",
        "details": "Match 22",
        "length": 90,
        "placeholder": false,
        "has_odds": false,
        "has_premium_odds": false,
        "starting_at_timestamp": 1781726400
      },

Approach 2: Schedules Endpoint

The schedules endpoint returns fixtures organised by stages and rounds:

async function getScheduleBySeasonId(seasonId) {
  try {
    const data = await makeAPIRequest(`/schedules/seasons/${seasonId}`, {
      include: 'fixtures'
    });
    
    // The schedules endpoint returns structured data
    // with stages, rounds, and their fixtures
    return data.data;
  } catch (error) {
    console.error('Failed to fetch schedules:', error);
    throw error;
  }
}

When to use each approach:

  • Fixtures include: Best for getting all fixtures at once and organising them yourself.
  • Schedules endpoint: Best when you need the fixtures already organised by the API’s structure

Step 3: Understanding Groups and Stages

Getting Stage Information

Stages define the major phases of the tournament:

async function getSeasonStages(seasonId) {
  try {
    const data = await makeAPIRequest(`/stages/seasons/${seasonId}`, {
      include: 'groups;rounds'
    });
    
    const stages = data.data || [];
    
    // Group stages vs knockout stages
    const groupStage = stages.find(s => s.type_id === 223);
    const knockoutStages = stages.filter(s => s.type_id === 224);
    
    return {
      groupStage,
      knockoutStages,
      allStages: stages
    };
  } catch (error) {
    console.error('Failed to fetch stages:', error);
    throw error;
  }
}

Stage Type IDs:

223: Group Stage (has standings tables)
224: Knockout Stage (elimination format)
225: Qualifying Stage

Working with Groups

Groups are subdivisions within the group stage:

async function getGroupStageFixtures(seasonId, stageId) {
  try {
    // Fetch the stage directly (stageId is your group stage id)
    // Example: /stages/77478590?include=fixtures
    const data = await makeAPIRequest(`/stages/${stageId}`, {
      include: 'fixtures'
    });

    //The stage endpoint returns fixtures under the data key.fixtures
    const fixtures = data?.data?.fixtures || [];

    // Optional safety: ensure we only keep fixtures for this season + stage
    const groupStageFixtures = fixtures.filter(
      (f) => String(f.stage_id) === String(stageId) && String(f.season_id) === String(seasonId)
    );

    // Organise by group_id
    const fixturesByGroup = groupStageFixtures.reduce((acc, fixture) => {
      const groupId = fixture.group_id;
      if (!groupId) return acc;

      if (!acc[groupId]) acc[groupId] = [];
      acc[groupId].push(fixture);

      return acc;
    }, {});

    return fixturesByGroup;
  } catch (error) {
    console.error('Failed to fetch group fixtures:', error);
    throw error;
  }
}

 

 

Step 4: Organising Fixtures by Date and Round

Grouping by Match Day

function organizeFixturesByDate(fixtures) {
  const byDate = {};
  
  fixtures.forEach(fixture => {
    // Extract date from starting_at (YYYY-MM-DD HH:MM:SS)
    const date = fixture.starting_at ? fixture.starting_at.split(' ')[0] : 'TBD';
    
    if (!byDate[date]) {
      byDate[date] = [];
    }
    
    byDate[date].push(fixture);
  });
  
  // Sort fixtures within each date by time
  Object.keys(byDate).forEach(date => {
    byDate[date].sort((a, b) => 
      (a.starting_at_timestamp || 0) - (b.starting_at_timestamp || 0)
    );
  });
  
  return byDate;
}

Organising by Round (Match Day)

async function getFixturesByRound(seasonId, stageId) {
  try {
    // Get rounds for the stage
    const roundsData = await makeAPIRequest(`/rounds/seasons/${seasonId}`, {
      include: 'fixtures.participants',
      filters: `roundStages:${stageId}`
    });
    
    const rounds = roundsData.data || [];
    
    // Organise fixtures by round
    const fixturesByRound = rounds.reduce((acc, round) => {
      acc[round.name] = {
        roundInfo: {
          id: round.id,
          name: round.name,
          starting_at: round.starting_at,
          ending_at: round.ending_at,
          is_current: round.is_current,
          finished: round.finished
        },
        fixtures: round.fixtures || []
      };
      return acc;
    }, {});
    
    return fixturesByRound;
  } catch (error) {
    console.error('Failed to organise by round:', error);
    throw error;
  }
}

 

Step 5: Enriching Fixture Data

To display meaningful information, include team details and match states:

async function getEnrichedFixtures(seasonId) {
  try {
    const data = await makeAPIRequest(`/seasons/${seasonId}`, {
      include: 'fixtures.participants;fixtures.state;fixtures.venue'
    });
    
    const fixtures = data.data.fixtures || [];
    
    // Process each fixture
    const enrichedFixtures = fixtures.map(fixture => {
      const participants = fixture.participants || [];
      const homeTeam = participants.find(p => p.meta?.location === 'home');
      const awayTeam = participants.find(p => p.meta?.location === 'away');
      
      return {
        id: fixture.id,
        matchName: fixture.name,
        kickoffTime: fixture.starting_at,
        timestamp: fixture.starting_at_timestamp,
        homeTeam: {
          id: homeTeam?.id,
          name: homeTeam?.name,
          logo: homeTeam?.image_path
        },
        awayTeam: {
          id: awayTeam?.id,
          name: awayTeam?.name,
          logo: awayTeam?.image_path
        },
        venue: fixture.venue?.name,
        state: fixture.state?.name,
        stageId: fixture.stage_id,
        groupId: fixture.group_id,
        roundId: fixture.round_id,
        matchDetails: fixture.details
      };
    });
    
    return enrichedFixtures;
  } catch (error) {
    console.error('Failed to enrich fixtures:', error);
    throw error;
  }
}

 

Step 6: Building a Complete Schedule View

Now let’s combine everything into a comprehensive schedule display:

class WorldCupSchedule {
  constructor(apiToken) {
    this.apiToken = apiToken;
    this.leagueId = null;
    this.seasonId = null;
    this.stages = {};
    this.fixtures = [];
  }
  
  async initialize() {
    // Find World Cup and 2026 season
    const league = await this.findWorldCupLeague();
    this.leagueId = league.id;
    
    const season = await this.find2026Season(league.id);
    this.seasonId = season.id;
    
    // Load all data
    await this.loadStages();
    await this.loadFixtures();
    
    return this;
  }
  
  async findWorldCupLeague() {
    const data = await makeAPIRequest('/leagues/search/World Cup', {
      include: 'seasons'
    });
    
    return data.data.find(l => 
      l.name.toLowerCase() === 'fifa world cup'
    );
  }
  
  async find2026Season(leagueId) {
    const data = await makeAPIRequest(`/leagues/${leagueId}`, {
      include: 'seasons'
    });
    
    return data.data.seasons.find(s => s.name.includes('2026'));
  }
  
  async loadStages() {
    const data = await makeAPIRequest(`/stages/seasons/${this.seasonId}`, {
      include: 'groups;rounds'
    });
    
    const stages = data.data || [];
    
    // Organise stages by type
    this.stages = {
      groupStage: stages.find(s => s.type_id === 223),
      roundOf32: stages.find(s => s.name.includes('Round of 32')),
      roundOf16: stages.find(s => s.name.includes('Round of 16') || s.name.includes('8th Finals')),
      quarterFinals: stages.find(s => s.name.includes('Quarter')),
      semiFinals: stages.find(s => s.name.includes('Semi')),
      final: stages.find(s => s.name === 'Final'),
      allStages: stages
    };
  }
  
  async loadFixtures() {
    const data = await makeAPIRequest(`/seasons/${this.seasonId}`, {
      include: 'fixtures.participants;fixtures.state;fixtures.venue'
    });
    
    this.fixtures = data.data.fixtures || [];
  }
  
  getGroupStageFixtures() {
    if (!this.stages.groupStage) return {};
    
    const groupStageId = this.stages.groupStage.id;
    const groupFixtures = this.fixtures.filter(f => f.stage_id === groupStageId);
    
    // Organise by group
    const byGroup = {};
    groupFixtures.forEach(fixture => {
      const groupId = fixture.group_id;
      if (!groupId) return;
      
      if (!byGroup[groupId]) {
        byGroup[groupId] = [];
      }
      byGroup[groupId].push(fixture);
    });
    
    return byGroup;
  }
  
  getKnockoutFixtures(stageName) {
    const stage = this.stages[stageName];
    if (!stage) return [];
    
    return this.fixtures
      .filter(f => f.stage_id === stage.id)
      .sort((a, b) => a.starting_at_timestamp - b.starting_at_timestamp);
  }
  
  getFixturesByDate(startDate, endDate) {
    const start = new Date(startDate).getTime() / 1000;
    const end = new Date(endDate).getTime() / 1000;
    
    return this.fixtures.filter(f => 
      f.starting_at_timestamp >= start && 
      f.starting_at_timestamp <= end
    );
  }
  
  getTodaysMatches() {
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const todayTimestamp = today.getTime() / 1000;
    
    const tomorrow = new Date(today);
    tomorrow.setDate(tomorrow.getDate() + 1);
    const tomorrowTimestamp = tomorrow.getTime() / 1000;
    
    return this.fixtures.filter(f => 
      f.starting_at_timestamp >= todayTimestamp && 
      f.starting_at_timestamp < tomorrowTimestamp
    );
  }
}

// Usage
const schedule = new WorldCupSchedule('YOUR_API_TOKEN');
await schedule.initialize();

// Get all group stage fixtures
const groupFixtures = schedule.getGroupStageFixtures();
console.log('Group A fixtures:', groupFixtures[groupAId]);

// Get knockout stage fixtures
const quarterFinals = schedule.getKnockoutFixtures('quarterFinals');
console.log('Quarter-finals:', quarterFinals);

// Get today's matches
const todaysMatches = schedule.getTodaysMatches();
console.log('Today\'s matches:', todaysMatches);

 

Step 7: Displaying the Schedule

Rendering Group Stage Matches

function renderGroupStageSchedule(groupFixtures, groups) {
  const html = Object.keys(groupFixtures).map(groupId => {
    const fixtures = groupFixtures[groupId];
    const group = groups.find(g => g.id === groupId);
    
    return `
      <div class="group-schedule">
        <h3>${group.name}</h3>
        <div class="fixtures">
          ${fixtures.map(fixture => renderFixture(fixture)).join('')}
        </div>
      </div>
    `;
  }).join('');
  
  return html;
}

function renderFixture(fixture) {
  const participants = fixture.participants || [];
  const home = participants.find(p => p.meta?.location === 'home');
  const away = participants.find(p => p.meta?.location === 'away');
  
  const kickoffTime = new Date(fixture.starting_at_timestamp * 1000)
    .toLocaleString('en-US', {
      month: 'short',
      day: 'numeric',
      hour: '2-digit',
      minute: '2-digit'
    });
  
  return `
    <div class="fixture-card">
      <div class="fixture-time">${kickoffTime}</div>
      <div class="fixture-teams">
        <div class="team">
          <img src="${home?.image_path}" alt="${home?.name}">
          <span>${home?.name}</span>
        </div>
        <div class="vs">vs</div>
        <div class="team">
          <img src="${away?.image_path}" alt="${away?.name}">
          <span>${away?.name}</span>
        </div>
      </div>
      <div class="fixture-venue">${fixture.venue?.name}</div>
    </div>
  `;
}

 

Creating a Match Calendar

function createMatchCalendar(fixtures) {
  // Organise by date
  const byDate = {};
  
  fixtures.forEach(fixture => {
    const date = new Date(fixture.starting_at_timestamp * 1000)
      .toISOString()
      .split('T')[0];
    
    if (!byDate[date]) {
      byDate[date] = [];
    }
    byDate[date].push(fixture);
  });
  
  // Render calendar
  const sortedDates = Object.keys(byDate).sort();
  
  return sortedDates.map(date => {
    const dateObj = new Date(date);
    const dateLabel = dateObj.toLocaleDateString('en-US', {
      weekday: 'long',
      month: 'long',
      day: 'numeric'
    });
    
    return `
      <div class="calendar-day">
        <h3>${dateLabel}</h3>
        <div class="day-fixtures">
          ${byDate[date]
            .sort((a, b) => a.starting_at_timestamp - b.starting_at_timestamp)
            .map(f => renderFixture(f))
            .join('')}
        </div>
      </div>
    `;
  }).join('');
}

 

Alternative Approach: Using Fixtures by Date Range

For a more targeted approach, you can request fixtures for specific date ranges:

async function getFixturesForDateRange(startDate, endDate, leagueId) {
  try {
    const data = await makeAPIRequest(`/fixtures/between/${startDate}/${endDate}`, {
      include: 'participants,state,venue',
      filters: `fixtureLeagues:${leagueId}`
    });
    
    return data.data || [];
  } catch (error) {
    console.error('Failed to fetch fixtures by date:', error);
    throw error;
  }
}

// Example: Get opening week fixtures
const openingWeek = await getFixturesForDateRange(
  '2026-06-11',  // Tournament starts
  '2026-06-18',  // One week later
  worldCupLeagueId
);

When to use this approach:
– You only need fixtures for specific time periods.
– You’re building a “this week’s matches” feature.
– You want to reduce payload size by not loading the entire tournament.

Handling Placeholder Fixtures

The World Cup 2026 will have many fixtures with placeholder team names (e.g., “Winner Group A vs Runner-up Group B”) until the group stage completes.

function isPlaceholderFixture(fixture) {
  // Check if fixture has placeholder flag
  if (fixture.placeholder === true) {
    return true;
  }
  
  // Check if team names contain placeholders
  const participants = fixture.participants || [];
  const hasPlaceholder = participants.some(p => 
    p.name.includes('Winner') ||
    p.name.includes('Runner-up') ||
    p.name.includes('1st') ||
    p.name.includes('2nd') ||
    p.name.includes('3rd')
  );
  
  return hasPlaceholder;
}

function renderFixtureWithPlaceholders(fixture) {
  const participants = fixture.participants || [];
  const home = participants[0];
  const away = participants[1];
  
  if (isPlaceholderFixture(fixture)) {
    return `
      <div class="fixture-card placeholder">
        <div class="placeholder-text">
          ${fixture.name}
        </div>
        <div class="fixture-details">
          ${fixture.details || ''}
        </div>
        <div class="fixture-time">
          ${new Date(fixture.starting_at_timestamp * 1000).toLocaleString()}
        </div>
      </div>
    `;
  }
  
  return renderFixture(fixture);
}

 

Best Practices

1. Cache Season and Stage Data

This data rarely changes, so cache it to reduce API calls:

const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours

const cache = {
  season: null,
  stages: null,
  lastUpdated: null
};

async function getCachedSeasonData(seasonId) {
  const now = Date.now();
  
  if (cache.season && cache.lastUpdated && (now - cache.lastUpdated < CACHE_TTL)) {
    return cache.season;
  }
  
  const data = await makeAPIRequest(`/seasons/${seasonId}`, {
    include: 'stages.groups,stages.rounds'
  });
  
  cache.season = data.data;
  cache.stages = data.data.stages;
  cache.lastUpdated = now;
  
  return cache.season;
}

2. Handle Timezone Conversions

The API returns times in the tournament’s local timezone. Convert to the user’s timezone:

function convertToUserTimezone(timestamp) {
  const date = new Date(timestamp * 1000);
  return date.toLocaleString(undefined, {
    timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    month: 'short',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    timeZoneName: 'short'
  });
}

3. Filter Efficiently

Use API filters instead of filtering client-side:

// Good: Filter on the API
const groupAFixtures = await makeAPIRequest('/fixtures', {
  include: 'participants',
  filters: `fixtureLeagues:${leagueId};fixtureGroups:${groupAId}`
});

// Less efficient: Get all fixtures, then filter client-side
const allFixtures = await makeAPIRequest(`/seasons/${seasonId}`, {
  include: 'fixtures.participants'
});
const groupAFixtures = allFixtures.fixtures.filter(f => f.group_id === groupAId);

4. Handle Missing Data Gracefully

Not all data may be available until closer to the tournament:

function safelyGetFixtureData(fixture) {
  return {
    id: fixture.id,
    name: fixture.name || 'TBD',
    kickoff: fixture.starting_at || 'Date TBD',
    venue: fixture.venue?.name || 'Venue TBD',
    homeTeam: fixture.participants?.[0]?.name || 'TBD',
    awayTeam: fixture.participants?.[1]?.name || 'TBD',
    isPlaceholder: fixture.placeholder || false
  };
}

 

Putting It All Together: Complete Example

Here’s a complete working example that ties everything together:

class WorldCup2026App {
  constructor(apiToken) {
    this.apiToken = apiToken;
    this.config = {
      leagueId: null,
      seasonId: null
    };
    this.data = {
      stages: [],
      fixtures: [],
      groups: []
    };
  }
  
  async initialize() {
    console.log('Initialising World Cup 2026 data...');
    
    // Find league and season
    await this.findWorldCup();
    
    // Load all necessary data
    await Promise.all([
      this.loadStages(),
      this.loadFixtures()
    ]);
    
    console.log('Initialisation complete!');
    return this;
  }
  
  async findWorldCup() {
    const leagueData = await makeAPIRequest('/leagues/search/World Cup', {
      include: 'seasons'
    });
    
    const league = leagueData.data.find(l => 
      l.name.toLowerCase().includes('world cup')
    );
    
    if (!league) throw new Error('World Cup not found');
    
    this.config.leagueId = league.id;
    
    const season2026 = league.seasons.find(s => s.name.includes('2026'));
    if (!season2026) throw new Error('2026 season not found');
    
    this.config.seasonId = season2026.id;
  }
  
  async loadStages() {
    const data = await makeAPIRequest(
      `/stages/seasons/${this.config.seasonId}`,
      { include: 'groups,rounds' }
    );
    
    this.data.stages = data.data || [];
    
    // Extract groups from the group stage
    const groupStage = this.data.stages.find(s => s.type_id === 223);
    if (groupStage?.groups) {
      this.data.groups = groupStage.groups;
    }
  }
  
  async loadFixtures() {
    const data = await makeAPIRequest(
      `/seasons/${this.config.seasonId}`,
      { include: 'fixtures.participants,fixtures.state,fixtures.venue' }
    );
    
    this.data.fixtures = data.data.fixtures || [];
  }
  
  // Public methods for accessing data
  
  getGroupStageSchedule() {
    const groupStage = this.data.stages.find(s => s.type_id === 223);
    if (!groupStage) return null;
    
    const fixtures = this.data.fixtures.filter(
      f => f.stage_id === groupStage.id
    );
    
    // Organise by group
    const byGroup = {};
    fixtures.forEach(fixture => {
      const groupId = fixture.group_id;
      if (!groupId) return;
      
      if (!byGroup[groupId]) {
        byGroup[groupId] = {
          groupInfo: this.data.groups.find(g => g.id === groupId),
          fixtures: []
        };
      }
      byGroup[groupId].fixtures.push(fixture);
    });
    
    return byGroup;
  }
  
  getKnockoutSchedule() {
    const knockoutStages = this.data.stages.filter(s => s.type_id === 224);
    
    return knockoutStages.map(stage => ({
      stage: stage.name,
      fixtures: this.data.fixtures.filter(f => f.stage_id === stage.id)
    }));
  }
  
  getMatchesByDate(date) {
    const targetDate = date.toISOString().split('T')[0];
    
    return this.data.fixtures.filter(fixture => {
      if (!fixture.starting_at) return false;
      const fixtureDate = fixture.starting_at.split(' ')[0];
      return fixtureDate === targetDate;
    });
  }
  
  getMatchById(fixtureId) {
    return this.data.fixtures.find(f => f.id === fixtureId);
  }
}

// Usage
async function main() {
  const app = new WorldCup2026App('YOUR_API_TOKEN');
  await app.initialize();
  
  // Get group stage schedule
  const groupSchedule = app.getGroupStageSchedule();
  console.log('Groups:', Object.keys(groupSchedule).length);
  
  // Get knockout schedule
  const knockoutSchedule = app.getKnockoutSchedule();
  console.log('Knockout stages:', knockoutSchedule.length);
  
  // Get matches for opening day
  const openingDay = new Date('2026-06-11');
  const openingMatches = app.getMatchesByDate(openingDay);
  console.log('Opening day matches:', openingMatches.length);
}

main().catch(console.error);

 

Conclusion

You now have a complete understanding of how to retrieve and organise World Cup 2026 fixtures using the Sportmonks Football API. The key takeaways are:

  1. Hierarchical Structure: The API uses League → Season → Stage → Group/Round → Fixture hierarchy
  2. Multiple Approaches: You can use seasons/{id}?include=fixtures or the schedules endpoint
  3. Stage Types: Different stage types (223 for groups, 224 for knockouts) require different handling
  4. Enrichment: Always include participants, state, and venue for meaningful displays
  5. Placeholders: Handle TBD fixtures gracefully until team qualifications are confirmed

With this foundation, you can build comprehensive World Cup schedule features, including:
– Full tournament calendars
– Group-by-group schedules
– Knockout bracket visualisations
– Match reminders and notifications.
– Personalised team schedules

The methodology taught here applies to any tournament structure in the Sportmonks API, so you can use these same patterns for other competitions, such as the Euros, Champions League, or Copa América.

Resources

FAQs

How many teams and groups will there be in the 2026 FIFA World Cup?
The 2026 World Cup will have 48 teams divided into 12 groups (Groups A to L).
Where will the World Cup 2026 matches be played?
Matches will take place across stadiums in the United States, Canada, and Mexico.
What is the format for advancing from the group stage?
The top two teams from each group, plus the eight best third-place teams, advance to the Round of 32.
How do I access the World Cup 2026 schedule using the Sportmonks API?
Use the Sportmonks API to retrieve the season and fixtures for the World Cup 2026. You’ll need an API token and the correct league/season IDs.

Written by David Jaja

David Jaja is a technical content manager at Sportmonks, where he makes complex football data easier to understand for developers and businesses. With a background in frontend development and technical writing, he helps bridge the gap between technology and sports data. Through clear, insightful content, he ensures Sportmonks' APIs are accessible and easy to use, empowering developers to build standout football applications