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}/eventsPOST /websocket/auth
Full reference: docs · OpenAPI · what each endpoint returns, by league
Plans
| Plan | Price | Calls | Rate | WebSocket | Webhooks |
|---|---|---|---|---|---|
| Free | $0 | 125/mo (1,000 first 30 days) | 1/s | 500 msgs/mo | No |
| Starter | $29/mo ($290/yr) | 10,000/mo | 5/s | Shares monthly pool | Shares monthly pool |
| Growth | $49/mo ($490/yr) | 25,000/mo | 10/s | Shares monthly pool | Shares monthly pool |
| Pro | $99/mo ($990/yr) | 50,000/mo | 20/s | 100,000 msgs/mo | 25,000/mo |
| Business | $179/mo ($1,790/yr) | 150,000/mo | 40/s | 300,000 msgs/mo | 75,000/mo |
| Scale | $299/mo ($2,990/yr) | 500,000/mo | 100/s | 500,000 msgs/mo | 100,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.