statshawk

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.

SportCurrent statusHistorical intervals
MLBYes, two sources (ESPN report + official MLB transactions), refreshed hourlyYes, true injured-list stints back to 2024-01-01
NFL, NBA, NHL, WNBAYes, ESPN report, refreshed hourlyReport transitions only, accruing since 2026-08-28 (no deep history)
Everything elseNoneNone

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 injury field means the player is healthy. The field is omitted, never null.
  • 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 200 with coverage blocks and empty event and interval lists. A 404 means 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 }
}
FieldMeaning
games_before_latest_intervalSampled games whose kickoff precedes the stint. The stale-form signal.
latest_interval_daysThe stint's span, counted through today while it is still open.
window_spans_intervaltrue when the sampled games bracket the stint, a gap a raw game log hides.
intervalThe 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_kind says how the interval opened. reported is a dated transaction; listed is the first time the player was observed on the source report; unknown is left-censored (the player was already injured when tracking began, so the start date is a bound, not the onset); and tolerant_open means 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" (with end: 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

  • status is the source's own wording, not an enum we invented, so render it verbatim. ESPN ships values like Out, Questionable, Day-To-Day, Injured Reserve, 60-Day-IL, and Active; MLB transactions use 7-Day-IL, 10-Day-IL, 15-Day-IL, and 60-Day-IL. Do not hardcode an exhaustive list.
  • Active is 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.
  • source is espn or mlb_stats.
  • Event kind values are snapshot_baseline, snapshot_listed, snapshot_changed, and snapshot_unlisted for ESPN report transitions, and il_placed, il_transferred, and il_activated for 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).

On this page