College football rankings API in Python

One small script with three commands: print a poll with records, points and first-place votes; compare it with the previous week (movers, new entries, teams that dropped out); and list this week's games that involve a ranked team.

Stack
Python, requests
Plan
Free (one call per command; movers makes two)

Updated 2026-11-05. 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-college-football/ 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-college-football

3. Install

Python 3.10+ and requests. Nothing else.

Terminal

python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env    # REALTIME_SPORTS_API_KEY

4. The script

poll() reads one poll from /rankings (poll=ap, coaches or cfp; add week= for a past week). show prints each ranked team with record, points and first-place votes, then the others receiving votes. movers reads the previous week too and compares by team id, so it lists the biggest moves, the new entries and the teams that dropped out. matchups reads this week's scoreboard, where every ranked team carries a rank, and prints games like #2 UGA @ #10 MISS.

python-college-football/rankings.py

#!/usr/bin/env python3
"""College football rankings in Python with the Realtime Sports API: the AP Top 25, coaches poll or
College Football Playoff rankings, the week's biggest movers, and this week's ranked matchups.

Usage:
  python rankings.py                    # AP Top 25, latest release
  python rankings.py --poll cfp         # CFP rankings (or: coaches)
  python rankings.py movers             # who moved most since the previous week (AP)
  python rankings.py matchups           # this week's games with a ranked team, "#2 UGA @ #10 MISS"

Needs REALTIME_SPORTS_API_KEY (env or .env). `movers` makes two calls, the others one each.
"""
from __future__ import annotations

import argparse
import os
import sys
from pathlib import Path
from typing import Any, Dict, List

import requests

API_BASE = os.environ.get("RSA_API_BASE", "https://www.realtimesportsapi.com/api/v1")
CFB = "/sports/football/leagues/college-football"


def load_dotenv(path: Path) -> None:
    """Tiny .env loader (KEY=value). Existing environment variables win."""
    if not path.exists():
        return
    for line in path.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('"').strip("'"))


def api_get(path: str, **params: Any) -> Dict[str, Any]:
    key = os.environ.get("REALTIME_SPORTS_API_KEY")
    if not key:
        sys.exit("Set REALTIME_SPORTS_API_KEY (get a free key at https://www.realtimesportsapi.com/signup)")
    r = requests.get(f"{API_BASE}{path}", params=params, headers={"Authorization": f"Bearer {key}"}, timeout=20)
    body = r.json()
    if r.status_code != 200 or not body.get("success", False):
        err = body.get("error") or {}
        sys.exit(f"HTTP {r.status_code}: {err.get('message') or body}")
    return body


def poll(name: str, week: int | None = None) -> Dict[str, Any]:
    params: Dict[str, Any] = {"poll": name}
    if week:
        params["week"] = week
    polls: List[Dict[str, Any]] = api_get(f"{CFB}/rankings", **params)["data"]
    if not polls:
        sys.exit(f"No {name} poll for that week")
    return polls[0]


def cmd_show(args: argparse.Namespace) -> None:
    p = poll(args.poll)
    print(f"{p['name']} - {p['week'].get('label') or ''} ({(p.get('date') or '')[:10]})")
    for r in p["ranks"]:
        t = r["team"]
        votes = f", {r['firstPlaceVotes']} first-place" if r.get("firstPlaceVotes") else ""
        pts = f", {r['points']:g} pts" if r.get("points") else ""
        print(f"  {r['rank']:>2}. {t.get('displayName')} ({r.get('record') or '-'}){pts}{votes}")
    if p.get("othersReceivingVotes"):
        print("  Others receiving votes: " + ", ".join(f"{o['team'].get('abbreviation')} {o.get('points') or 0:g}" for o in p["othersReceivingVotes"]))


