API Documentation

Realtime Sports API gives you live scores, play-by-play, box scores, schedules, teams, athletes, injuries, news and betting odds under one schema and one API key, with push delivery over webhooks and WebSocket. 29 leagues are monitored live: NFL, college football, NBA, men's college basketball, MLB, NHL and 23 soccer competitions.

Freshness: data is aggregated from public sources and is typically 20-30 seconds behind live play (our pipeline adds about 1 second). There is no SLA.

Base URL

https://www.realtimesportsapi.com/api/v1

Machine-readable: OpenAPI 3 spec, Postman collection, MCP server for Claude, Cursor and ChatGPT, and llms.txt.

Try it: no signup, no key

Run a real request against the live API. Responses are cached for up to 30 seconds; long lists are shortened here (5 items, nested lists 3).

Upcoming, live and recent games for the league, with teams, scores and status.

curl "https://www.realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events?limit=5" \
  -H "Authorization: Bearer YOUR_API_KEY"

Replace YOUR_API_KEY with your key. Base URL: https://www.realtimesportsapi.com/api/v1

Get your free API key

1,000 calls in your first month, then 125/month. No credit card. Every data endpoint and league.

Authentication

All API requests require your API key (an opaque string, not a JWT). Send it in the Authorization header, or in an X-API-Key header if a proxy strips Authorization:

Authorization: Bearer YOUR_API_KEY

Note: Get your API key from the dashboard.

Rate Limits

Rate limits vary by subscription tier. Monthly call limits reset at the start of each month.

TierCalls/MonthCalls/Second
Free125 (1,000 first month)1
Starter10,0005
Growth25,00010
Pro50,00020
Scale500,000100

Every successful REST call counts as one call (on Starter and Growth, WebSocket messages and webhook deliveries share the same monthly pool). Remaining quota is returned in meta.rateLimit. When the quota is used up the API answers HTTP 429 with a Retry-After header; wait, upgrade, or enable opt-in overage in the dashboard.

API Structure

The API follows a hierarchical structure: Sport → League → Resource

/api/v1/sports
  /{sport}/leagues
    /{league}
      /teams                    # All teams in league
      /athletes                 # All athletes (paginated)
        /{athleteId}            # Single athlete
      /seasons                  # Historical seasons
        /{year}                 # Season details
      /events                   # All events
        /live                   # Live events only
        /{eventId}              # Single event
          /plays                # Play-by-play (paginated)
          /penalties            # Penalties only

Sports & Leagues

GET/v1/sports

Get all available sports.

Example Request

curl -X GET "https://realtimesportsapi.com/api/v1/sports" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": [
    {
      "id": "20",
      "name": "Football",
      "slug": "football",
      "displayName": "Football"
    },
    {
      "id": "40",
      "name": "Basketball",
      "slug": "basketball",
      "displayName": "Basketball"
    }
  ]
}
GET/v1/sports/:sport/leagues

Get all leagues for a specific sport.

Example Request

curl -X GET "https://realtimesportsapi.com/api/v1/sports/football/leagues" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": [
    {
      "id": "28",
      "name": "National Football League",
      "slug": "nfl",
      "abbreviation": "NFL",
      "currentSeason": 2025
    },
    {
      "id": "35",
      "name": "NCAA Football",
      "slug": "college-football",
      "abbreviation": "NCAA"
    }
  ]
}

Sport & League Slugs

Every league path is /v1/sports/{sport}/leagues/{league}/.... These 29 leagues are monitored live (live scores, play-by-play, WebSocket and webhooks). Other slugs from our upstream source may return data through REST, but they are not monitored or guaranteed.

LeaguesportleagueNotes
NFLfootballnflSchedule by week supported.
College Football (NCAA FBS)footballcollege-footballNCAA Division I FBS only; FCS and lower-division games are not covered. The teams endpoint lists all divisions; use /seasons/{season}/types/2/groups/80/teams for FBS teams. Schedule by week supported.
NBAbasketballnba
Men's College Basketball (NCAA)basketballmens-college-basketballNCAA Division I men's. Women's college basketball is not monitored.
MLBbaseballmlbLive events include inning state (half, outs, count, runners).
NHLhockeynhlBox scores are not available.
MLSsoccerusa.1MLS: the slug is usa.1, not mls.
U.S. Open Cupsoccerusa.open
English Premier Leaguesoccereng.1Premier League.
UEFA Champions Leaguesocceruefa.champions
UEFA Europa Leaguesocceruefa.europa
English FA Cupsoccereng.fa
Spanish LaLigasocceresp.1
Italian Serie Asoccerita.1
Mexican Liga MXsoccermex.1
FIFA World Cupsoccerfifa.world
English Women's Super Leaguesoccereng.w.1
UEFA Women's Champions Leaguesocceruefa.wchampions
UEFA Conference Leaguesocceruefa.europa.conf
English League Cupsoccereng.league_cup
German Bundesligasoccerger.1
French Ligue 1soccerfra.1
NWSLsoccerusa.nwsl
English Championshipsoccereng.2
FIFA World Cup Qualifying (UEFA)soccerfifa.worldq.uefa
FIFA World Cup Qualifying (CONMEBOL)soccerfifa.worldq.conmebol
FIFA World Cup Qualifying (AFC)soccerfifa.worldq.afc
FIFA World Cup Qualifying (CAF)soccerfifa.worldq.caf
FIFA World Cup Qualifying (CONCACAF)soccerfifa.worldq.concacaf

