MLB box scores with typed stat lines
any planFind an MLB contest and read batting/pitching lines from the current boxscore API.
The current REST surface exposes MLB game results through competition editions and contest
box scores. A boxscore returns one line per appearance, and each line has typed phase blocks
such as batting and pitching; there is no raw stats blob to unpack.
1. Find the contest
Start with the MLB edition and date you care about:
curl -H "X-API-Key: $STATSHAWK_KEY" \
"https://api.statshawk.ai/v1/competitions/mlb/editions/2026/games?date=2026-04-15"The response is a contest list; use the id from one data.items[] row as the
contest_id.
2. Read the boxscore
curl -H "X-API-Key: $STATSHAWK_KEY" \
"https://api.statshawk.ai/v1/contests/{contest_id}/boxscore"The response
The data object is a ContestBoxscore — the contest's match summary, a finalized
flag, and one line per appearance:
{
"contest": {
"id": "cst_8m2vq4x1",
"competition": "comp_2r9wq7k3",
"edition": "edt_5t1nq8y6",
"home_team": "team_4c8xk2m9",
"away_team": "team_7p3jw5v0",
"kickoff": "2026-04-15T02:10:00Z",
"status": "Final",
"score": { "home": 5, "away": 3 }
},
"finalized": true,
"lines": [
{
"appearance": "apr_...",
"person": "per_...",
"team": "team_7p3jw5v0",
"phases": [
{
"phase": "batting",
"measures": {
"ab": 4,
"h": 2,
"hr": 1,
"rbi": 3,
"ops": 1.250
}
}
]
},
{
"appearance": "apr_...",
"person": "per_...",
"team": "team_4c8xk2m9",
"phases": [
{
"phase": "pitching",
"measures": {
"ip": 6.0,
"pitching_k": 8,
"pitching_h": 4,
"er": 2,
"whip": 1.00
}
}
]
}
]
}Ids are opaque minted ids (cst_…, comp_…, edt_…, team_…, per_…, apr_…) —
resolve names via /v1/teams/{team_id} and /v1/persons/{person_id}, or hold onto the
names from the contest list you already fetched. finalized is true once the contest
status is Final; status is one of Scheduled, InProgress, Final, Postponed,
Cancelled, Suspended.
Phase blocks
| Phase | Common measures |
|---|---|
batting | ab, h, hr, rbi, bb, k, avg, obp, slg, ops |
pitching | ip, pitching_k, pitching_h, pitching_bb, er, era, whip |
Measures that collide between phases are prefixed on the pitching side: a pitcher's
strikeouts are pitching_k (batter strikeouts stay k), hits allowed are pitching_h,
walks issued are pitching_bb. A two-way player has both a batting and a pitching
block on the same line. Derived rates (avg, era, whip, …) are omitted when undefined
rather than fabricated as 0 — use .get(...) semantics, not direct key access.
A Python script — pitcher strikeouts
import os, requests
KEY = os.environ["STATSHAWK_KEY"]
BASE = "https://api.statshawk.ai/v1"
def first_game_id(date: str) -> str:
r = requests.get(
f"{BASE}/competitions/mlb/editions/2026/games",
headers={"X-API-Key": KEY},
params={"date": date},
timeout=10,
)
r.raise_for_status()
return r.json()["data"]["items"][0]["id"]
def pitching_lines(contest_id: str):
r = requests.get(
f"{BASE}/contests/{contest_id}/boxscore",
headers={"X-API-Key": KEY},
timeout=15,
)
r.raise_for_status()
for line in r.json()["data"]["lines"]:
for phase in line["phases"]:
if phase["phase"] == "pitching":
yield line["person"], phase["measures"]
contest_id = first_game_id("2026-04-15")
for person_id, measures in pitching_lines(contest_id):
print(f"{person_id}: {measures.get('pitching_k', 0)} K, {measures.get('ip', 0)} IP")Going deeper: pitch-level data
The boxscore is the game-line summary. For Statcast-grade pitch tracking — every pitch's
type, velocity, and result, grouped by plate appearance — use
/v1/contests/{contest_id}/play-by-play (weight 5×). It takes a detail parameter
(sparse, standard, or full, default full) to trade payload size for field
coverage, and pitcher_id / batter_id filters to narrow to one player or a
head-to-head intersection.
This endpoint is available on any tier and reads the same normalized contest model as the NBA, NHL, NFL, KBO, NCAA, and soccer surfaces.