Tutorial 01 / JavaScript + Node.js

Build your own
NFL scoreboard.

Start with a working sample, then connect your server to SportyApi. You’ll build game cards, inspect JSON, and handle missing data without exposing a credential.

This guide follows starter v1.0.0. Sample fixtures are illustrative. You only need an activated API key if you choose to enable live mode.

01 / Run the starter.

You’ll need Git and Node.js 22.9 or newer. The starter has no runtime dependencies and no build step.

git clone https://github.com/averyswoodshop/sportyapi-scoreboard-starter.git
cd sportyapi-scoreboard-starter
git checkout v1.0.0
npm start

Open http://localhost:3000. You should see three illustrative NFL game cards: in progress, scheduled, and final. Choose NBA or soccer and watch the response change. No account or card is required for this step.

public/app.js calls the local /api/scoreboard?sport=nfl endpoint. In sample mode, the server reads public/samples/scoreboards.json. The hosted demo’s Sample mode uses the same fixtures and rendering code.

02 / Keep the fetch on your server.

Open src/scoreboard.mjs. The handler accepts only the seven supported sport identifiers. When live mode is enabled, its upstream request looks like this:

const response = await fetch(
  `https://sportyapi.com/api/sports/${sport}/scoreboard`,
  {
    headers: { 'X-API-Key': env.SPORTYAPI_API_KEY },
    signal: AbortSignal.timeout(10000)
  }
);

This code runs on the server. The browser never receives the API key. The complete handler in the repo also checks HTTP status codes, caches responses, and handles timeouts. Use it rather than copying only this request snippet into production.

A successful SportyApi scoreboard response contains sport and a games array. For a minimal illustration:

{
  "sport": "nfl",
  "games": [
    {
      "id": "sample-nfl-1",
      "homeTeam": {
        "name": "Baltimore Ravens",
        "score": 17
      },
      "awayTeam": {
        "name": "Buffalo Bills",
        "score": 21
      },
      "status": {
        "type": "STATUS_IN_PROGRESS",
        "completed": false
      }
    }
  ]
}

The starter adds mode and fetchedAt to its local response so the UI can distinguish sample data from a live API fetch. fetchedAt is your server’s retrieval time, not a guarantee of the upstream data’s age.

03 / Turn JSON into game cards.

Open public/app.js. Each card uses DOM elements and textContent, so team names and status text are treated as text rather than HTML. This small excerpt shows the core idea:

const row = document.createElement('div');
const name = document.createElement('span');
const score = document.createElement('strong');
name.textContent = game.homeTeam?.name || 'Team unavailable';
score.textContent = String(game.homeTeam?.score ?? '—');
row.append(name, score);

The full renderer handles both teams, game status, local dates, and missing venues. Scheduled games show a dash until there is a meaningful score. Keep optional fields optional; a missing record or venue should not break the whole screen. The side-by-side JSON view makes it easier to see what a feed actually returns.

04 / Connect your own key.

Ready for real API responses? Create a key, save it securely, and activate access through billing. The 14-day trial requires a card and renews at $9 USD/month unless canceled.

Copy .env.example to .env in the starter root. PowerShell:

Copy-Item .env.example .env

Or macOS/Linux:

cp .env.example .env

Edit the local file using your own key:

SPORTYAPI_MODE=live
SPORTYAPI_API_KEY=YOUR_API_KEY
PORT=3000

Stop and restart npm start. The badge now says “Live API response” when the request succeeds. The server caches each feed for 60 seconds, so repeatedly clicking refresh does not guarantee a newer upstream response.

Never commit the filled-in .env file. Keep credentials out of public JavaScript and screenshots. The starter binds to localhost. Before exposing live mode on a public server, add authentication or rate limiting and an appropriate upstream request budget.

05 / Make failure understandable.

The starter includes the following behaviors:

  • No games: show an empty state and let the reader choose another feed.
  • Missing or inactive key: show a configuration error without echoing the credential.
  • HTTP 402: direct the developer to activate trial or Pro access.
  • HTTP 429: respect Retry-After; do not immediately retry upstream.
  • Timeout or upstream failure: show an unavailable state, with no automatic retry loop.

The 60-second cache is local to each server process or Worker isolate. It reduces traffic but does not enforce a global spending limit. There is no automatic polling in this starter.

npm test

The tests use mocked upstream responses and fake credentials. They verify sample mode, error handling, cache behavior, and secret handling without calling the paid service.

06 / Make it yours.

Change the layout in public/index.html, the game cards in public/app.js, and the styles in public/developers.css. Switch the initial sport to any supported feed. For date-based games, see the schedule endpoint reference; its status object differs from the scoreboard shape.

The repo includes an optional Cloudflare Worker entry point. Follow its README to deploy sample mode first, then protect the endpoint before enabling public live traffic.