Injuries
Injury availability and history, what it covers, and how to read it honestly. Absence only means healthy inside a covered window.
Statshawk serves player injury availability and, for MLB, real injured-list history. It arrives as one new endpoint plus three additive fields on responses you already use. Everything here is additive: a healthy player serializes exactly as before, so nothing breaks until you choose to read the new data.
The one rule that governs the whole feature: injury data is only as honest as its coverage window. Read Reading coverage honestly before you treat an absent field as a healthy player, because outside a covered window an absent field means we do not know, not that the player is fine.
What's covered
We track injury data for five leagues today. MLB is the only sport with real multi-season history; the rest carry current status from the ESPN injury report.
| Sport | Current status | Historical intervals |
|---|---|---|
| MLB | Yes, two sources (ESPN report + official MLB transactions), refreshed hourly | Yes, true injured-list stints back to 2024-01-01 |
| NFL, NBA, NHL, WNBA | Yes, ESPN report, refreshed hourly | Report transitions only, accruing since 2026-08-28 (no deep history) |
| Everything else | None | None |
Reading coverage honestly
Every source reports a coverage window, covered_from and covered_through. Inside that
window the source is actively tracking the player; outside it, the source was not yet
watching. This window is the difference between "healthy" and "unknown," so it comes first.
- Inside a covered window, an absent
injuryfield means the player is healthy. The field is omitted, nevernull. - Outside a covered window, absence means unknown. ESPN history begins 2026-08-28 and MLB transactions go back to 2024-01-01, so a player with no ESPN interval before that date is not "healthy then," just untracked then.
- A known-healthy person returns
200with coverage blocks and empty event and interval lists. A404means the person id is unknown, never that the player is healthy.
Where injury data shows up
Current status on person detail
GET /v1/persons/{id} gains an optional injury object. List, search, and overview
responses are unchanged.
"injury": {
"status": "60-Day-IL",
"detail": "New York Yankees placed RF Aaron Judge on the 60-day injured list. Right rib stress fracture.",
"injury_type": "Ribs",
"return_date": null,
"source": "mlb_stats",
"source_reported_at": "2026-07-18T00:00:00Z",
"source_observed_at": "2026-08-29T13:06:00Z"
}injury_type ("Ribs", "Hamstring") and return_date come from ESPN-sourced rows only, and
return_date is an estimate: treat it as soft. See the
Persons API reference for the full endpoint.
Current status on team rosters
GET /v1/teams/{id}/roster carries the same optional injury object on each entry, with the
same shape and the same mlb_stats-wins precedence. Healthy entries are byte-identical to
before.
Stale-form flags on prop analysis
This is the motivating feature. GET /v1/analysis/player-prop gains an optional injury
object and an injury_window_flags object. The flags are annotation only: averages, hit
rates, windows, and denominators are computed exactly as before. They exist so a UI can
explain a window, never to alter it.
"injury_window_flags": {
"games_before_latest_interval": 59,
"latest_interval_days": 42,
"window_spans_interval": false,
"interval": { "start": "2026-07-18", "end": null }
}| Field | Meaning |
|---|---|
games_before_latest_interval | Sampled games whose kickoff precedes the stint. The stale-form signal. |
latest_interval_days | The stint's span, counted through today while it is still open. |
window_spans_interval | true when the sampled games bracket the stint, a gap a raw game log hides. |
interval | The stint driving the flags, picked for relevance to the sampled window, with source precedence only as tie-break. |
Together they let a card say "59 of the last 59 sampled games predate a 42-day injured-list stint (out since Jul 18)" instead of letting a stale game log read as a slump.
Full history
GET /v1/persons/{id}/injuries returns the complete history, one block per source, with
derived injured-list intervals. Sources stay separate.
{
"person": "per_06fhk9szx1rz7besydrene8qj0",
"sources": [
{
"source": "mlb_stats",
"intervals": [
{ "start": "2025-07-26", "end": "2025-08-05",
"start_kind": "reported", "close_kind": "activated", "status": "10-Day-IL" },
{ "start": "2026-07-18", "end": null,
"start_kind": "reported", "close_kind": "open", "status": "60-Day-IL" }
],
"covered_from": "2024-01-01T00:00:00Z",
"covered_through": "2026-08-29T13:06:00Z"
}
]
}Each interval's start_kind and close_kind tell you how much to trust its edges:
start_kindsays how the interval opened.reportedis a dated transaction;listedis the first time the player was observed on the source report;unknownis left-censored (the player was already injured when tracking began, so the start date is a bound, not the onset); andtolerant_openmeans a change or transfer event arrived with no previously open interval, so the start boundary is inferred and weaker.close_kind: "activated"is an official MLB activation.close_kind: "unlisted"only means the player left the ESPN report, which is weaker than "recovered."close_kind: "open"(withend: null) means the stint is ongoing.
Events sit in a separate events array on each source block, as siblings of the intervals,
not nested inside them. Each carries the receipts: detail, return_date, changed_fields
(which facts moved, for example an estimated-return slip), source_event_id (the MLB
transaction id), and both observed_at (when we ingested the event) and effective_date (when
an MLB transaction actually took effect, which differs from observed_at). unattached_events
is a source-wide count of events not attached to any person; a nonzero value qualifies how
confidently you can read an empty history for a single player.
Vocabulary
statusis the source's own wording, not an enum we invented, so render it verbatim. ESPN ships values likeOut,Questionable,Day-To-Day,Injured Reserve,60-Day-IL, andActive; MLB transactions use7-Day-IL,10-Day-IL,15-Day-IL, and60-Day-IL. Do not hardcode an exhaustive list.Activeis meaningful, not an error. It means the player is on the injury report but playing (probable, or just returned). Keep those rows; do not filter them out.sourceisespnormlb_stats.- Event
kindvalues aresnapshot_baseline,snapshot_listed,snapshot_changed, andsnapshot_unlistedfor ESPN report transitions, andil_placed,il_transferred, andil_activatedfor MLB transactions.
Freshness
Both lanes refresh hourly, but read the injury-specific timestamps, not meta.fetched_at,
which records when the response was assembled, not when the injury data changed. For current
status, source_observed_at is when the source last observed the player. For history, each
source's covered_through is how current its coverage runs, and an absent covered_from means
the source has never synced, so everything is unknown for it. See
Data freshness for the full
freshness contract, and the REST API reference or the
data model for the generated schemas
(PersonDetailView, PersonInjuryHistory, SourceInjuryHistory, InjuryStatusView,
InjuryEventView, DerivedInterval, and InjuryWindowFlags).