Soccer box scores carry team totals only (no player rows), and injuries are not available for soccer. Example: /v1/sports/football/leagues/college-football/events.

Teams

GET/sports/{sport}/leagues/{league}/teams

Get all teams for a league.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/baseball/leagues/mlb/teams" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response Includes

  • ✓ Team names, abbreviations, slugs
  • ✓ Logos (default & dark theme, proxied)
  • ✓ Team colors (hex codes)
  • ✓ Venue information (stadium, capacity)
  • ✓ Records and standings
GET/sports/{sport}/leagues/{league}/teams/{teamId}/roster

Current active roster for a team, grouped by position plus a flat list.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/teams/12/roster" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response Includes

  • ✓ Position groups plus a flat athletes list
  • ✓ Per player: id, name, jersey, position, age, height/weight
  • ✓ Experience, status, and headshot (proxied)
GET/sports/{sport}/leagues/{league}/teams/{teamId}/depthchart

Team depth chart: formations → positions → rank-ordered athletes (with names).

Query Parameters

  • season - Season year (defaults to current year)

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/teams/12/depthchart?season=2024" \
  -H "Authorization: Bearer YOUR_API_KEY"

Injuries, News & Transactions

GET/sports/{sport}/leagues/{league}/injuries

Current injuries grouped by team (plus a flat list). Each entry includes player, status, fantasy status, body part, date, the provider's injury record id (injuryId), the expected returnDate when given, and practice participation (DNP / LIMITED / FULL, with the weekday). Practice participation is parsed from the news note, so it is marked derived: true and is absent when the note doesn't say.

Note: Populated in-season. Expect a short/empty list in the deep off-season; the NFL report fills in once training camps open.

Query Parameters

  • team - Filter to a single team id

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/basketball/leagues/nba/injuries" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/player-availability

Coverage note: the league-wide response is truncated by our data provider to the 25 most recent records per team (limit/page have no effect on it). For a team's complete current report — one latest record per athlete, including a structured statusType (e.g. INJURY_STATUS_IR) — pass ?team={teamId} on this endpoint or on /injuries. Entries with status Active are informational news notes, not availability designations; filter with ?status=.

Normalized per-player availability. Each player carries a single availability value — OUT, DOUBTFUL, QUESTIONABLE, PROBABLE, DAY_TO_DAY, INJURED_RESERVE, SUSPENDED, AVAILABLE, or UNKNOWN — so you can gate lineups without parsing free-text injury notes.

Query Parameters

  • team - Filter to a single team id
  • status - Filter to a normalized availability value (e.g. OUT)

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/player-availability?status=OUT" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/player-availability/history

League-wide feed of availability changes, newest first. We snapshot the injury report every 5 minutes and record a transition whenever a player's designation, injury type, team, report date, expected return date or practice participation changes — including when a player drops off the report (changeType: "cleared"). Each record carries previousAvailability so you can see e.g. QUESTIONABLE → OUT. History begins from the date tracking was enabled (NFL: Aug 2026); it is not backfilled.

Query: ?team={teamId}, ?status=OUT (normalized value), ?since=ISO, ?limit= (max 500). Covers NFL, college football, NBA, MLB, NHL. Records observed from Sep 2026 onward also carry the season context at observation time — seasonYear, seasonType (1 pre / 2 regular / 3 post), and week for weekly leagues (NFL/CFB) — so a change can be joined to that week's schedule via teamId + week on the events endpoints.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/player-availability/history?status=OUT&since=2026-09-01T00:00:00Z" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": [
    {
      "athleteId": "3886633",
      "name": "Hjalte Froholdt",
      "position": "C",
      "teamId": "22",
      "teamName": "Arizona Cardinals",
      "availability": "OUT",
      "previousAvailability": "QUESTIONABLE",
      "status": "Out",
      "previousStatus": "Questionable",
      "reason": "Hamstring",
      "detail": "Froholdt was ruled out Friday...",
      "reportDate": "2026-09-04T21:10Z",
      "observedAt": "2026-09-04T21:30:02.118Z",
      "seasonYear": 2026,
      "seasonType": 2,
      "week": 1,
      "changeType": "change"
    }
  ]
}
GET/sports/{sport}/leagues/{league}/athletes/{athleteId}/availability-history

Full designation timeline for one player (newest first) plus their current state. Same record shape as the league feed. Query: ?from=ISO, ?to=ISO, ?limit=.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/athletes/3886633/availability-history" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/transactions

Recent transactions newest-first: signings, IL/DL moves, call-ups, releases, and trades. Each item includes date, description, and the team involved.

Query Parameters

  • limit - Number of transactions (default: 25)
  • page - Page through history
  • team - Filter to a single team id
  • season - Season year (defaults to current season)

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/baseball/leagues/mlb/transactions?limit=25" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/news

Latest news articles for a league: headline, description, images, link, published date, and the athletes/teams the article is tagged with (stable ids that join to /athletes/{id} and /teams/{id}). The provider exposes a rolling window of the ~50 most recent articles per league — for anything older, use /news/history below.

Query Parameters

  • limit - Number of articles (default: 10, max 50)
  • team - Filter to a single team id
  • athlete - Filter to articles tagged with an athlete id
  • since / from, to - ISO date bounds on published (within the rolling window)
  • page - 1-based page within the window

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/news?athlete=3139477&limit=5" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/news/history

