← Back to tutorials

Tutorial

Build a league fixtures dashboard in JavaScript

Fetch the next ten fixtures for an English league and display match winner probabilities in a sortable table.

Published on October 29, 2025 · 8 min read
Table UI showing upcoming fixtures and probability columns.

Build a lightweight dashboard that lets scouts or traders pick a British league and instantly see the next ten fixtures with match winner probabilities.

Step 1 — Prepare the markup

Start with a minimal index.html that declares the selector, status message, and fixtures table.

index.html
<!DOCTYPE html><html lang="en">  <head>    <meta charset="utf-8" />    <title>GameForecastAPI fixtures</title>  </head>  <body class="dashboard">    <main class="card">      <h1 class="card__title">Upcoming fixtures</h1>      <label class="card__control">        Choose a league        <select id="league-select"></select>      </label>      <p id="status" class="status"></p>      <div class="table-wrapper">        <table>          <thead>            <tr>              <th>Home</th>              <th>Away</th>              <th>Kickoff</th>              <th>Home %</th>              <th>Draw %</th>              <th>Away %</th>            </tr>          </thead>          <tbody id="fixtures-body"></tbody>        </table>      </div>    </main>    <script type="module" src="fixtures.js"></script>  </body></html>

The dashboard template focuses on the essentials: a league selector, a status line that communicates loading/error states, and a table body that the script can hydrate with RapidAPI data.

The moment you host this file behind a CDN or a static site generator, you have a sharable scouting tool that reads straight from GameForecastAPI on RapidAPI.

Step 2 — Write the client script

The script below fetches all English leagues, lets the user pick one, and renders the upcoming fixtures with match winner probabilities. Replace YOUR_RAPIDAPI_KEY with a key from RapidAPI.

fixtures.js
const RAPID_API_HOST = "game-forecast-api.p.rapidapi.com";const COUNTRY_CODE = "GB";const RAPID_API_KEY = "YOUR_RAPIDAPI_KEY";const headers = {  "X-RapidAPI-Key": RAPID_API_KEY,  "X-RapidAPI-Host": RAPID_API_HOST,};const leagueSelect = document.getElementById("league-select");const fixturesBody = document.getElementById("fixtures-body");const status = document.getElementById("status");if (!leagueSelect || !fixturesBody || !status) {  throw new Error("Required DOM nodes are missing. Check your HTML markup.");}function setStatus(message) {  status.textContent = message;}function formatProbability(value) {  if (typeof value !== "number") {    return "-";  }  return value.toFixed(1) + "%";}function renderFixtures(fixtures) {  fixturesBody.innerHTML = "";  fixtures.forEach((event) => {    const row = document.createElement("tr");    const home = document.createElement("td");    home.textContent = event.team_home.name;    row.appendChild(home);    const away = document.createElement("td");    away.textContent = event.team_away.name;    row.appendChild(away);    const kickoff = document.createElement("td");    kickoff.textContent = new Date(event.start_at).toLocaleString();    row.appendChild(kickoff);    const probabilities =      event.predictions && event.predictions[0] && event.predictions[0].match_result;    ["home", "draw", "away"].forEach((key) => {      const cell = document.createElement("td");      const value = probabilities ? probabilities[key] : undefined;      cell.textContent = formatProbability(value);      row.appendChild(cell);    });    fixturesBody.appendChild(row);  });}async function listLeagues() {  setStatus("Loading leagues…");  try {    const url = new URL("https://" + RAPID_API_HOST + "/leagues");    url.searchParams.set("country_code", COUNTRY_CODE);    url.searchParams.set("page_size", "50");    const response = await fetch(url, { headers });    if (!response.ok) {      throw new Error("Failed to load leagues");    }    const payload = await response.json();    const leagues = payload.data || [];    leagueSelect.innerHTML = "";    leagues.forEach((league) => {      const option = document.createElement("option");      option.value = String(league.id);      option.textContent = league.name;      leagueSelect.appendChild(option);    });    if (leagues.length > 0) {      await loadFixtures(leagues[0].id);      setStatus("Showing the next 10 fixtures.");    } else {      setStatus("No leagues returned for GB.");    }  } catch (error) {    console.error(error);    setStatus("Unable to fetch leagues. Check your RapidAPI key.");  }}async function loadFixtures(leagueId) {  setStatus("Fetching fixtures…");  try {    const url = new URL("https://" + RAPID_API_HOST + "/events");    url.searchParams.set("league_id", String(leagueId));    url.searchParams.set("status_code", "NOT_STARTED");    url.searchParams.set("page_size", "10");    const response = await fetch(url, { headers });    if (!response.ok) {      throw new Error("Failed to load fixtures");    }    const payload = await response.json();    const fixtures = payload.data || [];    if (fixtures.length === 0) {      fixturesBody.innerHTML = "";      setStatus("No upcoming fixtures for this league.");      return;    }    renderFixtures(fixtures);    setStatus("Showing the next 10 fixtures.");  } catch (error) {    console.error(error);    setStatus("Unable to fetch fixtures.");  }}leagueSelect.addEventListener("change", (event) => {  const target = event.target;  if (target && target instanceof HTMLSelectElement && target.value) {    loadFixtures(target.value);  }});listLeagues();

Under the hood you are chaining two endpoints: /leagues to list competitions filtered by country_code, then /events to pull upcoming fixtures with daily refreshed predictions. The match_result array provides percentage confidence for home, draw, and away — perfect for highlighting value bets or validating trading models.

Go further by persisting event.id values in your CRM and setting alerts when probabilities swing by a given delta between two refreshes.

Step 3 — Run it locally

  1. Install a static server such as npx serve to avoid CORS issues when calling RapidAPI.
  2. Create a modest styles.css to brand the card, highlight top probabilities, and add team crests.
  3. Swap COUNTRY_CODE to unlock the 150+ supported competitions.

Deploy it with production credentials

Connect this widget to your RapidAPI application, monitor quota from the RapidAPI dashboard, and upsell premium odds history to your power users. Upgrade to the Pro plan when you need 120 req/min bursts and historical odds snapshots.
Get your RapidAPI key