Skip to main content

Nutrition Calculation

This endpoint takes an activity description and returns a fuel prescription: carbohydrate, sodium, and fluid targets, with safety guardrails applied.

Progressive enrichment

Saturday works with whatever data you have. More data narrows the prescription; minimal inputs still return a usable result.

Minimal inputs

The bare minimum for a calculation:
This returns a prescription with a low confidence_score. The numbers are based on population-level defaults for the given activity type and body weight.

Standard inputs

Add intensity and environmental conditions:
Thermal stress drives the fluid and sodium targets. A 3-hour ride at high thermal stress fuels differently from the same ride in cool conditions.

Comprehensive inputs

For the highest accuracy, include an athlete reference:
When you include an athlete_id, Saturday reads that athlete’s stored profile: sex, year of birth, body weight, sweat level, saltiness, carb experience, usual carb consumption, satiety level, fitness level, and fueling concerns. Inline fields in the request body override the stored values for that one call.

Understanding the response

Confidence score

The safety.confidence_score (0.0-1.0) reports how much of the athlete’s fueling profile was answered: a complete fueling profile scores 1.0, and every unanswered field lowers the score. precision.missing_fields tells you which answers would raise it.

Comparing across conditions

To compare fueling across different conditions (a cool morning against a hot afternoon, say), call POST /v1/nutrition/calculate once per scenario and diff the results yourself, or use POST /v1/nutrition/calculate/batch to submit up to 50 scenarios in a single request. Each scenario in a batch debits your quota individually.
Two prescription shapes. The calculate endpoints return a flat response (carb_g_per_hr, sodium_mg_per_hr, … with a nested safety block). The stored-prescription read (GET /v1/athletes/{id}/activities/{id}/prescription) returns a wrapped response: { "prescription": { … }, "safety": { … } }. Read the fields accordingly depending on which endpoint you called.

Calculation timing

Every engine run carries a deliberate computation window, the same one athletes see in the Saturday app. Size your loading state around it:
  • Single calculation (POST /v1/nutrition/calculate, POST .../activities/{id}/calculate): roughly 1 to 3 seconds, scaling with activity duration and rising with intensity. Recalculating an activity that already has a prescription takes about half as long.
  • Batch (POST /v1/nutrition/calculate/batch): each scenario adds about half a single calculation’s time. The response headers arrive immediately with X-Batch-Estimated-Ms, the expected total processing time, so you can size a progress indicator before the body lands. The completed body includes estimated_ms and elapsed_ms.
  • Estimate without calculating: send the same batch payload with "estimate_only": true to get estimated_ms back immediately, with no calculations run and no quota debited. Useful when your HTTP client can’t read streamed headers early.
  • Reads are instant. Fetching a stored prescription (GET .../prescription) carries no computation window.
Fill the window with a specific state, such as “calculating your fueling plan”, rather than a bare spinner or a frozen screen.

Teaser vs. full responses

Responses depend on the athlete’s Saturday subscription status:
  • Subscribed athletes get exact numbers: "carb_g_per_hr": 72
  • Free/teaser athletes get ranges: "carb_range_g_per_hr": "60-90"
Teaser ranges are bucketed to a fixed grid, 30 g/hr for carbohydrate and 500 units/hr for sodium and fluid, so the same underlying number always lands in the same bucket. See Freemium Model for implementation details.

Safety

Every calculation response includes a safety block, on every tier. See Safety for the fields, the guardrails behind them, and what you are required to display.
Safety data is never gated behind subscription status. Teaser responses carry the same safety block as full ones.

Supported activity types

These seven keys are the complete set. GET /v1/activity-types returns the same list with descriptions, and accepts an optional ?activity_type= filter to validate a single key.
Every calculation response carries a precision object: profile_complete, impact-sorted missing_fields, and an onboarding invite. Exact numbers require a complete fueling profile; an incomplete one gets a band instead. See Athlete Onboarding.