Archived news articles, newest first — same article shape as /news. We archive each league's feed hourly, so unlike the live endpoint this one is queryable by arbitrary date range. The archive accumulates from Sep 2, 2026 (the provider offers no earlier history, so it is not backfilled). Covers NFL, college football, NBA, MLB, NHL.

Query Parameters

  • from, to - ISO date bounds on published
  • team - Filter to articles tagged with a team id
  • athlete - Filter to articles tagged with an athlete id
  • limit - Articles per page (default: 25, max 100)
  • page - 1-based page

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/news/history?athlete=3139477&from=2026-09-02T00:00:00Z" \
  -H "Authorization: Bearer YOUR_API_KEY"

Athletes

GET/sports/{sport}/leagues/{league}/athletes

Get all athletes for a league (paginated).

Note: MLB has 36,000+ athletes. Always use pagination!

Query Parameters

  • page - Page number (default: 1)
  • limit - Results per page (default: 25)

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/baseball/leagues/mlb/athletes?page=1&limit=25" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/athletes/{athleteId}

Get single athlete by ID.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/baseball/leagues/mlb/athletes/30836" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response Includes

  • ✓ Full name, position, jersey number
  • ✓ Headshot (proxied image)
  • ✓ Team affiliation
  • ✓ Age and stats availability

Statistics Scoping (Query Parameters)

  • season - Scope statistics to a season year (e.g. 2024)
  • seasonType - 1=preseason, 2=regular, 3=postseason (requires season)
  • Omit both for career/current totals.
curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/athletes/3139477?season=2024&seasonType=2" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/athletes/search

Find players by name, scoped to the sport/league.

Query Parameters

  • query - Player name to search for (required)
  • limit - Max results (default: 25)

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/athletes/search?query=mahomes" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/athletes/{athleteId}/gamelog

Per-game (and per-week) stat lines for an athlete in a season.

Query Parameters

  • season - Season year (required)
  • week - Filter to a single week number
  • seasonType - 1=preseason, 2=regular, 3=postseason

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/athletes/3139477/gamelog?season=2024&week=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

Seasons

GET/sports/{sport}/leagues/{league}/seasons

Get all seasons for a league (historical data).

Note: MLB has 150+ seasons dating back to 1870s!

Query Parameters

  • page - Page number (default: 1)
  • limit - Results per page (default: 25)

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/seasons?limit=5" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/seasons/{year}

Get details for a specific season.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/baseball/leagues/mlb/seasons/2025" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response Includes

  • ✓ Season year and display name
  • ✓ Current season type (preseason, regular, postseason, offseason)
  • ✓ Start and end dates
  • ✓ All season types with dates

Events

GET/sports/{sport}/leagues/{league}/events

Get all events for a league.

Query Parameters

  • limit - Results per page (default: 50)

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events?limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/events/live

Get currently live events only.

MLB events also carry status.inning: number, half (top, bottom, middle or end), outs, balls, strikes and onFirst/onSecond/onThird. The half, count and runners are filled while the game is live; after the final out only the inning number remains.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/live" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": [
    {
      "id": "401772982",
      "name": "Buffalo Bills at Denver Broncos",
      "shortName": "BUF @ DEN",
      "date": "2026-01-17T21:30Z",
      "status": {
        "state": "in",
        "period": 3,
        "clock": "10:45"
      },
      "homeTeam": {
        "id": "7",
        "name": "Denver Broncos",
        "abbreviation": "DEN",
        "logo": "/api/images/proxy?url=...",
        "color": "0a2343",
        "score": 21
      },
      "awayTeam": {
        "id": "2",
        "name": "Buffalo Bills",
        "abbreviation": "BUF",
        "score": 17
      },
      "venue": {
        "name": "Empower Field at Mile High",
        "city": "Denver",
        "state": "CO"
      }
    }
  ]
}
GET/sports/{sport}/leagues/{league}/events/{eventId}

Get single event with full details.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/401772982" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/seasons/{year}/schedule

Events for a season, optionally by week. Supported for week-based leagues (NFL, college football). Upcoming games for future seasons are served live when not yet stored (meta.source is synced or live).

Query Parameters

  • week - Week number (optional)
  • seasonType - 1=preseason, 2=regular (default), 3=postseason
  • includeOdds - true to attach current odds

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/seasons/2026/schedule?week=1" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/seasons/{year}/weeks

List of weeks for a season (number, seasonType, label, start/end dates). NFL and college football only.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/seasons/2026/weeks" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/basketball/leagues/mens-college-basketball/seasons/{year}/tournament

The 68-team NCAA tournament (March Madness) for a season. Each game is annotated with region, round, and a First Four flag; NIT/CBI games are excluded.

Query Parameters

  • round - Filter by round (e.g. First Four, 1st Round, Sweet 16)
  • region - Filter by region (e.g. South)
  • firstFour - true for only the 4 play-in games, false for the 63 main-bracket games

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/basketball/leagues/mens-college-basketball/seasons/2025/tournament?firstFour=false" \
  -H "Authorization: Bearer YOUR_API_KEY"

Plays & Pagination

GET/sports/{sport}/leagues/{league}/events/{eventId}/plays

Get plays for an event (paginated - games can have 200+ plays).

Query Parameters

  • page - Page number (default: 1)
  • limit - Plays per page (default: 25, max: 100)
  • enrich - Resolve athlete id/name/team/position (default: true). Set false for a faster response with only role/order.

