Examples · React and Vite

Build a Live Sports Scoreboard With React

This tutorial builds a scoreboard with today's games for each league. It refreshes every minute while a game is in progress and every 10 minutes otherwise, so it stays current without wasting calls.

Cost: 2 credits per refresh. The free plan includes 1,000 credits a month. Full project on GitHub.

1. Keep the key off the browser

Anything in browser code is public. The Vite config forwards /api requests to MoneyLine and adds your key on the dev server. In production, do the same from your own backend or an edge function.

2. Load today's games

/v1/events/today returns each game's status, period, clock and score. The useScores hook loads it for the chosen league and sorts games by start time.

3. Refresh faster while games are live

After each load, the hook checks whether any game is in progress. If so it refreshes in a minute, and if not in 10 minutes. Each refresh costs 2 credits.

The code

live-scoreboard/src/App.jsx, straight from the repository.

src/App.jsx
import { useEffect, useState } from 'react'

const LEAGUES = { nfl: 'NFL', mlb: 'MLB', nba: 'NBA', nhl: 'NHL', ncaa_football: 'College Football', soccer_mls: 'MLS' }
// Each refresh costs 2 credits: check every minute while a game is live, every 10 minutes otherwise.
const LIVE_MS = 60 * 1000
const IDLE_MS = 10 * 60 * 1000

async function todaysGames(league) {
  const res = await fetch(`/api/v1/events/today?league=${league}`)
  const body = await res.json()
  if (!body.success) throw new Error(body.error?.message || `HTTP ${res.status}`)
  return body.data.sort((a, b) => a.startTime.localeCompare(b.startTime))
}

function useScores(league) {
  const [state, setState] = useState({ games: [], error: null, updatedAt: null })
  useEffect(() => {
    let timer
    let cancelled = false
    const load = async () => {
      try {
        const games = await todaysGames(league)
        if (cancelled) return
        setState({ games, error: null, updatedAt: new Date() })
        timer = setTimeout(load, games.some((g) => g.status === 'in_progress') ? LIVE_MS : IDLE_MS)
      } catch (err) {
        if (cancelled) return
        setState((s) => ({ ...s, error: err.message }))
        timer = setTimeout(load, IDLE_MS)
      }
    }
    load()
    return () => { cancelled = true; clearTimeout(timer) }
  }, [league])
  return state
}

function status(game) {
  if (game.status === 'in_progress') return [game.period, game.clock].filter(Boolean).join(' · ') || 'Live'
  if (game.status === 'final') return 'Final'
  return new Date(game.startTime).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })
}

function Game({ game }) {
  const started = game.status !== 'scheduled'
  const row = (team, score, won) => (
    <div className={`team${won ? ' won' : ''}`}>
      <span>{team}</span>
      <span className="score">{started ? score : ''}</span>
    </div>
  )
  const { home, away } = game.scores ?? {}
  return (
    <div className={`game ${game.status}`}>
      <div className="status">{status(game)}</div>
      {row(game.awayTeamName, away, game.status === 'final' && away > home)}
      {row(game.homeTeamName, home, game.status === 'final' && home > away)}
    </div>
  )
}

export default function App() {
  const [league, setLeague] = useState('mlb')
  const { games, error, updatedAt } = useScores(league)
  return (
    <main>
      <h1>Today&apos;s scores</h1>
      <nav>
        {Object.entries(LEAGUES).map(([id, name]) => (
          <button key={id} onClick={() => setLeague(id)} aria-pressed={id === league}>{name}</button>
        ))}
      </nav>
      {error && <p className="error">Couldn&apos;t load scores: {error}</p>}
      {!error && updatedAt && games.length === 0 && <p>No {LEAGUES[league]} games today.</p>}
      <div className="grid">{games.map((g) => <Game key={g.eventId} game={g} />)}</div>
      {updatedAt && <p className="muted">Updated {updatedAt.toLocaleTimeString()} · Scores from MoneyLine Sports API</p>}
    </main>
  )
}

Run it

Get a free API key, clone the example, then run:

Terminal
npm install
echo "MONEYLINE_API_KEY=your-key" > .env.local
npm run dev

More examples