Live sports scores in Python

Four short scripts on one helper module: live games for any league, a season schedule, one game's details, box score and plays, and a watcher that polls and prints only score changes. For one league in depth, the NFL, NBA, NHL, soccer and college football guides go further.

Stack
Python 3.10+, requests
Plan
Free to build and try; Starter to poll one team's games all season
Code
python/ (MIT)

Updated 2026-11-06. Tested against the live API on that date. 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.

1. Get a free API key

Sign up (no card) and copy the key from the dashboard. The Free plan gives you 1,000 calls in the first 30 days, then 125 a month, at 1 request per second. Every successful REST call counts as one call.

Check the key works with one call to the live-scores endpoint:

Terminal

export REALTIME_SPORTS_API_KEY=your_key
curl -H "Authorization: Bearer $REALTIME_SPORTS_API_KEY" \
  "https://www.realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/live"

2. Get the code

Everything below is in the python/ folder of the examples repo (MIT). Clone it and work from that folder.

Terminal

git clone https://github.com/ElcoDevRepos/realtime-sports-api-examples
cd realtime-sports-api-examples/python

3. Install

Python 3.10+ and requests.

Terminal

python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt

4. The helper

api_get sends your key as a Bearer token, passes keyword arguments as query parameters and raises with the API's own error message and hint on a failed call. format_game prints one line per game with the event id in brackets.

python/rsa_common.py

"""Shared helpers for the Python examples (uses `requests`)."""
from __future__ import annotations

import os
import sys
from pathlib import Path
from typing import Any, Dict, Optional

import requests

API_BASE = os.environ.get("RSA_API_BASE", "https://www.realtimesportsapi.com/api/v1")

# Load ../.env (KEY=value lines) if present; variables already in the environment win.
_env = Path(__file__).resolve().parent.parent / ".env"
if _env.exists():
    for line in _env.read_text().splitlines():
        line = line.strip()
        if line and not line.startswith("#") and "=" in line:
            k, v = line.split("=", 1)
            os.environ.setdefault(k.strip(), v.strip().strip("'\""))


class ApiError(Exception):
    def __init__(self, status: int, body: Optional[Dict[str, Any]], retry_after: Optional[str]):
        err = (body or {}).get("error") or {}
        self.status = status
        self.code = err.get("code")
        self.hint = err.get("hint")
        self.retry_after = retry_after
        super().__init__(f"HTTP {status} {self.code or ''}: {err.get('message', 'request failed')}")


def require_key() -> str:
    key = os.environ.get("REALTIME_SPORTS_API_KEY", "").strip()
    if not key or key == "your_api_key_here":
        sys.exit(
            "Set REALTIME_SPORTS_API_KEY first (export it, or copy .env.example to .env).\n"
            "Get a free key at https://www.realtimesportsapi.com/signup"
        )
    return key


def api_get(path: str, **query: Any) -> Dict[str, Any]:
    """GET a path under /api/v1 and return the envelope {success, data, meta}. Raises ApiError on non-2xx."""
    key = require_key()
    params = {k: v for k, v in query.items() if v is not None}
    res = requests.get(API_BASE + path, params=params, headers={"Authorization": f"Bearer {key}"}, timeout=30)
    try:
        body = res.json()
    except ValueError:
        body = None
    if not res.ok:
        raise ApiError(res.status_code, body, res.headers.get("Retry-After"))
    return body


def format_game(g: Dict[str, Any]) -> str:
    """One-line score: 'KC 14 @ BAL 10  5:23 - 2nd Quarter  [eventId]'"""
    a, h = g.get("awayTeam") or {}, g.get("homeTeam") or {}

    def side(t: Dict[str, Any]) -> str:
        name = t.get("abbreviation") or t.get("name") or "?"
        return name if t.get("score") is None else f"{name} {t['score']}"

    return f"{side(a)} @ {side(h)}  {(g.get('status') or {}).get('detail', '')}  [{g.get('id')}]"


def run(main) -> None:
    """Run main() and print API errors cleanly instead of a traceback."""
    try:
        main()
    except ApiError as e:
        print(e, file=sys.stderr)
        if e.hint:
            print(f"Hint: {e.hint}", file=sys.stderr)
        if e.status == 429:
            print(f"Monthly quota exhausted. Retry after {e.retry_after or '?'} s or upgrade.", file=sys.stderr)
        sys.exit(1)
    except requests.RequestException as e:
        sys.exit(f"Network error: {e}")

5. Games in progress right now

One call. At 2:49 pm Central on 6 November 2026, python live_scores.py soccer eng.1 printed COV 0 @ EVE 0 HT [401879242]. Sport and league slugs: football nfl, basketball nba, hockey nhl, baseball mlb, soccer eng.1, football college-football.

python/live_scores.py

"""Games in progress right now for one league.

Usage: python live_scores.py [sport] [league]
  python live_scores.py football nfl
  python live_scores.py soccer eng.1    # soccer is "soccer"; "football" is American football
"""
import sys

from rsa_common import api_get, format_game, run


def main() -> None:
    sport = sys.argv[1] if len(sys.argv) > 1 else "football"
    league = sys.argv[2] if len(sys.argv) > 2 else "nfl"
    body = api_get(f"/sports/{sport}/leagues/{league}/events/live")
    games = body.get("data") or []
    if not games:
        print(f"No {league} games live right now.")
    for g in games:
        print(format_game(g))
    remaining = ((body.get("meta") or {}).get("rateLimit") or {}).get("remaining")
    if remaining is not None:
        print(f"\nCalls remaining this month: {remaining}")


