Baseball statistics and MLB data for Python: sabermetrics calculations plus a zero-dependency client for the MLB Stats API.
Project description
⚾ pyhomerun
Baseball statistics and MLB data for Python — with zero dependencies.
pyhomerun does two things:
- Sabermetrics — pure functions for batting, pitching, and fielding statistics (AVG, OBP, SLG, OPS, wOBA, wRAA, ERA, FIP, WHIP, Game Score, ...). Plain numbers in, plain numbers out.
- MLB data —
MLBClient, a tiny client for the free, key-less MLB Stats API (players, season stats, teams, rosters, schedules, standings, boxscores).
It is built entirely on the Python standard library — installing it installs nothing else, and the test suite runs with stock Python.
Installation
pip install pyhomerun
Or straight from source:
git clone https://github.com/SRock44/pyhomerun
cd pyhomerun
pip install .
Requires Python 3.9+.
Quick start
Calculate statistics
import pyhomerun as bb
# Classic rate stats
avg = bb.batting_average(hits=200, at_bats=600) # 0.333
obp = bb.on_base_percentage(hits=200, walks=70, hit_by_pitch=5,
at_bats=600, sacrifice_flies=5) # 0.404
tb = bb.total_bases(hits=200, doubles=40, triples=5, home_runs=35) # 355
slg = bb.slugging_percentage(tb, at_bats=600) # 0.592
print(f"{avg:.3f}/{obp:.3f}/{slg:.3f} OPS {bb.ops(obp, slg):.3f}")
# Advanced stats
bb.woba(walks=70, hit_by_pitch=5, singles=120, doubles=40, triples=5,
home_runs=35, at_bats=600, sacrifice_flies=5) # ~0.416
bb.fip(home_runs=18, walks=45, hit_by_pitch=5, strikeouts=190,
innings_pitched=180) # ~3.19
bb.era(earned_runs=3, innings_pitched=bb.innings(6.2)) # 4.05
Fetch MLB data
from pyhomerun import MLBClient
mlb = MLBClient()
# Find a player and pull their season batting line
judge = mlb.search_players("Aaron Judge")[0]
splits = mlb.player_stats(judge["id"], group="hitting", season=2025)
line = splits[0]["stat"]
print(judge["fullName"], line["avg"], line["homeRuns"], line["ops"])
# Today's games
for game in mlb.schedule():
away, home = game["teams"]["away"], game["teams"]["home"]
print(f'{away["team"]["name"]} at {home["team"]["name"]} — {game["status"]["detailedState"]}')
# Standings
for division in mlb.standings(season=2025):
for record in division["teamRecords"]:
print(record["team"]["name"], record["wins"], record["losses"])
Put them together
import pyhomerun as bb
mlb = bb.MLBClient()
player = mlb.search_players("Juan Soto")[0]
s = mlb.player_stats(player["id"], group="hitting", season=2025)[0]["stat"]
singles = s["hits"] - s["doubles"] - s["triples"] - s["homeRuns"]
w = bb.woba(walks=s["baseOnBalls"], hit_by_pitch=s["hitByPitch"],
singles=singles, doubles=s["doubles"], triples=s["triples"],
home_runs=s["homeRuns"], at_bats=s["atBats"],
intentional_walks=s["intentionalWalks"], sacrifice_flies=s["sacFlies"])
print(f'{player["fullName"]} wOBA: {w:.3f}')
API reference
Every function has a full docstring with its formula and a worked example (help(bb.woba)).
Batting
| Function | Statistic |
|---|---|
batting_average(h, ab) |
AVG |
on_base_percentage(h, bb, hbp, ab, sf) |
OBP |
total_bases(h, 2b, 3b, hr) |
TB |
slugging_percentage(tb, ab) |
SLG |
ops(obp, slg) |
OPS |
ops_plus(obp, slg, lg_obp, lg_slg) |
OPS+ (100 = league average) |
isolated_power(slg, avg) |
ISO |
babip(h, hr, ab, k, sf) |
BABIP |
woba(...) |
wOBA (customizable linear weights) |
wraa(woba, pa) |
wRAA (runs above average) |
plate_appearances(...) |
PA |
walk_rate(bb, pa) / strikeout_rate(k, pa) |
BB% / K% |
stolen_base_percentage(sb, cs) |
SB% |
Pitching
| Function | Statistic |
|---|---|
innings(6.2) / innings_from_outs(20) |
Box-score notation → true innings |
era(er, ip) |
ERA |
whip(bb, h, ip) |
WHIP |
fip(hr, bb, hbp, k, ip) |
FIP (customizable constant) |
k_per_9 / bb_per_9 / hr_per_9 / h_per_9 |
Per-9 rates |
k_bb_ratio(k, bb) |
K/BB |
left_on_base_percentage(...) |
LOB% |
game_score(...) |
Bill James Game Score |
Fielding
| Function | Statistic |
|---|---|
fielding_percentage(po, a, e) |
FPCT |
range_factor_per_game(po, a, g) / range_factor_per_9(po, a, inn) |
RF/G, RF/9 |
caught_stealing_percentage(cs, sb) |
CS% |
MLB Stats API client
| Method | Returns |
|---|---|
MLBClient(timeout=10.0) |
— |
.search_players(name) |
Player matches (with MLBAM id) |
.player(player_id) |
Bio for one player |
.player_stats(id, group, stat_type, season) |
Stat splits ("hitting"/"pitching"/"fielding"; "season"/"career"/"yearByYear"/"gameLog") |
.teams(season) / .roster(team_id) |
Teams / active roster |
.schedule(date, team_id) |
Games for a date (default today) |
.standings(season) |
Division standings |
.boxscore(game_pk) / .linescore(game_pk) |
Game details |
.get(path, **params) |
Any other endpoint, as parsed JSON |
All methods return plain dicts/lists parsed from the API's JSON — nothing is hidden, and .get() is an escape hatch to the API's many other endpoints. Errors raise pyhomerun.MLBAPIError.
Conventions
- Division by zero never raises: rate stats return
0.0(ormath.inffor ERA-style stats when runs scored without an out recorded). See each module's docstring. - Innings must be true innings (6⅔, not the box-score
6.2) — convert withinnings(). - League constants:
wobaandfipship with representative modern-era defaults. For season-exact work, pass your ownWobaWeights/ FIP constant using values from the free FanGraphs Guts! page. - Typed: the package ships a
py.typedmarker; all functions are annotated.
Running the tests
No test framework needed:
python -m unittest discover tests -v
(The suite also works under pytest if you prefer it.) The MLB client tests are fully offline — they never touch the network — and every docstring example runs as a doctest.
Contributing
Contributions are welcome! See CONTRIBUTING.md. The short version: keep it dependency-free, add a docstring with formula + example to every public function, and include tests.
License and data
Code is MIT licensed. Data from the MLB Stats API is subject to the MLB copyright notice; this project is not affiliated with or endorsed by MLB.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyhomerun-0.1.0.tar.gz.
File metadata
- Download URL: pyhomerun-0.1.0.tar.gz
- Upload date:
- Size: 19.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c53781589bd7431eb030d7e45100be656092c61e076543efec642cc5a1233cf0
|
|
| MD5 |
0a69e8c460ce8614b0f7fb0c26b55717
|
|
| BLAKE2b-256 |
91374cb9c5873d01ede4b6eb21a32b47447a34a87b18b30a4ccdcca0e6b84a93
|
Provenance
The following attestation bundles were made for pyhomerun-0.1.0.tar.gz:
Publisher:
publish.yml on SRock44/pyhomerun
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyhomerun-0.1.0.tar.gz -
Subject digest:
c53781589bd7431eb030d7e45100be656092c61e076543efec642cc5a1233cf0 - Sigstore transparency entry: 2111176401
- Sigstore integration time:
-
Permalink:
SRock44/pyhomerun@a1a777dc3549c751a66284b52e8808f6b96e97f3 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/SRock44
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a1a777dc3549c751a66284b52e8808f6b96e97f3 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pyhomerun-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyhomerun-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eeb3b750b6845977608d985b1cb540e035a46c2455955e11150c3b5edb30a05b
|
|
| MD5 |
ea6f2dafc5aded54721041545ae54d25
|
|
| BLAKE2b-256 |
7895a5bc0c55ead1a050e1737299bc2dcc57e854e0bf40c11d097b716bad2f8c
|
Provenance
The following attestation bundles were made for pyhomerun-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on SRock44/pyhomerun
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyhomerun-0.1.0-py3-none-any.whl -
Subject digest:
eeb3b750b6845977608d985b1cb540e035a46c2455955e11150c3b5edb30a05b - Sigstore transparency entry: 2111176570
- Sigstore integration time:
-
Permalink:
SRock44/pyhomerun@a1a777dc3549c751a66284b52e8808f6b96e97f3 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/SRock44
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a1a777dc3549c751a66284b52e8808f6b96e97f3 -
Trigger Event:
release
-
Statement type: