---
title: "Fitness, fatigue, strain, polarization, readiness"
method: GET
path: "/training/metrics"
tags: ["Training"]
---

# Fitness, fatigue, strain, polarization, readiness

`GET /training/metrics`

## Request

- Base URL: `https://bodylab.lol/api/v1`
- URL: `https://bodylab.lol/api/v1/training/metrics`
- Auth: HTTP bearer

## Query parameters

- `days` integer: How many days back from today.

## Headers

- `X-Client-Platform` string
- `X-Client-Version` string

## Response `200`

OK

- object
  - `fitness` object, required
    - `ctl` number, required: Chronic training load (fitness): 42-day exponentially weighted load.
    - `atl` number, required: Acute training load (fatigue): 7-day exponentially weighted load.
    - `form` number, required: ctl − atl.
    - `acwr` number, nullable, required: Acute:chronic ratio; null until there's enough chronic load to divide by.
    - `acwrChronic` number, required: The chronic load acwr divides by — a 28-day EWMA, not ctl's 42-day one.
    - `rampRate` number, nullable, required: Weekly change in ctl.
  - `strain` object, required
    - `monotony` number, nullable, required
    - `strain` number, nullable, required
    - `weeklyLoad` number, required
    - `monotonyHigh` boolean, required
    - `strainHigh` boolean, required: The fixed rule: monotony at or above 2.0 on a week at or above the load the athlete is conditioned for.
    - `personalHigh` number, nullable, required: The athlete's own threshold: their highest 7-day strain in the 12 weeks before this week. Null until 8 weeks of that history have a strain.
    - `aboveOwnHigh` boolean, required: strainHigh, and this week's strain above personalHigh too: what holds today's session easy. False while personalHigh is null.
  - `polarization` object, required
    - `easyFraction` number, required
    - `moderateFraction` number, required
    - `hardFraction` number, required
    - `totalSeconds` number, required
    - `sufficient` boolean, required: Enough heart-rate data in the window to judge.
  - `debt` object, required
    - `daysSinceStrength` number, nullable, required
    - `strengthSessionsLast7` integer, required
    - `strengthSessionsLast14` integer, required
    - `meetsStrengthFloor` boolean, required
    - `daysSinceImpact` number, nullable, required
    - `daysSinceHardSession` number, nullable, required
    - `supportedLoadFraction` number, required
    - `windowDays` integer, required
  - `readiness` object, required
    - `band` 'green' | 'amber' | 'red' | 'unknown', required
    - `score` number, nullable, required
    - `contributors` object[], required: Every signal readiness knows about, heaviest first, whether or not it counted today — see ReadinessContributor.
      - `key` string, required: Which wellness signal this is. Known values: hrv, restingHr, sleep, energy. Treat unknown values as "other".
      - `label` string, required: What to call the signal to an athlete, lower case for mid-sentence use: "resting heart rate".
      - `state` string, required: Whether this signal counted towards today's score. `baselining` means readings are arriving but there isn't enough history to judge them against yet — it must never be shown as a zero, which on this scale means "exactly normal for you". `missing` means nothing recent enough was recorded. Known values: scored, baselining, missing. Treat unknown values as "other".
      - `score` number, nullable, required: This signal's own score, −1 to +1, positive meaning recovered. Null unless state is `scored`.
      - `weight` number, required: Share of today's composite this signal carries, 0 to 1 — not a fixed weight. Readiness reweights to the signals it has, so one signal alone carries all of it. 0 unless state is `scored`.
      - `z` number, nullable, required: Signed deviation from this signal's own rolling baseline in standard deviations, in the direction of the reading: resting heart rate above baseline is positive, though that's the unfavourable direction — `score` is the field that always points the same way. Null when the signal isn't judged against a baseline at all, as a device's already-calibrated sleep or energy score isn't.
    - `hrvZ` number, nullable, required
    - `restingHrDelta` number, nullable, required
  - `block` object, required
    - `consecutiveTrainingDays` integer, required
    - `consecutiveLoadingWeeks` integer, required
    - `week` object, required
      - `startDate` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
      - `loadToDate` number, required
      - `chronicAtStart` number, nullable, required
      - `daysTrained` integer, required
      - `daysRemaining` integer, required
      - `typicalSessionsPerWeek` number, required
  - `seriesDays` integer, required
  - `excludedActivities` integer, required: Activities in the window with no evidence to score them; non-zero means the metrics understate training.
  - `nearestEvent` object, nullable, required: The nearest A event driving the engine's taper/recovery rules, if any — or, on the day of an event of any priority, that event.
    - `name` string, required
    - `daysUntil` integer, required: Negative once the event has passed — only true while still inside its recovery window.
    - `phase` 'build' | 'taper' | 'event_day' | 'recovery', required
    - `totalDays` integer, nullable, required: Length of the taper or recovery window in days; null in the build and event_day phases.
  - `preparation` object, nullable, required: The emphasis of the current block of training before the athlete's nearest A event. Recomputed every morning from the event's date and expected duration; readiness still decides each day, and a phase never overrides a rest day, a taper or a recovery window.
    - `eventId` string, required: Opaque identifier.
    - `eventName` string, required
    - `phase` string, required: Which part of the run-up to an A event a day sits in. An emphasis worked out each morning from the event's date, not a stored plan. Known values: base, build, specific, taper. Treat unknown values as "other".
    - `week` integer, required: Which week of this phase today is in, from 1.
    - `weeks` integer, required: How many weeks the phase runs for.
    - `startsOn` string, date, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.
    - `endsOn` string, date, required: Inclusive: the last day of this phase.
    - `nextPhase` string, nullable, required: Which part of the run-up to an A event a day sits in. An emphasis worked out each morning from the event's date, not a stored plan. Known values: base, build, specific, taper. Treat unknown values as "other".
    - `nextStartsOn` string, date, nullable, required: A day on the athlete's own calendar: YYYY-MM-DD, no time zone.

## Other responses

- `401`: Error: UNAUTHORIZED.

---

[API](https://skmtc.dev/bodylab/apis/body-lab-api.md) · [All operations](https://skmtc.dev/bodylab/apis/body-lab-api/llms.txt) · [OpenAPI document](https://skmtc.dev/bodylab/apis/body-lab-api/revisions/544c201eaf88?raw)