def cmd_movers(args: argparse.Namespace) -> None:
    now = poll(args.poll)
    week = now["week"].get("number")
    if not week or week < 2:
        sys.exit("No previous week to compare with yet")
    before = {r["team"]["id"]: r["rank"] for r in poll(args.poll, week - 1)["ranks"]}
    moved = [(before[r["team"]["id"]] - r["rank"], r) for r in now["ranks"] if r["team"]["id"] in before]
    print(f"{now['name']}: Week {week - 1} -> Week {week}")
    for delta, r in sorted(moved, key=lambda m: -abs(m[0]))[:6]:
        if delta:
            print(f"  #{r['rank']:>2} {r['team'].get('displayName')}: {'+' if delta > 0 else ''}{delta} (was #{before[r['team']['id']]})")
    for r in now["ranks"]:
        if r["team"]["id"] not in before:
            print(f"  new at #{r['rank']}: {r['team'].get('displayName')}")
    for d in now.get("droppedOut") or []:
        print(f"  dropped out: {d['team'].get('displayName')} (was #{d.get('previousRank')})")


def cmd_matchups(args: argparse.Namespace) -> None:
    games = api_get(f"{CFB}/events", limit=200)["data"]
    tag = lambda t: f"#{t['rank']} {t.get('abbreviation')}" if t.get("rank") else t.get("abbreviation")
    ranked = [g for g in games if g["homeTeam"].get("rank") or g["awayTeam"].get("rank")]
    for g in sorted(ranked, key=lambda g: g.get("date") or ""):
        st = g.get("status") or {}
        score = "" if st.get("state") == "pre" else f"  {g['awayTeam'].get('score')}-{g['homeTeam'].get('score')}"
        print(f"[{g['id']}] {tag(g['awayTeam'])} @ {tag(g['homeTeam'])}{score}  {st.get('detail') or ''}")
    print(f"\n{len(ranked)} of {len(games)} games involve a ranked team (rank = the ranking the scoreboard shows for each team)")


def main() -> None:
    load_dotenv(Path(__file__).with_name(".env"))
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("--poll", default="ap", choices=["ap", "coaches", "cfp"])
    ap.add_argument("cmd", nargs="?", default="show", choices=["show", "movers", "matchups"])
    args = ap.parse_args()
    {"show": cmd_show, "movers": cmd_movers, "matchups": cmd_matchups}[args.cmd](args)


if __name__ == "__main__":
    main()

5. Run it

On 5 November 2026, python rankings.py printed the Week 10 AP Top 25 headed by 1. Miami Hurricanes (8-0), 1740 pts, 62 first-place. movers showed Ohio State falling from #2 to #11 and Florida from #17 to #25, USC, Iowa, New Mexico and South Carolina entering, and SMU, Northwestern, Oklahoma State and Wisconsin dropping out. --poll cfp printed the committee's Week 10 rankings, and matchups found 16 of the week's 63 games with a ranked team, including #9 BYU @ #3 UTAH.

Terminal

python rankings.py              # AP Top 25
python rankings.py --poll cfp   # College Football Playoff rankings
python rankings.py movers       # since last week
python rankings.py matchups     # ranked games this week

6. Going further

The same endpoint serves men's college basketball (/sports/basketball/leagues/mens-college-basketball/rankings, AP and coaches). For scores, box scores and play-by-play, see the college football API in Python guide; for every field, the rankings endpoint page. Data typically runs 20-30 seconds behind live play.

Endpoints used

  • GET /sports/football/leagues/college-football/rankings?poll=ap|coaches|cfp
  • GET /sports/football/leagues/college-football/rankings?poll=ap&week=N
  • GET /sports/football/leagues/college-football/events

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

FAQ

Is there a free college football rankings API?
Yes. A free key (no card) gives 1,000 calls in the first 30 days, then 125 a month, and includes the rankings endpoint for the AP Top 25, coaches poll and CFP rankings.
Can I get a past week's AP poll?
Yes. Add week=N (and season= for an earlier season) to /rankings; without it you get the latest release.
Why does a team's rank on a game differ from the AP poll?
The rank on a scoreboard game is the one shown for that team going into the game, which can follow the CFP rankings once they are released. Use /rankings with poll= for a specific poll.

More guides