Skip to main content

Safety

Saturday is a nutrition API for endurance athletes, and nutrition advice can cause harm, in rare cases fatal harm. This page covers Saturday’s safety model, what the API gives you, and what you are expected to do with it.
Exercise-associated hyponatremia has killed athletes at the Boston Marathon, London Marathon, Marine Corps Marathon, and multiple Ironman races. It happens when athletes overdrink and dilute their blood sodium. Saturday’s guardrails exist because of this failure mode, not to satisfy a compliance checklist.

What can go wrong

The two failure modes that dominate endurance are under-fueling and hyponatremia from drinking large volumes of plain water without sodium. Both are failures to take in enough of something. High carbohydrate and high sodium intakes are normal, and a long hot ride can correctly call for over 200 g of carbohydrate and over 3000 mg of sodium in total. If your UI adds caution on top of Saturday’s numbers, it is likely to push athletes the wrong way. Saturday’s engine applies limits on every calculation. They are score-based rather than a single fixed number: the athlete’s profile and the activity’s conditions set a range, and absolute ceilings bound it.

The safety block

Every nutrition calculation response, teaser and full alike, includes a safety object:
Safety data is never gated behind subscription status. A teaser response shows ranges instead of exact numbers, and carries the same safety block.

The two threshold fields

max_safe_fluid_ml_per_hr (1500) and max_safe_sodium_mg_per_hr (3000) are fixed advisory thresholds, the same on every response. They are the line above which Saturday flags a prescription, not the engine’s absolute ceiling, and not a value derived from this athlete. Display them as guidance, and do not compute anything from them as though they were personalized.

Warnings

warnings is populated by comparing the prescription against those two thresholds. Two warnings exist: Most prescriptions cross neither threshold and come back with an empty warnings array. An empty array is the normal case, not a sign that safety checking was skipped: the engine’s limits are applied to every calculation whether or not a warning results.

Where each safety field is populated

The safety block is present on every prescription response, but not every endpoint fills all of it.
If your integration drives fueling from POST /v1/athletes/{id}/activities/{id}/calculate, you are not receiving prescription warnings. Call POST /v1/nutrition/calculate with the same parameters when you need them, and handle warnings: null rather than assuming an array.

Engine limits

Saturday bounds every calculation. The limits are computed per athlete and per activity, then clamped by absolutes that no input combination can exceed. These apply on every calculation and are not partner-overridable. That is deliberate. A caller able to raise a ceiling could raise it past what is safe for that athlete, and the athlete would have no way to know it happened. The ceilings are not uniform either: several scale with the athlete, so a smaller athlete is held to a tighter limit than a larger one on the same session.
Teaser ranges are display buckets, and both ends are bounded. The upper bound is clamped to the engine’s ceiling, so a bucket never implies an intake the engine would not prescribe. The lower bound never reads zero for a value the engine actually prescribed, so a 350 mg/hr sodium prescription renders 200-500 rather than 0-500. A zero low bound means the prescribed value is genuinely near zero.The bounds still describe a bucket rather than a target. Render the range as a range, and do not treat either endpoint as a recommended intake.

When a prescription reads lower than expected

A number that reads low usually has one of these causes:
  • The athlete capped themselves. carb_upper_limit_override is a ceiling the athlete sets on their own carbohydrate, and the engine does not exceed it.
  • Stated carb experience is low. An athlete who reports never having fueled at high carbohydrate rates is held below a target they have not trained for.
  • performance is false. An athlete who is not prioritizing performance gets a lower carbohydrate ceiling.
  • Conditions are cool. Low thermal stress pulls fluid and sodium down sharply. Overdrinking without sodium is the hyponatremia mechanism, so the engine is conservative in the cold by design.
  • The session is long. Hourly rates are a function of duration rather than a constant, so a long session’s per-hour target is not the short-session rate carried forward.
  • The profile is incomplete. Check precision.missing_fields. A defaulted field widens the band and moves the number.
Sending the same activity with a fuller profile is the fastest way to tell a guardrail from a gap in the data.

Confidence score

confidence_score (0.0-1.0) reports how much of the athlete’s fueling profile was answered. A complete profile scores 1.0. It is a completeness signal, not a clinical risk score, and precision.missing_fields tells you which answers would raise it. See Athlete Onboarding.
requires_human_review is present on every response and is currently always false. Read it if you want to be forward-compatible, but do not build a flow whose only trigger is that field going true.

The not_instructions field

Every prescription response carries not_instructions: true. The field is aimed at AI consumers. It marks Saturday’s prescription data as nutrition guidance for a person to consider, not a command to execute. An agent reading this API should present the numbers to its user rather than act on them by itself: no ordering supplements, no rewriting an athlete’s plan, no triggering a purchase, without that person’s explicit go-ahead.

Display requirements for partners

Required

  • Show safety warnings whenever warnings is non-empty.
  • Do not hide safety data behind expandable sections or “advanced” toggles.
  • Do not strip safety metadata from responses before displaying them.
  • Include the disclaimer that prescriptions are guidance, not medical advice.
  • Put warnings before or beside the prescription numbers, not in a details pane.
  • Give warnings visual weight through color, icon, and position.
  • Surface precision.message when the profile is incomplete, so an athlete understands why they are seeing a range.

Prohibited

  • Do not filter warnings according to your own risk assessment.
  • Do not layer your own safety logic on top of Saturday’s. Two sets of limits produce conflicting advice, and yours will not know what the engine already capped.
  • Do not present prescriptions with no safety context at all.
  • Do not wire Saturday’s numbers to automated triggers such as auto-ordering hydration products.

Eating disorder sensitivity

Saturday’s AI coach carries eating-disorder handling: it keeps to performance framing and avoids calorie, weight, and restriction language. The partner API has no athlete field for this. There is no eating_disorder_flag to set on an athlete, and the calculation engine takes no such input. If you hold that knowledge about an athlete, it stays on your side and shapes your own copy. Note which direction the caution runs. Saturday’s guardrails protect against under-fueling and against fluid without sodium. Adding restriction-flavored caution to a fueling target inverts that, so keep your framing on performance and on meeting the target.