Bet Tracker Sports API Update

[09/16/2025]

Problem Statement

API Sports has been inconsistent, prompting us to improve Bet Tracker’s consistency by using a Sports Radar API. In general, Bet Tracker is very closely coupled with the response structure of a third party API, which would make changing our provider in the future difficult.

Proposed Solution

All these changes should apply for the NFL trackers only to allow us to develop and test incrementally.

Refactor NFL bet tracker to pull game data from our Redis cache instead of API Sports or any other sports data provider. Its processing will be very similar besides this. Some accessed fields may have to change to match our own Redis’ data format.

A separate process running on the same EC2 instance will be responsible for updating our Redis cache with game data including current quarter, each team’s scores, and relevant scoring events such as touchdowns.

Outcome Generator will also need to be changed to reference game data from both APIs.

Architectural & Technical Details

This covers the techincal details of 3 separate conceptual services, the Bet Tracker, the Outcome Generator, and a new service referred to as the Games Tracker. We start with this new service to clarify the structure of the Redis cache that will influence the decisions made in the other services.

Games Tracker

A Python process similar to the bet tracker, which is responsible for updating our games cache with the current game statuses. It will fetch from both APIs and use the most up-to-date data to set the values in our cache. It does not resolve bets or edit any database values directly.

On startup and at midnight each day, the games tracker will pull the list of all games for each league (for now only NFL) for the next day from our Redis cache. The tracker will loop regularly and at each iteration perform roughly the following steps:

  • Determine active games based on the timestamps of each game happening today. This will be done with a state machine including an idle and standby handler similar to the Bet Tracker.
  • For each active game,
    • We pull from the Sports Radar API (most likely either BoxScore or Play-by-Play).
    • At a slower rate, we pull from API Sports to validate our current data.
    • If API Sports has a greater score for either team or a later status/progression, we assume that Sports Radar is behind, and use API Sports’ data instead
  • Tranform the chosen response into the structure described below and store it in the Redis cache with the key equal to an ID representing this game uniquely. This will be the Radar Sports id if available. If for some reason it’s not, we’ll fall back to the API Sports one.

The expected games cache will have this format:

{
    "api_sports_id": int,
    "sports_radar_id": str,
    "timestamp": int (for when game is set to start),
    "status": str (enum: started, live, finished),
    "progress": int (current quarter, round, half depending on sport),
    "league": str (name),
    "home": str (name),
    "away": str (name),
    "score": [
      {
        "home": int,
        "away": int
      }
    ],
    "scoring_events": [
      {
        "team": str (enum: home, away),
        "type": str (sport-specific i.e. touchdown, field goal),
        "timestamp": int
      }
    ]
}

Bet Tracker

Requests for games responses will now be to our own Redis cache. The inital games list will be the games in the next 24 hours resulting from using the league as a key. Requests for a specific game will be made using the key equal to the one used in the Games Tracker. For now, we expect this to be the Radar Sports ID, and the API Sports ID as a fallback if the first one returns no response.

It would also be heplful, but not necessary, to refactor the way we determine “next” bets to use the “scoring_events” field instead of calculating score difference in between calls. This would help us potentially eliminate the need to store the previous game state in-memory.

Outcome Generator and Outcome Model

Outcome model will be edited to include the new field “sports_radar_id”, rename the old field of “game_id” to “api_sports_id”, and remove the “team_id” field for simplicity since it’s unused. The “sport_radar_id” field will be a string set to the id value received from the game objects from SportsRadar’s API response.

The Outcome Generator will make an additional call to SportsRadar through one of the following endpoints to get game data for the following week or entire season.

  • https://developer.sportradar.com/football/reference/nfl-current-week-schedule
  • https://developer.sportradar.com/football/reference/nfl-current-season-schedule

From that, and the calls to API Sports we currently make, we can append the corresponding game IDs to generated outcomes and cache entries. The cache entry generated from here will be mostly the same. The main change is that each game in the entry will have to include the sports radar API as a field.

Next Steps

Begin writing the Games Tracker code to update individual games’ Redis cache entries. We start with this because it is the most disconnected from current app function.

Action Items

  • Create GamesTracker code and test its functionality.
  • Edit Outcome model and Outcome Generator to include Radar Sports id in the model and cache entries
  • Edit NFL Bet Tracker to make requests to cache instead of API directly
  • Get API keys for Sports Radar to test implementation.

Open Questions

Open to partially rethinking the Games Tracker design based on PushFeed availability and usefulness.

Approvals

You need architectural approval from Trace Carrasco

  • Trace Carrasco