NBA Stats Desk
An NBA data platform that connects BigQuery and dbt to a workbench for asking basketball questions, exploring players, and comparing performance across seasons.
On this page
Overview
NBA Stats Desk is where I ask basketball questions and dig into player stats. Underneath, it's a small version of a modern AI data stack: an ELT pipeline with data contracts, dbt models and a semantic layer in BigQuery, and an AI agent on top that answers questions using governed metrics instead of writing its own SQL.
The FastAPI app serves Ask, player profiles, playoff performances, trends, and a 3D map of player styles. The default season is currently 2025-26, and Ask also supports historical questions within the available 2023-24 through 2025-26 data, with coverage depending on the question and source.
Core flow:
NBA API + injury reports
-> Airflow + source contracts
-> GCS + BigQuery
-> dbt models + semantic layer
-> FastAPI app
Ask agent: tool calling over governed metrics
Players, Performance, Trending, ProfilesWhy I Built It
Big NBA fan here, and enjoy looking at stats from the game.
I started this project because I wanted to create a full end-to-end NBA data platform, not just run a few analyses and stop there. I wanted something that could handle ingestion, storage, modeling, data quality, and consistent outputs in a way that feels productionized and letting me own the data.
Half of it is me being a basketball fan, the other half is me wanting to build things with data and AI.
How It Evolved
The project grew one layer at a time, ending up close to a modern data stack.
Notebook to ELT pipeline (Feb–Mar 2026). It started as a notebook that loaded game logs into BigQuery and had Claude write a recap. I rebuilt it as a proper pipeline: Airflow for orchestration, GCS and BigQuery for storage, dbt for bronze/silver/gold models, and Terraform for infrastructure.
Data contracts and quality (Apr–May 2026). Source contracts, quarantined records, freshness tracking, and atomic publishing mean a bad refresh never replaces good data. I kept the analysis in deterministic SQL until this foundation felt solid.
Semantic layer (Sep 2026). Metrics moved onto one shared semantic contract, so Ask and every page calculate stats the same way. I also backfilled two more seasons and retired the old snapshot pipeline.
Agentic layer (Jun–Sep 2026). Ask brought the LLM back as an agent planner on top of the semantic layer. It uses tool calling over read-only tools with structured, validated plans, and it never writes SQL. It resolves player names, asks to clarify when a name is ambiguous, and declines questions the data can't answer.
Evals and guardrails (Sep–Oct 2026). An evaluation harness over frozen evidence, plus human review, checks that answers stay grounded. Request telemetry and bounded model calls and streaming keep it reliable across providers. I also dropped the Redshift sync to keep one warehouse.
How It Works
- Ingest and orchestrate: a daily Airflow run pulls game logs, schedules, line scores, player reference data, shot locations, and official injury reports into GCS and BigQuery
- Data contracts: sources are checked against contracts, rejected records are quarantined for review, and validated data is published together so a failed refresh keeps the last good version
- Semantic layer: dbt models and a shared semantic contract mean Ask and the player pages calculate stats the same way
- Agent planning: Ask resolves the player, season, phase, and dates, then plans with tool calling over allowlisted read-only tools. Plans are schema-validated, and the model never writes SQL
- Grounded answers: answers, tables, and charts are built in code from the collected evidence. A separate visualization agent only picks from charts the server has already built
- Evals and guardrails: an evaluation harness and human review check answers, while telemetry and bounded model calls keep requests reliable
Features
- Ask: answers basketball questions with an overview, key takeaways, percentiles, and charts. It asks to clarify ambiguous names, says when the data can't answer something, and runs on OpenAI, Anthropic, or OpenRouter
- Player comparisons: compare two players side by side on core stats, shooting efficiency, ball security, and per-36 production over the same games
- Teammate comparisons: compare a player's observed stats with and without a teammate, with the dates and available sample shown
- Players: search players and open profiles with trends, game logs, and statistical breakdowns
- Performance and Trending: compare playoff games against season baselines, or see who is producing more over recent games
- Profiles: explore player styles and nearest matches in a 3D map
Project Status
Active project. I keep using it to test out new data ideas and dig further into NBA data.