Each play's athletes[] identifies the players involved with id, name, position, and team, alongside their role (passer, rusher, tackler, kicker, …) and order — useful for score-event attribution and fantasy analytics.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/401772982/plays?page=1&limit=25" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": [
    {
      "id": "40177298265",
      "text": "B.Nix pass to P.Bryant for 12 yards",
      "type": { "id": "24", "text": "Pass Reception" },
      "period": 1,
      "clock": { "displayValue": "14:56" },
      "situation": {
        "down": 1,
        "distance": 10,
        "yardLine": 26,
        "downDistanceText": "1st & 10 at DEN 26"
      },
      "yardsGained": 12,
      "athletes": [
        { "id": "4426338", "name": "Bo Nix", "position": "QB", "team": { "id": "7" }, "role": "passer", "order": 1 },
        { "id": "4373678", "name": "Pat Bryant", "position": "WR", "team": { "id": "7" }, "role": "receiver", "order": 2 }
      ]
    }
  ],
  "meta": {
    "pagination": {
      "page": 1,
      "pageSize": 25,
      "total": 213,
      "totalPages": 9,
      "hasNextPage": true
    }
  }
}
GET/sports/{sport}/leagues/{league}/events/{eventId}/penalties

Get penalties only from an event.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/401772982/penalties" \
  -H "Authorization: Bearer YOUR_API_KEY"
GET/sports/{sport}/leagues/{league}/events/{eventId}/roster

Game-specific rosters: every player on each team's game roster with per-game availability flags — starter, didNotPlay, and active (on the field; live games only). The provider publishes game rosters at kickoff (the endpoint returns 404 ROSTER_NOT_PUBLISHED before then), so this is a kickoff-onward and historical source — for pre-game designations use /player-availability. Works for completed games, so it doubles as historical starter/DNP data. Player id is the same stable athlete ID used by /plays, /boxscore, and /athletes/{id}.

Query: ?team={teamId} for one side, ?starters=true for starters only, ?enrich=false to skip athlete lookups (ids + flags only).

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/401671733/roster?starters=true" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": {
    "eventId": "401671733",
    "teams": [
      {
        "team": { "id": "18", "name": "New Orleans Saints", "abbreviation": "NO" },
        "homeAway": "home",
        "summary": { "total": 67, "starters": 22, "didNotPlay": 1 },
        "players": [
          {
            "id": "13971",
            "name": "Cameron Jordan",
            "jersey": "94",
            "position": "DE",
            "starter": true,
            "didNotPlay": false,
            "active": false
          }
        ]
      }
    ]
  }
}
GET/sports/{sport}/leagues/{league}/events/{eventId}/winprobability

Per-play win probability series for an event, plus drive summaries. Each point carries a playId that joins to /plays, so you can compute win-probability-added per play. Drives include result, yards, time elapsed, start/end field position and the list of play IDs. Pass ?drives=false to omit drives.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/401671733/winprobability" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": {
    "eventId": "401671733",
    "winProbability": [
      { "playId": "401671733237", "homeWinPercentage": 0.5033, "awayWinPercentage": 0.4967, "tiePercentage": 0 }
    ],
    "drives": [
      {
        "id": "4016717331",
        "team": { "id": "1", "name": "Atlanta Falcons", "abbreviation": "ATL" },
        "description": "6 plays, 17 yards, 1:58",
        "result": "PUNT",
        "isScore": false,
        "yards": 17,
        "offensivePlays": 6,
        "start": { "period": 1, "clock": "15:00", "yardLine": 74, "text": "ATL 26" },
        "end":   { "period": 1, "clock": "13:02", "yardLine": 57, "text": "ATL 43" },
        "playIds": ["4016717331", "..."]
      }
    ]
  }
}

Betting & Odds

Get current odds (spread, moneyline, over/under), odds history for line movement, and real-time odds updates via webhooks and WebSockets. Ideal for betting apps, sportsbooks, and sharp tools.

GET/sports/{sport}/leagues/{league}/events/{eventId}/odds

Current betting odds for an event: spread, moneyline, and over/under. Same shape used when you request includeOdds=true on event or list endpoints.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/401772982/odds" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "success": true,
  "data": {
    "spread": { "line": -3.5, "home": -110, "away": -110 },
    "moneyline": { "home": -180, "away": 150 },
    "overUnder": { "total": 48.5, "over": -110, "under": -110 },
    "provider": "DraftKings",
    "updatedAt": "2026-01-28T20:00:00Z"
  }
}

Odds on event and list endpoints

Avoid N+1 calls by requesting odds with events in one go. Add includeOdds=true to:

  • Single event: GET .../events/{eventId}?includeOdds=true
  • Live events: GET .../events/live?includeOdds=true
  • League events: GET .../events?includeOdds=true
  • Season schedule: GET .../seasons/{season}/schedule?includeOdds=true (optional with week)

Each event in the response will include an odds property when available. Using includeOdds=true may increase latency and usage.

GET/sports/{sport}/leagues/{league}/events/{eventId}/odds/history

Historical odds for line movement, closing line, or movement alerts. Use limit (default 100) to control how many history entries are returned.

curl "https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/401772982/odds/history?limit=50" \
  -H "Authorization: Bearer YOUR_API_KEY"

Real-time odds updates

