Maxwell Grody

Game feed warehouse / a nested delivery, unnested

Query the game feed warehouse

Major League Baseball publishes one JSON document per game. A game holds plays, a play holds events, an event holds the pitch's coordinates and everything that happened between pitches. This pipeline lands every regular-season document of 2025 as delivered, 2,395 games of it, and dbt unnests them in SQL into games, plays, pitch events, runner movements and fielding credits, with 39 tests on the way. The tables are served here as Parquet and SQL runs in your browser. The event-level tables are one sample week; the gold aggregates and the game-level tables are the whole season.

The project page says why it was built and how it would run at ten thousand games. The pitch warehouse (opens in a new tab) is the same shape over Baseball Savant's flat export, and Gamebot (opens in a new tab) over a TV show.

Lineage

Every table, by layer, with the dependencies dbt declares. Click a table for its columns, its tests and their status from the last build, and for silver and gold the SQL that builds it. Bronze is the document as delivered, as jsonb, with the pull that brought it. Silver is jsonb_array_elements over the layer above, one cast per column, nothing filtered. Gold is what an analyst reads. A dashed table is served as a sample.

SQL

DuckDB runs in the page and reads each table's Parquet file on demand, so a query fetches only what it names. Tables are bronze.games_raw (one game's document, so the unnesting can be tried by hand), silver.plays, silver.pitch_events, gold.pitches and the rest of the lineage map. Clicking a table name in the sidebar writes a starter query.

The three questions

Fixed before the first game was pulled, one SQL file each, run over the whole season. Each tab shows the question, why a coach would ask it, the query, the first rows of the result, and what the result cannot tell you. The queries also run here, against the served tables, from the SQL panel.

One play, event by event

Pick a game from the sample week and a play in it. Below are the events of that play in order, from silver.pitch_events: every pitch with its call, speed and location, and everything between pitches, pickoffs, mound visits, timeouts, with the runner movements from silver.runners underneath. This is the nesting the pipeline exists to flatten, one level at a time.

Tests

Every dbt test in the project and its status from the last build, by table. The ones the design asked for are here. Runs per game from the plays equal the linescore, pitches per game equal the boxscore, the feed's two statements of the base state agree, every event has a play and every play a game, every scheduled game has a document or is listed, and every game under the tracking floor is listed rather than dropped.

Data: the MLB Stats API game feed, public and unauthenticated, one request a second with a stated user agent. Bronze holds the documents as delivered. The site serves derived tables and one sample week of event-level rows, never a full copy. Built by Max Grody.