Live scores on the screens in your venue

Sports bars, restaurants, gyms, arenas and signage companies.

Start with the free widget in a browser kiosk. For a branded multi-league board, run one small server that holds a WebSocket and serves a page every screen loads, so a whole venue uses one connection and one key.

Updated 2026-11-04. Data is aggregated from public sources and is typically 20-30 seconds behind live play (about 1 second after our source). There is no SLA.

Fastest option: the free widget

Open a browser in kiosk mode on the screen and load a page with the free scoreboard widget: one league per widget, no key, refreshes every 60 seconds. Put several widgets on one page for a multi-league board.

HTML for a two-league board

<div class="rsa-scoreboard" data-league="nfl"></div>
<div class="rsa-scoreboard" data-league="college-football"></div>
<script src="https://www.realtimesportsapi.com/widget.js" async></script>

A branded board from one connection

For your own design and logo, run a small Node server (a Raspberry Pi or any cheap VPS works). It loads the scoreboard once, keeps one WebSocket open for score changes and finals, and serves the current board as JSON to every screen on your network. Screens never see your key.

board-server.js

import http from 'node:http';
const LEAGUES = [['football', 'nfl'], ['hockey', 'nhl']];
const H = { Authorization: `Bearer ${process.env.RSA_API_KEY}` };
const board = new Map(); // eventId -> game

async function loadBoards() {
  for (const [sport, league] of LEAGUES) {
    const { data } = await (await fetch(`https://www.realtimesportsapi.com/api/v1/sports/${sport}/leagues/${league}/events`, { headers: H })).json();
    for (const e of data) board.set(e.id, { league, name: e.shortName, home: e.homeTeam, away: e.awayTeam, status: e.status });
  }
}

async function stream() {
  const { data } = await (await fetch('https://www.realtimesportsapi.com/api/websocket/auth', { method: 'POST', headers: H })).json();
  const ws = new WebSocket(`${data.url}?token=${data.token}`);
  ws.onopen = () => {
    for (const [sport, league] of LEAGUES)
      for (const event of ['event_score_change', 'event_live', 'event_final'])
        ws.send(JSON.stringify({ type: 'subscribe', event, filters: { sport, league } }));
  };
  ws.onmessage = (m) => {
    const { type, data: d } = JSON.parse(m.data);
    const g = d && board.get(d.eventId);
    if (!g || !type.startsWith('event_')) return;
    if (d.homeTeam) g.home.score = d.homeTeam.score;
    if (d.awayTeam) g.away.score = d.awayTeam.score;
    if (type === 'event_final') g.status = { ...g.status, state: 'post', detail: 'Final' };
  };
  ws.onclose = () => setTimeout(stream, 5000); // tokens last 1 hour; reconnect with a new one
}

await loadBoards();
setInterval(loadBoards, 15 * 60 * 1000); // pick up new games and clocks
stream();
http.createServer((_q, r) => { r.setHeader('content-type', 'application/json'); r.end(JSON.stringify([...board.values()])); }).listen(8080);

What it costs

Each WebSocket message counts once. A night with eight NFL or NHL games produces a few hundred score-change, live and final messages, plus the scoreboard refreshes. Clock updates are optional: subscribe to event_status_change with frequency: "1m" if you want the clock to tick.

Endpoints used

  • GET /sports/{sport}/leagues/{league}/events
  • POST /websocket/auth

Full reference: docs · OpenAPI · what each endpoint returns, by league

Plans

PlanPriceCallsRateWebSocketWebhooks
Free$0125/mo (1,000 first 30 days)1/s500 msgs/moNo
Starter$29/mo ($290/yr)10,000/mo5/sShares monthly poolShares monthly pool
Growth$49/mo ($490/yr)25,000/mo10/sShares monthly poolShares monthly pool
Pro$99/mo ($990/yr)50,000/mo20/s100,000 msgs/mo25,000/mo
Business$179/mo ($1,790/yr)150,000/mo40/s300,000 msgs/mo75,000/mo
Scale$299/mo ($2,990/yr)500,000/mo100/s500,000 msgs/mo100,000/mo

Every successful REST call counts as one call. Over the limit you get HTTP 429 with a Retry-After header. No SLA. Full pricing.

FAQ

Is the widget really free for a business?
Yes. It needs no account and no key. It shows one league per widget and refreshes every 60 seconds.
Will the scores be ahead of the TV?
No. Data is typically 20-30 seconds behind live play, so the board updates shortly after the broadcast.
How many screens can one account drive?
Any number, if the screens read from your own server as above. The concurrent connection limit (Starter 2, Growth 3, Pro 5) counts WebSocket connections to the API, not screens.
Can I show odds on the board?
Odds are available for some leagues, but this page does not cover wagering displays; check local rules before showing them.

Complete builds to start from

Other use cases