When lines move, get notified without polling:

  • Webhook: Subscribe to event.odds_change. Your endpoint receives a POST when odds for that event change.
  • WebSocket: Listen for event_odds_change (any paid plan). Payload includes event id and updated odds.

Configure webhooks in your dashboard. See the Webhooks and WebSockets sections below for subscription details.

Example: Fetch live games with odds

// Live NFL games with current odds (one request)
const res = await fetch(
  'https://realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/live?includeOdds=true',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { data: games } = await res.json();
games.forEach(g => {
  console.log(g.name, g.odds?.spread?.line, g.odds?.overUnder?.total);
});

JavaScript Example

const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://realtimesportsapi.com/api/v1';

// Discover sports and leagues
async function discoverAPI() {
  // Get all sports
  const sports = await fetch(`${BASE_URL}/sports`, {
    headers: { 'Authorization': `Bearer ${API_KEY}` }
  }).then(r => r.json());
  
  console.log('Sports:', sports.data);
  
  // Get football leagues
  const leagues = await fetch(`${BASE_URL}/sports/football/leagues`, {
    headers: { 'Authorization': `Bearer ${API_KEY}` }
  }).then(r => r.json());
  
  console.log('Leagues:', leagues.data);
}

// Get live NFL games
async function getLiveNFLGames() {
  const response = await fetch(
    `${BASE_URL}/sports/football/leagues/nfl/events/live`,
    { headers: { 'Authorization': `Bearer ${API_KEY}` } }
  );
  
  const data = await response.json();
  
  if (data.success) {
    console.log('Live games:', data.data);
  }
}

// Get plays with pagination
async function getPlays(eventId, page = 1) {
  const response = await fetch(
    `${BASE_URL}/sports/football/leagues/nfl/events/${eventId}/plays?page=${page}&limit=25`,
    { headers: { 'Authorization': `Bearer ${API_KEY}` } }
  );
  
  const data = await response.json();
  
  console.log(`Page ${page}: ${data.data.length} plays`);
  console.log(`Total: ${data.meta.pagination.total} plays`);
  
  return data;
}

// Get all plays (all pages)
async function getAllPlays(eventId) {
  const allPlays = [];
  let page = 1;
  let hasMore = true;
  
  while (hasMore) {
    const response = await getPlays(eventId, page);
    allPlays.push(...response.data);
    hasMore = response.meta.pagination.hasNextPage;
    page++;
  }
  
  return allPlays;
}

// Usage
discoverAPI();
getLiveNFLGames();
getAllPlays('401772982').then(plays => {
  console.log(`Fetched ${plays.length} total plays`);
});

Teams & Athletes

GET/sports/{sport}/leagues/{league}/teams

Get all teams for a league.

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/baseball/leagues/mlb/teams" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response Includes

  • ✓ Team names, abbreviations, colors
  • ✓ Logos (proxied through our CDN)
  • ✓ Venue information
  • ✓ Records and standings
GET/sports/{sport}/leagues/{league}/athletes

Get athletes (paginated - MLB has 36,000+!).

Example Request

curl "https://realtimesportsapi.com/api/v1/sports/baseball/leagues/mlb/athletes?page=1&limit=25" \
  -H "Authorization: Bearer YOUR_API_KEY"

Special Features

Image Proxying

All images (team logos, athlete headshots) are automatically proxied through our CDN.

// Team logos are proxied
"logo": "https://realtimesportsapi.com/api/images/proxy?url=..."

// Direct image access (no auth required)
<img src="/api/images/proxy?url=..." alt="Team Logo" />

Pagination Best Practices

  • ✓ Plays: Default 25 per page (games have 200+ plays)
  • ✓ Athletes: Default 25 per page (MLB: 36,000+)
  • ✓ Check hasNextPage in meta
  • ✓ Use totalPages to show progress

Quota in every response

Most responses include your monthly quota in meta.rateLimit; on HTTP 429, honor the Retry-After header:

"meta": {
  "rateLimit": {
    "limit": 50000,
    "remaining": 49995,
    "reset": 1767225600000  // quota reset, Unix epoch ms
  }
}

WebSocket Support (paid plans)

Stream real-time sports data directly to your application. Connect once, then subscribe to the event types you care about and optionally filter by sport, league, or a specific game so you only receive the notifications you need.

Note: WebSocket support is included with every paid plan (free tier gets a 500-message monthly preview). Get your API key and connection URL from your dashboard.

How it works

  1. Get a token — POST to /api/websocket/auth with your API key. You receive a WebSocket URL and a short-lived token (1 hour).
  2. Connect — Open a WebSocket to the URL and pass the token (e.g. ?token=... or Authorization: Bearer ...).
  3. Subscribe — Send a subscribe message with an event type and optional filters (sport, league, eventId). You can subscribe to multiple event types and filter sets.
  4. Receive — The server pushes JSON messages when events occur. Each message has a type, timestamp, and data payload.

1. Get a WebSocket token

Call our auth endpoint with your API key. The response includes the WebSocket server URL and a token valid for 1 hour.

const response = await fetch('https://realtimesportsapi.com/api/websocket/auth', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
});
const { data } = await response.json();
// data.url  - WebSocket server URL (use wss:// in production)
// data.token - Token (expires in 1 hour)

2. Connect

Open a WebSocket to data.url and pass the token. For long tokens, some clients support Authorization: Bearer <token> on the handshake instead of the query string.

const ws = new WebSocket(`${data.url}?token=${data.token}`);

