# 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.

Published 2026-10-29. Updated 2026-10-29. Canonical: https://therundown.io/blog/sports-scores-api-python

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](/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](https://docs.therundown.io/reference/sports); 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:

```bash
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](https://docs.therundown.io/guides/scores-and-results) 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.

```python
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:

```json
{
  "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](https://docs.therundown.io/reference/event-statuses) 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](https://docs.therundown.io/guides/scores-and-results#read-the-period-arrays) 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](/blog/sports-odds-websocket-api) for subscriptions and recovery, rather than repeatedly downloading an entire slate.

If your screen also shows sportsbook prices, use the [Python odds guide](/blog/sports-betting-odds-api) 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](https://docs.therundown.io/guides/scores-and-results) and check [API pricing](/pricing/api) 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.
