Skip to main content

Connection

wss://v2.football.sportsapipro.com/ws?x-api-key=YOUR_API_KEY

Authentication

Pass your API key via:
  • Query string (recommended for browser clients): ?x-api-key=YOUR_API_KEY
  • Header: x-api-key: YOUR_API_KEY

Sport Selection

Optionally set the x-sport header during connection to scope live-scores to a specific sport (default: football). Supported values: football, basketball, ice-hockey, tennis, rugby, handball, volleyball, baseball, american-football, cricket, futsal, mma, motorsport, darts, snooker, badminton, table-tennis, beach-volley, cycling, esports, bandy, aussie-rules, floorball, minifootball, waterpolo.

Message Format

All messages are JSON. Send actions to subscribe/unsubscribe from channels:
{ "action": "subscribe", "channel": "live-scores" }
{ "action": "subscribe", "channel": "match:14109842" }
{ "action": "subscribe", "channel": "match:14109842:incidents" }
{ "action": "unsubscribe", "channel": "match:14109842" }
{ "action": "ping", "channel": "live-scores" }

Available Channels

ChannelDescriptionDelivery
live-scoresAll live match scores & status changes (auto-scoped to your x-sport)NATS push — sub-second
live-scores:{sport}Live scores for a specific sport (e.g. live-scores:basketball)NATS push — sub-second
match:{matchId}Score & status updates for a single matchNATS push — sub-second
match:{matchId}:incidentsGoals, cards, substitutions, VAR, period markersNATS-triggered fetch (~300ms) + 15s safety-net poll
match:{matchId}:statsMatch statistics (possession, shots, passes, fouls, corners)NATS-triggered fetch (~300ms) + 30s safety-net poll
match:{matchId}:oddsLive odds from all providersNATS-triggered fetch — sub-second
match:{matchId}:lineupsTeam lineups (typically available pre-match)Polling — 60s interval

Delivery Modes

  • NATS push — Data is pushed directly from the real-time feed with no delay
  • NATS-triggered fetch — When any match update is detected, detailed data is fetched instantly (no waiting for poll cycle)
  • Polling — Data is fetched at regular intervals (used for lineups which rarely change)

What’s delivered on :incidents

The match:{matchId}:incidents channel covers every discrete event the live feed emits:
IncidentDeliveredDetail
goalYesScorer, assist, minute
card (yellow/red)YesPlayer, minute, incidentClass: "yellow"|"red", reason (e.g. "Foul")
substitutionYesPlayer in/out, minute
varDecisionYesDecision type, minute
Injury time / period markersYesAdded time and phase transitions
Generic fouls (no card)NoNot tracked as discrete incidents
Non-carded fouls are not emitted as incidents. If you need foul counts, subscribe to match:{matchId}:stats — it carries the aggregate per-team foul count, updated in real time. There is no per-player foul stream.
The welcome payload surfaces "natsConnected": true while the NATS bridge is healthy. If it ever reports false, channels fall back to their safety-net poll cadence (15s for :incidents, 30s for :stats) — your code keeps working without changes.

Response Types

welcome

Sent immediately on connection:
{
  "type": "welcome",
  "message": "SportsAPIData WebSocket v3.1 — All Channels Sub-Second",
  "sport": "football",
  "natsConnected": true,
  "channels": ["live-scores", "match:{matchId}", "..."]
}

subscribed

Confirms a channel subscription:
{
  "type": "subscribed",
  "channel": "match:14109842:incidents",
  "mode": "realtime (NATS-triggered fetch, 15000ms safety-net poll)"
}

snapshot

Last known data sent immediately upon subscribing (if available):
{
  "channel": "match:14109842",
  "type": "snapshot",
  "data": { ... },
  "timestamp": 1710729600000
}

update

New data pushed in real-time:
{
  "channel": "match:14109842:incidents",
  "type": "update",
  "data": { ... },
  "source": "nats",
  "timestamp": 1710729600437
}

pong

Response to a ping (for keep-alive):
{
  "type": "pong",
  "channel": "live-scores",
  "timestamp": 1710729600000
}

Quick Start Examples

const ws = new WebSocket('wss://v2.football.sportsapipro.com/ws?x-api-key=YOUR_API_KEY');

ws.onopen = () => {
  // Subscribe to all live football scores
  ws.send(JSON.stringify({ action: 'subscribe', channel: 'live-scores' }));

  // Subscribe to a specific match's incidents (goals, cards, subs)
  ws.send(JSON.stringify({ action: 'subscribe', channel: 'match:14109842:incidents' }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  console.log(`[${msg.type}] ${msg.channel}:`, msg.data);
};

Best Practices

Each subscription consumes server resources. Only subscribe to channels your application actively uses.
Use match:{id}:incidents for real-time match events rather than polling the REST endpoint.
When you subscribe, you immediately get the last known state so your UI is never blank.
Send {"action":"ping","channel":"live-scores"} every 30s to keep the connection alive.
If disconnected, reconnect with exponential backoff (1s, 2s, 4s, 8s…).
Unsubscribe from match channels when the user navigates away.
Last modified on June 12, 2026