ws.onopen = () => console.log('Connected');
ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  if (msg.type === 'pong' && msg.data?.message) console.log(msg.data.message);
  else if (msg.error) console.error(msg.error);
  else console.log(msg.type, msg.data);
};
ws.onclose = () => console.log('Disconnected');

3. Event types: what you can subscribe to

Choose one or more event types. You can use either the canonical or legacy name; both work the same.

Event typeAlso acceptedWhen you get notified
event_score_changescore_updateA score changes in a live game (goal, point, etc.)
event_live—A game transitions to live
event_status_changestatus_changeGame status changes (e.g. scheduled → in-progress → final)
event_playplay_eventA new play-by-play event (play, goal, card, etc.)
event_play_corrected—Opt-in. A play we already sent was revised by the source (e.g. a catch overturned, a scorer changed). Same payload as event_play with corrected: true and the original play id: replace the play you stored. Webhook name: event.play_corrected.
event_final—A game ends
event_odds_change—Betting odds for an event change significantly

4. Filtering: control what you receive

When you subscribe, you can omit filters to get all events of that type, or narrow by sport, league, and/or eventId. The more filters you set, the more specific the stream.

  • No filters — You receive every event of that type across all sports and leagues.
  • sport + league — Only events for that league (e.g. all NFL games, all Premier League games).
  • eventId — Only that single game. Use with sport/league if you want to be explicit.

Example sport / league values: football / nfl, basketball / nba, soccer / usa.1 or eng.1, hockey / nhl, baseball / mlb. Same values as in the REST API.

// All score changes, all sports
ws.send(JSON.stringify({ type: 'subscribe', event: 'event_score_change' }));

// Only NFL score changes
ws.send(JSON.stringify({
  type: 'subscribe',
  event: 'event_score_change',
  filters: { sport: 'football', league: 'nfl' }
}));

// Only one game (e.g. play-by-play for event 401772982)
ws.send(JSON.stringify({
  type: 'subscribe',
  event: 'event_play',
  filters: { sport: 'football', league: 'nfl', eventId: '401772982' }
}));

// Optional: throttle repeating status (clock) updates (default 'asap' = immediate)
// frequency: 'asap' | '30s' | '1m' | '5m' | '10m' | '30m'
ws.send(JSON.stringify({
  type: 'subscribe',
  event: 'event_status_change',
  filters: { sport: 'hockey', league: 'nhl' },
  frequency: '30s'
}));

How frequency works: it only conflates repeating state updates, and it does so per game. With a value other than asap, an event_status_change where the game state does not change (clock, period or detail updates) and event_odds_change are held, and only the latest one per game is sent at the next tick, so you get at most one of each per game per window (e.g. at 30s, at most 120 status updates per game per hour). Ticks are fixed server-side intervals. event_play, event_score_change, event_live, event_final and any status change where status.state changes (pre → in, in → post, Postponed, Delayed, etc.) are always delivered immediately, and a state change discards that game's held clock update so you never receive an older one after it. Every delivered message counts once toward usage. An unknown value is rejected with an INVALID_FREQUENCY error and no subscription is created. Webhooks follow the same rules. Events re-sent by a resume replay are not conflated.

5. What you receive: message format

Every message from the server is JSON with type and timestamp. For event notifications, type matches the event type (e.g. event_score_change) and data contains the payload. Example score change:

{
  "type": "event_score_change",
  "timestamp": "2026-02-18T12:05:00.000Z",
  "data": {
    "eventId": "401772982",
    "sport": "football",
    "league": "nfl",
    "name": "Team A at Team B",
    "homeTeam": { "id": "...", "name": "Team B", "score": 14 },
    "awayTeam": { "id": "...", "name": "Team A", "score": 7 },
    "previousScore": { "home": 7, "away": 7 }
  }
}

Other server message types: subscribed / unsubscribed (after you subscribe/unsubscribe), pong (welcome or response to your ping), error (with error.code and error.message).

Concurrent connections: Free 1, Starter 2, Growth 3, Pro 5, Scale 25 per account. Over the limit, the newest connection gets an error message and is closed with code 4008; existing connections are never closed for this. On a monthly paid plan with overage on, you can open extra connections up to 3× your plan's limit at $0.02 per extra connection-hour (metered in 6-minute blocks, counted toward your monthly overage cap; on by default, can be turned off in the dashboard). When the cap is reached, new extras are refused and existing extra connections close (newest first); your within-plan connections stay open.

Keepalive: The server sends a WebSocket ping every 15 seconds so connections stay open behind proxies. If your client still disconnects after a few minutes, send { "type": "ping" } every 1–2 minutes; the server responds with pong.

6. Unsubscribe and full example

To stop receiving an event type, send unsubscribe with the same event and filters you used to subscribe.

