TheRundown
  1. Home
  2. Blog
  3. Developer guide

Developer guide

Read sports scores, status and game clocks in Python

Fetch NBA and NFL scores without odds. A runnable Python example preserves zero scores, matches teams by ID and separates game status from the clock.

· 4 min read

A scoreboard needs more than two numbers. It needs the right home and away teams, a status that distinguishes a scheduled game from a final, and a way to represent missing data without erasing a real zero. This guide fetches a response from TheRundown’s sports scores API and turns each event into a small Python dictionary suitable for a scoreboard or results table.

The examples use NBA regular season (sport_id=4) and NFL regular season (sport_id=2). Select other leagues and season types from the sports reference; coverage and available fields vary by sport and event.

Request scores without sportsbook prices

Scores are part of the score object on an event response. There is no separate scores endpoint. Pass affiliate_ids=0 on the dated-event endpoint to return no market or price entries:

curl --fail-with-body \
  -H "X-TheRundown-Key: $THERUNDOWN_API_KEY" \
  "https://therundown.io/api/v2/sports/4/events/2026-10-29?affiliate_ids=0"

Replace the sport ID and date with the slate you need. If you already have an event ID, use /api/v2/events/{eventID}?affiliate_ids=0 instead. Store your key in an environment variable and send it in the header.

This filter works for upcoming and completed games. Leaving out market_ids requests the default odds markets; it does not make a request scores only. The scores-only response still uses data points: two per event without live_game_state, or three when that additional field is returned. Read the response’s X-Datapoints header and the scores and results documentation for the billing details and access requirements.

A runnable Python scores client

Install requests with python -m pip install requests, save this as scores.py, and run python scores.py 4 2026-10-29. It makes one request, checks the HTTP result and prints one JSON row per event.

import json
import os
import sys
from datetime import date

import requests


def score_row(event):
    score = event.get("score") or {}
    teams = event.get("teams") or []
    teams_by_id = {team["team_id"]: team for team in teams if "team_id" in team}

    def team_name(side):
        team = teams_by_id.get(score.get(f"team_id_{side}"))
        if team is None:
            team = next((t for t in teams if t.get(f"is_{side}") is True), {})
        return " ".join(
            part for part in (team.get("name"), team.get("mascot")) if part
        ) or None

    return {
        "event_id": event.get("event_id"),
        "event_date": event.get("event_date"),
        "status": score.get("event_status"),
        "status_detail": score.get("event_status_detail"),
        "away_team": team_name("away"),
        "home_team": team_name("home"),
        "away_score": score.get("score_away"),
        "home_score": score.get("score_home"),
        "period": score.get("game_period"),
        "display_clock": score.get("display_clock"),
        "score_updated_at": score.get("updated_at"),
    }


def main():
    sport_id = int(sys.argv[1])
    day = date.fromisoformat(sys.argv[2]).isoformat()
    response = requests.get(
        f"https://therundown.io/api/v2/sports/{sport_id}/events/{day}",
        params={"affiliate_ids": "0"},
        headers={"X-TheRundown-Key": os.environ["THERUNDOWN_API_KEY"]},
        timeout=30,
    )
    response.raise_for_status()
    for event in response.json().get("events") or []:
        print(json.dumps(score_row(event)))


if __name__ == "__main__":
    main()

The team lookup uses score.team_id_away and score.team_id_home, with the teams’ is_away and is_home flags as a fallback. It does not assume that array position determines which team is at home. Missing names stay None, which JSON renders as null.

The score lookup also preserves the difference between zero and missing. score.get("score_home") returns 0 for a present zero and None for an absent field. An expression such as score.get("score_home") or "—" would incorrectly replace zero with a dash.

What the captured response looks like

On October 29, 2026, the NBA request above returned seven completed games. Here is one row from the Python example, trimmed to the score display fields:

{
  "status": "STATUS_FINAL",
  "away_team": "Golden State Warriors",
  "home_team": "Minnesota Timberwolves",
  "away_score": 119,
  "home_score": 114,
  "period": 4,
  "display_clock": "0.00"
}

The NFL request for November 1, 2026 returned eleven scheduled games. Baltimore at Buffalo had STATUS_SCHEDULED, scores of 0 and 0, period 0, and the same "0.00" display clock. These are dated snapshots, not live scores or a guarantee about a later response.

Keep status, clock and missing data separate

Use score.event_status for the event’s supplied state. A clock of zero appears before kickoff and after a final; the clock alone cannot distinguish them. Keep unfamiliar statuses visible for deliberate handling rather than treating every status other than scheduled as live or final. The event status reference lists the supported states.

Use score.display_clock as the feed’s display string and retain score.game_period alongside it. This example preserves missing fields as None and does not run a local countdown. It reads every displayed score and clock field from the same score object.

Some entitled responses also carry a separate live_game_state object with sport-specific context. That field requires Ultra or higher and may be absent. Its timestamps and clock fields belong to that object; do not mix them with a different score snapshot or assume that a missing object means the event is over.

Per-period arrays are available as score_away_by_period and score_home_by_period. Their entries are individual period scores, not cumulative totals. Read each array by its own length and validate it before deriving half scores or other results. The scores and results guide covers overtime and reconciliation.

Add updates or odds when you need them

For streaming score and status changes, Ultra and higher can subscribe to the multiplexed WebSocket’s scores channel. It does not include live_game_state. Follow the WebSocket guide for subscriptions and recovery, rather than repeatedly downloading an entire slate.

If your screen also shows sportsbook prices, use the Python odds guide to parse markets, participants and lines. A price’s closed_at determines its current availability; a game clock or score status does not replace that signal. For a first integration, start with the API documentation and check API pricing for your intended use.

Questions

How do I request sports scores without odds?
On the V2 dated-event or single-event endpoint, pass affiliate_ids=0. The response keeps events, scores and status with no market or price entries. Leaving out market_ids does not select scores only; it requests the default odds markets.
Does a zero game clock mean the game is final?
No. The captured scheduled NFL games and completed NBA games both had zero clocks. Read score.event_status and preserve the supplied status rather than inferring it from the clock, kickoff time or timestamp age.
How do I distinguish a missing score from zero in Python?
Use score.get(“score_home”) and score.get(“score_away”). A present zero stays 0; a missing field becomes None. Avoid truthiness fallbacks that replace a legitimate zero with a missing-data marker.
Is a scores-only response free of data-point charges?
No. Without prices or live_game_state, each event costs two data points: one event point and one scores point. A returned live_game_state adds another point. Check X-Datapoints on the response and the current documentation for access and billing.
Can I receive score changes over WebSocket?
Ultra and higher can use the multiplexed WebSocket scores channel for score and status updates. That channel does not include live_game_state. See the WebSocket guide for authentication, subscriptions and recovery.

Pull live odds in one request.

Get a free API key: pre-match game lines from DraftKings, FanDuel, and BetMGM, 20,000 data points a day, no credit card.