| title | Quickstart |
|---|---|
| description | Your first prescription, start to finish |
| sidebarTitle | Quickstart |
Self-serve: sign up at saturday.fit/api for $5.39/month, no contract. Your key is revealed once after checkout and emailed to you. An AI agent can do this for you: POST /v1/signup (no auth) returns a hosted checkout link. Not sure self-serve is your lane? See Getting Access.
Platform partners: keys come with your agreement. Contact api@saturday.fit.
Keys carry an environment prefix. Production keys start with sk_live_, sandbox keys with sk_test_. Each environment keeps separate key records, so a key issued by the sandbox does not authenticate against production. See Authentication for what each environment does and which base URL it uses.
The examples below use the production base URL and read a production key from SATURDAY_API_KEY. For sandbox work, replace the base URL with the one issued with your sk_test_ key:
export SATURDAY_API_KEY="sk_live_..."The prescription endpoint takes activity parameters and returns fuel, hydration, and sodium targets. Only activity_type and duration_min are required; everything else sharpens the result.
curl -X POST https://api.saturday.fit/v1/nutrition/calculate \
-H "Authorization: Bearer $SATURDAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"activity_type": "run",
"duration_min": 90,
"intensity_level": 5,
"athlete_weight_kg": 70,
"thermal_stress_level": 6
}'import os
import requests
response = requests.post(
"https://api.saturday.fit/v1/nutrition/calculate",
headers={
"Authorization": f"Bearer {os.environ['SATURDAY_API_KEY']}",
"Content-Type": "application/json",
},
json={
"activity_type": "run",
"duration_min": 90,
"intensity_level": 5,
"athlete_weight_kg": 70,
"thermal_stress_level": 6,
},
)
data = response.json()
print(data)const response = await fetch(
"https://api.saturday.fit/v1/nutrition/calculate",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SATURDAY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
activity_type: "run",
duration_min: 90,
intensity_level: 5,
athlete_weight_kg: 70,
thermal_stress_level: 6,
}),
}
);
const data = await response.json();
console.log(data);Saturday publishes SDKs for Python and TypeScript:
pip install 'saturday>=0.6.0'npm install '@saturdayinc/sdk@^0.6.0'import os
from saturday import Saturday
client = Saturday(api_key=os.environ["SATURDAY_API_KEY"])
result = client.nutrition.calculate(
activity_type="run",
duration_min=90,
intensity_level=5,
athlete_weight_kg=70,
thermal_stress_level=6,
)
print(result)import Saturday from "@saturdayinc/sdk";
const apiKey = process.env.SATURDAY_API_KEY;
if (!apiKey) throw new Error("Set SATURDAY_API_KEY before running this example");
const client = new Saturday({ apiKey });
const result = await client.nutrition.calculate({
activity_type: "run",
duration_min: 90,
intensity_level: 5,
athlete_weight_kg: 70,
thermal_stress_level: 6,
});
console.log(result);Both SDKs default to https://api.saturday.fit and accept a base URL override (base_url in Python, baseUrl in TypeScript) for sandbox work.
SDK 0.6.0 aligns prescription, batch, import, pagination, and athlete-settings types with the API while preserving raw response objects. Activity calculations return a tiered envelope; stored-prescription reads return {prescription, safety}, not the flat nutrition response above. Safety warnings may be null. Existing typed callers should read the migration notes in the TypeScript SDK or Python SDK.
AI-to-AI conversations, where your AI talks with Saturday's assistant, remain feature-gated. With ai_to_ai_conversations access, use ai.createConversationStream / ai.sendMessageStream in TypeScript, or the async context-managed ai.create_conversation_stream / ai.send_message_stream methods in Python. Consume all events, including safety warnings and errors. These writes are never automatically replayed, and a partial stream is not a complete answer. Legacy AI JSON-write methods fail locally with migration guidance; conversation metadata/history reads remain JSON. The SDK READMEs include executable stream examples and cancellation guidance.
A successful response returns a prescription with safety metadata. The request above has no athlete_id, so a partner-key request returns teaser ranges. The following illustrates an exact response for an entitled athlete with complete profile and activity inputs:
{
"tier": "full",
"carb_g_per_hr": 60.0,
"sodium_mg_per_hr": 600.0,
"fluid_ml_per_hr": 620.0,
"total_carb_g": 90,
"total_sodium_mg": 900,
"total_fluid_ml": 930,
"safety": {
"max_safe_fluid_ml_per_hr": 1500,
"max_safe_sodium_mg_per_hr": 3000,
"confidence_score": 0.72,
"requires_human_review": false,
"warnings": [],
"not_instructions": true
},
"attribution": {
"text": "Powered by Saturday",
"logo_url": "https://saturday.fit/logo.png",
"link": "https://saturday.fit",
"required": true
}
}| Field | What it means |
|---|---|
tier |
"full" for an entitled athlete, "teaser" for free ranges; incomplete full-tier inputs also return ranges |
carb_g_per_hr |
Carbohydrate target in grams per hour |
sodium_mg_per_hr |
Sodium target in milligrams per hour |
fluid_ml_per_hr |
Fluid target in milliliters per hour |
total_* |
Totals across the whole activity duration |
safety.max_safe_*_per_hr |
Advisory thresholds; a prescription above one carries a warning |
safety.confidence_score |
Confidence in this prescription, 0.0 to 1.0 |
safety.requires_human_review |
Always false |
safety.warnings |
Safety warnings for this prescription |
safety.not_instructions |
Marks the prescription as guidance, not commands |
A response may also carry a precision object describing which profile fields are still missing and how much they widen the answer. See Onboarding for how to collect them.
tier reflects the athlete's subscription and trial status, not your key's environment. A partner-key request with no athlete attached returns teaser ranges. A full-tier response can also contain ranges when profile or activity inputs are incomplete; check the returned fields and precision, not tier alone.
| Athlete status | What they get | Example |
|---|---|---|
| Subscribed or in trial, complete inputs | Exact numbers | "carb_g_per_hr": 60.0 |
| Subscribed or in trial, incomplete inputs | Ranges until the missing fields are answered | "carb_range_g_per_hr": "60-80" |
| Free | Ranges | "carb_range_g_per_hr": "60-90" |
Teaser responses carry a subscription_cta field, and their attribution.required is true:
{
"tier": "teaser",
"carb_range_g_per_hr": "60-90",
"sodium_range_mg_per_hr": "500-1000",
"fluid_range_ml_per_hr": "500-1000",
"attribution": {
"text": "Powered by Saturday",
"logo_url": "https://saturday.fit/logo.png",
"link": "https://saturday.fit",
"required": true
},
"subscription_cta": {
"message": "Get your exact carb, sodium, and fluid targets, not ranges",
"subscribe_url": "https://saturday.fit/subscribe?ref=YOUR_PARTNER_ID",
"features": [
"Exact gram/mg/mL targets per hour",
"Personalized product picks inside the app",
"Personalized fueling plan",
"15+ tuning factors"
]
}
}See Freemium Model for how the tiers are decided and how the subscribe loop pays you.
[Athletes](/guides/athletes) stores athlete settings (weight, sweat level, preferences) so calculations stop relying on defaults. [Safety](/guides/safety) covers the safety model and the ceilings above. [Activities](/guides/activities) creates activities, attaches prescriptions, and collects post-session feedback. [Authentication](/authentication) covers swapping a `sk_test_` key and its sandbox base URL for a `sk_live_` key against `api.saturday.fit`.