Inspiration

We love watching games where a single possession or moment can swing the outcome.
We wanted a tool that could answer questions like:

  • How does a player really perform in late game clutch situations
  • What is happening right now in this live game
  • What is likely to happen next for a specific player

Most sports APIs either give raw stats or simple box scores. We wanted a focused API that gives situation level context, live insights, and is simple for developers to plug into their own apps and dashboards.

What it does

GAMETIME.API is an HTTP and WebSocket API that:

  • Creates historical situations for NBA and soccer by filtering on sport, player, time, score, and game context
  • Aggregates stats into totals, per start averages, and a reliability grade
  • Lists live games and players with current activity, including touches
  • Computes live NBA player projections that blend current game stats with historical context
  • Exposes stateful live sessions where clients can subscribe to a continuous stream of events, insights, and narration
  • Lets users export situations to CSV for offline analysis and scouting

How we built it

  • Backend framework: Fastify in TypeScript for performance and JSON schema validation
  • Sports data: Sportradar NBA v7 and Soccer v4 APIs for schedules, play by play, and live feeds
  • Core services:
    • A situation builder that pulls and filters games, then normalizes stats
    • Adapters for NBA and soccer that share a common interface but handle sport specific rules
    • A live session service that tracks sessions in memory and fans out messages over WebSockets
  • Config and runtime:
    • Environment variables via dotenv with .env.example as a template
    • tsx for watch mode in development and tsc for a build step
  • Error handling:
    • A Fastify error handler that standardizes responses into a single error envelope with codes and request ids

Challenges we ran into

  • Data shape differences between NBA and soccer:
    • Different notions of time, score states, and play by play formats
    • We had to design adapters that hide these differences while keeping the API surface uniform
  • Upstream failures:
    • Handling Sportradar 403s and timeouts and surfacing them as meaningful UPSTREAM_ERROR and UPSTREAM_TIMEOUT responses
  • Balancing flexibility and validation:
    • Defining JSON schemas that are strict enough to catch bad requests, but still easy to use
  • Live session modeling:
    • Designing a message format that works for events, insights, and narration without versioning chaos

Accomplishments that we are proud of

  • A clean, sport aware REST surface with consistent naming under /v1
  • End to end flows that feel good to use from a terminal:
    • Create situation → get analysis → export CSV
    • List live games → list players → get projections or open a WebSocket stream
  • A clear error model:
    • Every failure returns a code, message, details, and request id
    • Client side bugs and upstream problems are easy to tell apart
  • Developer friendly docs:
    • A single README with copy pasteable curl commands and explanations for each endpoint

What we learned

  • Good sports APIs are as much about context as raw stats
  • Strong schema validation and a consistent error envelope make debugging much faster
  • Keeping a thin but well designed adapter layer lets you support multiple sports without duplicating logic
  • WebSockets are easiest to work with when you keep the message envelope stable and push evolution into the data field

What is next for GAMETIME.API

  • Persistent storage:
    • Move situations and live sessions from in memory storage to Redis or Postgres
  • Pagination and richer querying:
    • Add pagination and filters for lists of situations and live objects
  • More sports and metrics:
    • Extend the adapter pattern to more leagues and add advanced stats and shot charts
  • Hosted docs and sandbox:
    • Ship a hosted documentation page and a small demo UI on top of the API
  • Production readiness:
    • Add auth, rate limiting, and horizontal scaling with shared pub sub for WebSocket fan out

Built With

Share this project:

Updates