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:
- Hierarchical Structure: The API uses League → Season → Stage → Group/Round → Fixture hierarchy
- Multiple Approaches: You can use seasons/{id}?include=fixtures or the schedules endpoint
- Stage Types: Different stage types (223 for groups, 224 for knockouts) require different handling
- Enrichment: Always include participants, state, and venue for meaningful displays
- 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.


