Skip to main content
Working code for the most common V1 integrations. Drop in your x-api-key and ship.

1. Live score ticker (WebSocket)

const KEY = 'YOUR_API_KEY';
const ws = new WebSocket(`wss://v1.football.sportsapipro.com/ws?x-api-key=${KEY}`);

ws.onopen = () => ws.send(JSON.stringify({ type: 'subscribe', channel: 'live' }));

ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  if (msg.type !== 'scores') return;
  msg.games.forEach((g) => {
    document.getElementById(`g-${g.id}`)?.replaceWith(renderGame(g));
  });
};

function renderGame(g) {
  const el = document.createElement('div');
  el.id = `g-${g.id}`;
  el.textContent = `${g.homeCompetitor.name} ${g.homeCompetitor.score} - ${g.awayCompetitor.score} ${g.awayCompetitor.name} (${g.gameTimeDisplay})`;
  return el;
}
Reconnect with exponential backoff on ws.onclose.

2. League table page (LaLiga)

const res = await fetch(
  'https://v1.football.sportsapipro.com/api/v1/competition/11/standings',
  { headers: { 'x-api-key': KEY } }
);
const { data } = await res.json();
const table = data.standings[0];

console.table(
  table.rows.map((r) => ({
    pos: r.position,
    team: r.competitor.name,
    p: r.gamePlayed, w: r.gamesWon, d: r.gamesEven, l: r.gamesLost,
    gd: r.ratio, pts: r.points,
  }))
);

3. Match page (fetch in parallel)

async function loadMatch(id) {
  const opts = { headers: { 'x-api-key': KEY } };
  const base = 'https://v1.football.sportsapipro.com/api/v1/game';
  const [game, events, lineups, stats, stages] = await Promise.all([
    fetch(`${base}/${id}`,         opts).then(r => r.json()),
    fetch(`${base}/${id}/events`,  opts).then(r => r.json()),
    fetch(`${base}/${id}/lineups`, opts).then(r => r.json()),
    fetch(`${base}/${id}/stats`,   opts).then(r => r.json()),
    fetch(`${base}/${id}/stages`,  opts).then(r => r.json()),
  ]);
  return { game: game.data.game, events: events.data.events, lineups: lineups.data, stats: stats.data, stages: stages.data.stages };
}
Tip: only fetch children where the parent’s has* flag is true.

4. Pre-match H2H widget

const [h2h, homeForm, awayForm] = await Promise.all([
  fetch(`https://v1.football.sportsapipro.com/api/v1/h2h/${home}/${away}`, opts).then(r => r.json()),
  fetch(`https://v1.football.sportsapipro.com/api/v1/competitor/${home}/results?pageSize=5`, opts).then(r => r.json()),
  fetch(`https://v1.football.sportsapipro.com/api/v1/competitor/${away}/results?pageSize=5`, opts).then(r => r.json()),
]);

5. News feed with infinite scroll

let page = 1;
async function loadMore() {
  const r = await fetch(
    `https://v1.football.sportsapipro.com/api/v1/news?sportId=1&page=${page}`,
    { headers: { 'x-api-key': KEY } }
  );
  const { data } = await r.json();
  render(data.news);
  if (data.news.length) page++;
}

6. Polling without churn (use lastUpdateId)

let cursor = -1;
setInterval(async () => {
  const r = await fetch(
    `https://v1.football.sportsapipro.com/api/v1/football/live?lastUpdateId=${cursor}`,
    { headers: { 'x-api-key': KEY } }
  );
  const { data } = await r.json();
  if (data.lastUpdateId === cursor) return; // nothing new
  cursor = data.lastUpdateId;
  render(data.games);
}, 2000);

7. Per-sport quick reference

SportLive endpointNotes
Football/api/v1/football/live
Basketball/api/v1/basketball/liveQuarters in stages[].
Tennis/api/v1/tennis/liveSets in stages[].
Hockey/api/v1/hockey/livePeriods in stages[].
Handball/api/v1/handball/live
A. Football/api/v1/american-football/liveQuarters in stages[].
Baseball/api/v1/baseball/allUse /all, filter statusGroup === 3.
Volleyball/api/v1/volleyball/liveSets in stages[].
Rugby/api/v1/rugby/live
Last modified on June 12, 2026