if __name__ == "__main__":
    run(main)

6. Schedule and one game

python schedule.py football nfl 2026 9 listed the 15 week-9 games with UTC kickoff times and event ids (2026-11-06T01:15Z JAX 33 @ BAL 10 Final [401873037]). event_details.py takes a sport, league and event id and makes three calls: the game, its box score and the latest plays.

python/schedule.py

"""Season schedule. NFL and college football also take a week.

Usage: python schedule.py [sport] [league] [season] [week] [seasonType]
  python schedule.py football nfl 2026 5 2    # 2026 regular season, week 5
  python schedule.py hockey nhl 2026
"""
import sys

from rsa_common import api_get, format_game, run


def main() -> None:
    args = sys.argv[1:] + [None] * 5
    sport, league, season = args[0] or "football", args[1] or "nfl", args[2] or "2026"
    week, season_type = args[3], args[4] or "2"
    body = api_get(f"/sports/{sport}/leagues/{league}/seasons/{season}/schedule", week=week, seasonType=season_type)
    games = body.get("data") or []
    for g in games:
        print(f"{g.get('date', '')}  {format_game(g)}")
    print(f"\n{len(games)} games")


if __name__ == "__main__":
    run(main)

python/event_details.py

"""One game: details, box score and the latest plays (3 API calls).

Usage: python event_details.py <sport> <league> <eventId>
Find event ids with live_scores.py (printed in [brackets]).
"""
import sys

from rsa_common import api_get, format_game, run


def main() -> None:
    if len(sys.argv) < 4:
        sys.exit("Usage: python event_details.py <sport> <league> <eventId>   (e.g. football nfl 401772982)")
    sport, league, event_id = sys.argv[1:4]
    base = f"/sports/{sport}/leagues/{league}/events/{event_id}"
    print(format_game(api_get(base)["data"]))
    box = api_get(f"{base}/boxscore")["data"] or {}
    print("\nBox score keys:", ", ".join(box.keys()))
    plays = api_get(f"{base}/plays", limit=10)["data"] or []
    print("\nPlays:")
    for p in plays:
        print(" -", p.get("text") or " ".join(x for x in [(p.get("type") or {}).get("text") or "Play", (p.get("clock") or {}).get("displayValue")] if x))


if __name__ == "__main__":
    run(main)

7. Watch a league and print score changes

Each poll is one call: about 360 for a 3-hour game at 30 seconds. Try it on Free; Starter (10,000 calls a month) follows one team all season. Data is typically 20-30 seconds behind live play, so faster polling buys nothing, and it is not for in-play betting. On a paid plan, webhooks push score changes to you instead.

Terminal

python live_scores.py hockey nhl
python schedule.py football nfl 2026 9
python event_details.py soccer eng.1 401879242
python watch.py basketball nba 30

python/watch.py

"""Poll the live endpoint and print only what changed: score, clock/period, final.

Usage: python watch.py [sport] [league] [seconds]
  python watch.py basketball nba 30

Every poll is one API call: a 3-hour game polled every 30 s is about 360 calls.
Data is typically 20-30 seconds behind live play; not for in-play betting.
"""
import sys
import time
from datetime import datetime

from rsa_common import api_get, format_game, run


def main() -> None:
    sport = sys.argv[1] if len(sys.argv) > 1 else "football"
    league = sys.argv[2] if len(sys.argv) > 2 else "nfl"
    seconds = int(sys.argv[3]) if len(sys.argv) > 3 else 30
    last: dict = {}  # event id -> last line printed
    while True:
        games = api_get(f"/sports/{sport}/leagues/{league}/events/live").get("data") or []
        for g in games:
            line = format_game(g)
            if last.get(g["id"]) != line:
                print(f"{datetime.now():%H:%M:%S}  {line}", flush=True)
                last[g["id"]] = line
        if not games and not last:
            print(f"No {league} games live right now.", flush=True)
        time.sleep(seconds)


if __name__ == "__main__":
    run(main)

8. Going further

League guides with real output: NFL, NBA, NHL, soccer and college football. A whole season in pandas: season analysis.

Endpoints used

  • GET /sports/{sport}/leagues/{league}/events/live
  • GET /sports/{sport}/leagues/{league}/seasons/{season}/schedule
  • GET /sports/{sport}/leagues/{league}/events/{eventId}
  • GET /sports/{sport}/leagues/{league}/events/{eventId}/boxscore
  • GET /sports/{sport}/leagues/{league}/events/{eventId}/plays

Full reference: docs · OpenAPI · what each endpoint returns, by league

FAQ

Is there a free live sports scores API for Python?
Yes. A free key (no card) gives 1,000 calls in the first 30 days, then 125 a month, with every endpoint. These scripts need only requests.
How do I get live scores for one league in Python?
Call GET /sports/{sport}/leagues/{league}/events/live with your key, for example /sports/basketball/leagues/nba/events/live. It returns only the games in progress.
How fresh are the scores?
Typically 20-30 seconds behind live play: fine for dashboards, bots and alerts, not for in-play betting.

More guides