A web app for analyzing MLB pitcher statistics: search for a pitcher, list games within a date range, break down the pitch mix per game (including ERA/IP/W-L pulled from the official MLB boxscore), and compare multiple games side by side.
Built on top of the fantastic pybaseball library, which makes working with Statcast data (pitch type, velocity, spin rate, etc.) in Python genuinely pleasant — without pybaseball, this project would have taken a lot more effort.
pitcher-stats/
├── backend/ # FastAPI backend
└── frontend/ # SvelteKit frontend
A FastAPI service that processes pybaseball data and exposes it via a REST API.
Features:
- Pitcher search by partial name match, based on the Chadwick registry (cached locally)
- List games for a pitcher within any given date range
- Pitch-mix analysis per game: count, average velocity, and share per pitch type
- Boxscore enrichment: innings pitched, earned runs, game ERA, and win/loss/no-decision from the official MLB Stats API
- In-memory caching for boxscore lookups (completed games never change)
- Automatic interactive API documentation via Swagger UI
Tech stack: FastAPI · pybaseball · pandas · requests · Uvicorn
cd backend
python3 -m venv .venv
source .venv/bin/activate
pip install -e .uvicorn baseball_stats.api.main:app --reload- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
docker build -t pitcher-stats-api ./backend
docker run -p 8000:8000 -e PORT=8000 pitcher-stats-apiOr via Docker Compose from the repo root:
docker compose up --buildDeployed on Render (Docker-based).
| Method | Path | Description |
|---|---|---|
GET |
/pitchers/search?name=... |
Search for a pitcher by name |
GET |
/pitchers/{pitcher_id}/games?start=...&end=... |
List games within a date range |
GET |
/pitchers/{pitcher_id}/games/{game_pk}?start=...&end=... |
Pitch mix + boxscore data for a single game |
Full, interactive reference available via Swagger UI.
Deployed on Render (Docker-based).
A SvelteKit app that talks to the backend API.
Tech stack: SvelteKit · TypeScript · TailwindCSS
Features:
- Pitcher search with disambiguation when multiple matches are found
- Date-range-based game listing (only enabled once a pitcher is selected)
- Multi-select of games (including select-all/none) for combined viewing
- Pitch-mix table per game with ERA/IP/decision
cd frontend
npm installnpm run devRuns on http://localhost:5173 by default — the backend needs to be running in parallel (CORS is enabled for localhost:5173 as well as the production Vercel domain).
Deployed on Vercel via GitHub import (Root Directory: frontend).
- The innings-pitched calculation on the CLI path (without boxscore enrichment) is an approximation based on Statcast events, not an exact official figure.
- The in-memory cache for boxscore data is lost on server restart (not an issue at low load; could be replaced with SQLite later).
- The MLB Stats API (
statsapi.mlb.com) is unofficial/undocumented and may change at any time. - Chadwick registry entries for active players are sometimes missing a career-end year (NaN) — handled explicitly in the backend response.
This project wouldn't have been possible without the genuinely excellent work of the pybaseball maintainers and contributors — thank you. Thanks also to FastAPI for making the backend a joy to build (Swagger UI for free!) and to Svelte for such a clean, low-ceremony approach to building the frontend.