class RealtimeSportsWebSocket {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.ws = null;
    this.token = null;
  }

  async connect() {
    const res = await fetch('https://realtimesportsapi.com/api/websocket/auth', {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' }
    });
    const { data } = await res.json();
    this.ws = new WebSocket(`${data.url}?token=${data.token}`);

    this.ws.onopen = () => {
      this.subscribe('event_score_change', { sport: 'football', league: 'nfl' });
      this.subscribe('event_play', { sport: 'football', league: 'nfl', eventId: '401772982' });
    };
    this.ws.onmessage = (e) => {
      const msg = JSON.parse(e.data);
      if (msg.type === 'event_score_change') console.log('Score:', msg.data);
      else if (msg.type === 'event_play') console.log('Play:', msg.data);
      else if (msg.type === 'error') console.error(msg.error);
    };
    this.ws.onclose = () => setTimeout(() => this.connect(), 5000);
  }

  subscribe(eventType, filters = {}, frequency = 'asap') {
    if (this.ws?.readyState === WebSocket.OPEN)
      this.ws.send(JSON.stringify({ type: 'subscribe', event: eventType, filters, frequency }));
  }
  unsubscribe(eventType, filters = {}) {
    if (this.ws?.readyState === WebSocket.OPEN)
      this.ws.send(JSON.stringify({ type: 'unsubscribe', event: eventType, filters }));
  }
  disconnect() { this.ws?.close(); }
}

const client = new RealtimeSportsWebSocket('YOUR_API_KEY');
client.connect();

Webhooks (paid plans)

Receive real-time notifications when sports events occur. Webhooks send HTTP POST requests to your server when events happen, eliminating the need to poll our API.

Note: Webhooks are available on every paid plan. Upgrade from your dashboard.

What are Webhooks?

Webhooks allow you to receive real-time notifications when events occur in sports games. Instead of polling our API, we send HTTP POST requests to your server when:

  • A game goes live
  • Scores change
  • Game status changes (scheduled → in-progress → final)
  • New plays occur
  • A game ends

Available Event Types

event.live

Triggered when a game goes live (status changes to in-progress)

event.score_change

Triggered when the score changes during a game

event.status_change

Triggered when game status changes (scheduled → in-progress → final)

event.play

Triggered when a new play occurs in a game

event.final

Triggered when a game ends (status changes to final)

Webhook Payload Format

Each webhook request includes the following headers and payload:

// Headers
X-Webhook-Signature: 3f9a1c...        // hex HMAC-SHA256 of the raw body (no prefix)
X-Webhook-Event: event.score_change     // Event type
Content-Type: application/json
User-Agent: RealtimeSportsAPI/1.0

// Payload
{
  "event": "event.score_change",
  "timestamp": "2024-01-15T20:30:00.000Z",
  "data": {
    "eventId": "401772982",
    "sport": "football",
    "league": "nfl",
    "homeTeam": {
      "id": "2",
      "name": "Kansas City Chiefs",
      "score": 24
    },
    "awayTeam": {
      "id": "3",
      "name": "Buffalo Bills",
      "score": 17
    },
    "status": {
      "type": {
        "id": "1",
        "name": "STATUS_IN_PROGRESS",
        "state": "in",
        "completed": false
      },
      "period": 3,
      "clock": {
        "value": 1200,
        "displayValue": "20:00"
      }
    }
  }
}

Signature Verification

Always verify webhook signatures to ensure requests are from Realtime Sports API. The X-Webhook-Signature header is the lowercase hex HMAC-SHA256 of the raw request body bytes, keyed with your webhook's signing secret. There is no sha256= prefix.

Managing webhooks from your own code (API key): the endpoints below authenticate with your API key (Authorization: Bearer YOUR_API_KEY), require a paid plan, and return { success, data, meta } (errors: { success: false, error: { code, message } } with 400/403/404/500). Webhook objects never include the secret except in the create response.

GET    /api/v1/webhooks                      list
POST   /api/v1/webhooks                      create   { url, events[], leagues?[], frequency? }  -> includes secret (once)
GET    /api/v1/webhooks/{id}                 read
PATCH  /api/v1/webhooks/{id}                 update   any of { url, events, leagues, frequency, active }
DELETE /api/v1/webhooks/{id}                 delete
GET    /api/v1/webhooks/{id}/secret          reveal signing secret
POST   /api/v1/webhooks/{id}/secret          rotate signing secret
POST   /api/v1/webhooks/{id}/test            { eventType? } or { deliveryId? }  -> sends a signed sample / replays
GET    /api/v1/webhooks/{id}/deliveries      recent failed deliveries

# Disable (takes effect immediately) / re-enable
curl -X PATCH https://realtimesportsapi.com/api/v1/webhooks/WEBHOOK_ID \
  -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"active": false}'

# Response
{ "success": true,
  "data": { "id": "…", "url": "…", "events": ["event.live","event.final"], "leagues": ["nfl"], "frequency": "asap",
            "active": false, "consecutiveFailures": 0, "deactivatedReason": null, "deactivatedAt": null,
            "createdAt": "…", "updatedAt": "…" },
  "meta": { "rateLimit": { "limit": 60, "remaining": 59, "reset": 1700000000000 } } }

Enable / disable semantics: active: false stops deliveries immediately — the delivery service watches the webhook registry in real time (typically under a second); a request already in flight to your endpoint is not recalled. active: true re-enables, and this also clears an automatic deactivation: deactivatedReason is reset and the consecutive-failure counter returns to 0. A webhook is auto-deactivated (deactivatedReason: "repeated_failures") after 5 consecutive failed deliveries (non-2xx, timeout > 10s, or connection error); any successful delivery resets the counter. Management calls are rate-limited like other API calls but do not count toward your monthly call quota.

