Understand every pitch.
Mound retrieves, analyzes and visualizes MLB pitch-level data from the command line or a few lines of Python. Start from a player’s name. No MLB IDs to look up, no undocumented APIs to learn.
$pip install mound$ mound arsenal "Roki Sasaki" --game 825051
pitches velocity spin_rate release_extension horizontal_break induced_vertical_break whiff_rate chase_rate
pitch_type
four-seam fastball 35 98.8 2427.1 7.1 11.2 16.9 27.3 6.2
splitter 32 90.2 868.1 7.2 5.3 1.0 13.6 57.9
slider 14 87.1 2099.3 7.1 3.0 0.1 40.0 33.3
forkball 5 88.2 758.2 7.1 2.8 -2.0 50.0 0.0One start, one table. The four-seamer lives in the zone and gets missed when hitters swing at it; the splitter’s whole job is to be chased below it, and it was, 57.9% of the time.
Find answers to questions about a pitcher’s arsenal.
- 01
How many splitters did Roki Sasaki throw against the Diamondbacks last night?
- 02
How often has he thrown it relative to his other pitches over his last four starts?
- 03
What does its location look like over that period?
- 04
How does he attack one particular hitter, and does that hitter chase the splitter?
Mound answers all four with a few CLI commands or a few lines of Python, and it never asks you for an MLB player ID to get there.
A small surface, pointed at one job.
Eight commands and one Python object, sharing the same implementation underneath. Anything you can do in the shell, you can also do in a script.
mound search "Roki Sasaki"Start from a name
Resolve a player to an MLB ID, accents optional. Every other command takes the name directly, so you rarely need the ID at all.
--last 4 --pitch splitterFilter how you'd ask
Last N appearances, a date range, one game, one pitch type, one batter side, one at-bat, one exact pitch. Filters compose freely.
mound arsenalStuff and results together
Velocity, spin and movement next to whiff and chase rate, so how nasty a pitch was gets answered from three angles in one table.
mound zone --kind heatmapCharts that arrive finished
A headline, dek and source render around the strike zone. Scatter, heatmap or KDE, optionally split into vs-LHB and vs-RHB panels.
--batter perdomoMatchups from either side
Pitcher(batter=...) and Batter(pitcher=...) return the same pitches. Pick whichever player the question is actually about.
mound video --limit 1Broadcast clips, by pitch
Download the video for one pitch, one at-bat or a whole filtered collection, resolved straight from each pitch's own ID.
--cacheA cache that can't go stale
A finished game never changes, so a hit is always good. A game in progress is never written, so tonight's fourth inning never sticks.
--export roki.csvExport anywhere
CSV, JSON or Parquet from the CLI, or to_frame() for a pandas DataFrame with every field the feed returned.
Three angles on “how nasty was it?”
| Pitch | No. | Velo | Spin | Whiff | Chase |
|---|---|---|---|---|---|
| four-seam fastball | 35 | 98.8 | 2427.1 | 27.3% | 6.2% |
| splitter | 32 | 90.2 | 868.1 | 13.6% | 57.9% |
| slider | 14 | 87.1 | 2099.3 | 40.0% | 33.3% |
| forkball | 5 | 88.2 | 758.2 | 50.0% | 0.0% |
- Swing rate
- Swings over every pitch thrown. How often hitters were tempted at all.
- Whiff rate
- Swings that missed, over swings — Baseball Savant’s own convention, not misses over every pitch. A pitch rarely swung at can still post a high number.
- Chase rate
- Swings over pitches outside the zone. Read from location geometry rather than the strike ruling, because those are genuinely different things.
Charts that arrive finished.
A headline, a dek and a source line render around the strike zone itself, so a plot is publishable the moment it comes out of the function. All three are generated for you and all three are overridable.

kind="scatter"The default. Points colored by pitch type, using the same fixed palette across every chart.

kind="heatmap"Binned density for larger samples. No colorbar — darker means more pitches, and the panel stays aligned with every other kind.

split_by="stand"Location isn't mirrored for handedness, so mixing lefties and righties in one panel blurs the picture. Split it into a pair, each with its own zone and pitch count.
Then watch the pitch that did the damage.
Every pitch carries a pitch_id that doubles as the play ID on a Baseball Savant clip page. Any pitch you can filter to is a pitch you can download.
$ mound video-id a08dfb7d-1acd-3776-a6d8-0f5e80cdb0c6 --out clips/perdomo_triple_aug8.mp4$ mound video-id 13f4b8d1-39f4-3499-b696-8a3311899fde --out clips/carroll_triple_aug8.mp4Geraldo Perdomo Triple
Four-seam fastball, 96.5 mph, middle third of the zone and 0.06 feet off the center of the plate.
Corbin Carroll Triple
Four-seam fastball, 98.6 mph, middle third of the zone and 0.07 feet off the center of the plate.
Back-to-back triples off Edwin Díaz in the ninth at Chase Field on Aug. 8, 2026. Both were four-seam fastballs in the middle third of the zone, less than an inch off the center of the plate — the two pitches at the center of the blown-save walkthrough.
Two minutes to your first pitch.
Install it, point it at a name and start asking. The worked example goes all the way from a pitcher’s postgame quote to the video of the pitches that disproved it.
$pip install mound$mound search "Roki Sasaki"$mound arsenal "Roki Sasaki" --last 4Add pip install "mound[viz]" for KDE heatmaps, or "mound[parquet]" for Parquet export.