Testing and delivery history: from the webhook card in your dashboard, the test panel sends a signed, production-shaped sample of any event type (event.live, score_change, status_change, play, final) to your URL — with an extra X-Webhook-Test: true header and data.test: true — and shows the HTTP status, latency, the exact request headers/body sent, and your response body. Failed deliveries can be replayed with their original payload (X-Webhook-Test: replay). API equivalent: POST /api/webhooks/{webhookId}/test with { firebaseUid, eventType } or { firebaseUid, deliveryId }. Show failed deliveries (or GET /api/webhooks/{webhookId}/deliveries) lists recent failed attempts with the response code and body; successful deliveries aren't stored individually but count toward monthly usage. After 5 consecutive failures a webhook is automatically deactivated — re-enable it from the dashboard once your endpoint is fixed. Editing a webhook's URL, events or leagues never changes its secret; only Rotate does.

Where to find the secret: it is returned once in the secret field when you create a webhook via the API, and you can reveal or rotate it at any time from the webhook card in your dashboard ("Show secret" / "Rotate"), or via GET /api/webhooks/{webhookId}/secret?firebaseUid=…. Compute the HMAC over the raw body — if you re-serialize a parsed JSON object the bytes may differ and the check will fail.

// Node.js example
const crypto = require('crypto');

// rawBody must be the exact bytes received (Buffer or string), not a re-serialized object
function verifyWebhookSignature(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  const provided = String(signature || '');
  if (provided.length !== expected.length) return false;

  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided));
}

// In your webhook endpoint — use a raw body parser on this route
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const secret = process.env.RTS_WEBHOOK_SECRET; // From the dashboard ("Show secret")

  if (!verifyWebhookSignature(req.body, signature, secret)) {
    return res.status(401).send('Invalid signature');
  }

  // Process webhook
  const { event, data } = JSON.parse(req.body.toString('utf8'));
  console.log(`Received ${event}:`, data);

  res.status(200).send('OK');
});

Complete Example

// Express.js webhook endpoint example
const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

// Your webhook secret (from webhook creation)
const WEBHOOK_SECRET = 'your-webhook-secret';

function verifySignature(payload, signature) {
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  const provided = signature.replace('sha256=', '');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(provided)
  );
}

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const eventType = req.headers['x-webhook-event'];
  
  // Verify signature
  if (!verifySignature(req.body, signature)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  const { event, timestamp, data } = req.body;
  
  // Handle different event types
  switch (event) {
    case 'event.live':
      console.log(`Game went live: ${data.name}`);
      // Notify users, update database, etc.
      break;
      
    case 'event.score_change':
      console.log(`Score update: ${data.awayTeam.name} ${data.awayTeam.score} - ${data.homeTeam.score} ${data.homeTeam.name}`);
      // Update scores in your app
      break;
      
    case 'event.status_change':
      console.log(`Status changed: ${data.status.type.name}`);
      // Update game status
      break;
      
    case 'event.play':
      console.log(`New play: ${data.play.text}`);
      // Display play in your app
      break;
      
    case 'event.final':
      console.log(`Game ended: Final score ${data.awayTeam.score} - ${data.homeTeam.score}`);
      // Show final results
      break;
  }
  
  // Always return 200 OK
  res.status(200).json({ received: true });
});

app.listen(3000, () => {
  console.log('Webhook server listening on port 3000');
});

Best Practices

  • ✓ Always verify webhook signatures to prevent unauthorized requests
  • ✓ Return 200 OK immediately, then process the webhook asynchronously
  • ✓ Use HTTPS for your webhook endpoint
  • ✓ Implement idempotency to handle duplicate deliveries
  • ✓ Set up retry logic for failed webhook deliveries
  • ✓ Monitor webhook delivery status in your dashboard
  • ✓ Keep your webhook secret secure and never expose it in client-side code

Python Example

import requests

API_KEY = 'YOUR_API_KEY'
BASE_URL = 'https://realtimesportsapi.com/api/v1'

class RealtimeSportsAPI:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = BASE_URL
        self.headers = {'Authorization': f'Bearer {api_key}'}
    
    def get_sports(self):
        """Get all available sports"""
        response = requests.get(f'{self.base_url}/sports', headers=self.headers)
        return response.json()['data']
    
    def get_leagues(self, sport):
        """Get leagues for a sport"""
        response = requests.get(
            f'{self.base_url}/sports/{sport}/leagues',
            headers=self.headers
        )
        return response.json()['data']
    
    def get_live_games(self, sport, league):
        """Get live games for a league"""
        response = requests.get(
            f'{self.base_url}/sports/{sport}/leagues/{league}/events/live',
            headers=self.headers
        )
        return response.json()['data']
    
    def get_plays(self, sport, league, event_id, page=1, limit=25):
        """Get plays for an event (paginated)"""
        response = requests.get(
            f'{self.base_url}/sports/{sport}/leagues/{league}/events/{event_id}/plays',
            params={'page': page, 'limit': limit},
            headers=self.headers
        )
        return response.json()

# Usage
api = RealtimeSportsAPI(API_KEY)

# Discover sports
sports = api.get_sports()
print(f"Available sports: {[s['name'] for s in sports]}")

# Get live NFL games
nfl_games = api.get_live_games('football', 'nfl')
for game in nfl_games:
    print(f"{game['name']}: {game['awayTeam']['score']} - {game['homeTeam']['score']}")

# Get plays with pagination
plays_response = api.get_plays('football', 'nfl', '401772982', page=1)
print(f"Page 1: {len(plays_response['data'])} plays")
print(f"Total: {plays_response['meta']['pagination']['total']} plays")
print(f"Total pages: {plays_response['meta']['pagination']['totalPages']}")