Overview
The MyToFit Health Score API calculates comprehensive health risk scores based on biometric and clinical data.
It provides three scores — Total, Wearable, and Clinical —
each mapped to a color-coded risk zone.
Score Zones
| Score Range | Color | Label | Meaning |
| 85–100 |
Blue |
Superior |
Excellent health status |
| 70–84 |
Green |
Good |
Good health status |
| 55–69 |
Yellow |
Moderate |
Moderate health risks present |
| 40–54 |
Orange |
Risk |
Elevated health risks |
| 0–39 |
Red |
High Risk |
Significant health risks detected |
Endpoints Summary
| Endpoint | Purpose | Call Frequency | Latency |
POST /calculate |
Score calculation |
Multiple times/day |
~100ms |
POST /partner/daily-refresh |
Score + AI summaries + impact drivers |
Once/day |
~3–5s |
Authentication
All API access is via the GCP API Gateway with per-partner API keys issued by MyToFit.
Required Headers
| Header | Value |
X-API-Key |
Your GCP API key (provided by MyToFit) |
Content-Type |
application/json |
https://mytofit-partner-gw-202hslfc.an.gateway.dev
Rate Limits
Per-API-key rate limits are enforced to ensure fair usage:
| Endpoint | Limit | Rationale |
/calculate |
30 requests/minute |
Lightweight (~100ms), high-frequency |
/partner/daily-refresh |
15 requests/minute |
AI-powered (~3-5s), once-per-day design |
429 Too Many Requests: If you exceed the rate limit, the API returns
{"error": "Rate limit exceeded: 30 per 1 minute"}
Wait and retry after the limit window resets (1 minute).
Endpoint 1: Calculate Score
Calculate MyToFit health scores. Returns all 3 scores with color-coded risk zones.
All input fields are optional — partial data is accepted and missing fields use neutral defaults.
Request Body
| Field | Type | Required | Description | Example |
age | number | No | Age in years | 35 |
sex | string | No | Biological sex (M, F, Male, Female) | "M" |
weight_kg | number | No | Weight in kilograms (20–500) | 70 |
height_cm | number | No | Height in centimeters (50–300) | 170 |
rhr | number | No | Resting Heart Rate (bpm) | 60 |
hrv_rmssd | number | No | HRV RMSSD (ms) | 50 |
vo2max | number | No | VO2 Max (ml/kg/min). Also accepts legacy vo2_max | 45 |
spo2_min | number | No | Minimum nocturnal SpO2 (%) | 96 |
steps | number | No | Number of steps (daily) | 8000 |
steps_7d_avg | number | No | Average daily steps (7-day) | 8000 |
sleep_hours | number | No | Sleep hours (daily) | 7.5 |
sleep_hours_7d_avg | number | No | Average sleep hours (7-day) | 7.5 |
sbp | number | No | Systolic Blood Pressure (mmHg) | 120 |
triglycerides | number | No | Triglycerides (mg/dL) | 150 |
ldl | number | No | LDL Cholesterol (mg/dL) | 120 |
hba1c | number | No | HbA1c (%) | 5.5 |
smoking | string | No | Smoking status code (see below) | "never" |
alcohol | string | No | Alcohol consumption code (see below) | "none" |
Smoking Codes
| Code | Description |
never | Never smoked |
former_gt20 | Quit > 20 years ago |
former_5to_lt20 | Quit 5–20 years ago |
former_lt5 | Quit < 5 years ago |
current_lt20 | Currently smoking < 20 cigs/day |
current_20to40 | Currently smoking 20–40 cigs/day |
current_ge40 | Currently smoking > 40 cigs/day |
Alcohol Codes
| Code | Description |
none | Don't drink |
0_1_to_1_8 | 0.1 to 1.8 drinks/day |
1_8_to_3_1 | 1.8 to 3.1 drinks/day |
3_2_to_4_6 | 3.2 to 4.6 drinks/day |
gt_4_6 | > 4.6 drinks/day |
Response (200 OK)
{
"total_score": {
"score": 88.5,
"color": "Blue",
"label": "Superior",
"explanation": "Excellent health status.",
"range": "85-100"
},
"wearable_score": {
"score": 92.0,
"color": "Blue",
"label": "Superior",
"explanation": "Excellent health status.",
"range": "85-100"
},
"clinical_score": {
"score": 85.0,
"color": "Blue",
"label": "Superior",
"explanation": "Excellent health status.",
"range": "85-100"
}
}
Wearable History: This endpoint also accepts the optional
*_history arrays
(e.g.
steps_history,
rhr_history, etc.) documented in the
Wearable History section below.
When provided, history values are averaged and used for scoring instead of scalar values.
The same averaging rules apply (7-day avg for steps/RHR/HRV, yesterday's value for sleep/SpO2, latest for VO2Max).
Sample Request with Wearable History
{
"age": 35, "sex": "M",
"weight_kg": 75, "height_cm": 175,
"steps": 3000,
"steps_history": [10000, 8000, 9000, 4000, 8000, 6000, 5000],
"rhr": 73,
"rhr_history": [72, 70, 71, 74, 75, 77, 80],
"hrv_rmssd": 65,
"hrv_history": [67, 69, 71, 68, 72, 70, 69],
"sleep_history": [5, 4.2, 4, 6, 6.3, 6.45, 5],
"spo2_history": [98, 98, 98, 98, 99, 98, 98],
"vo2max_history": [32, 32, 32, 32, 32, 33, 33],
"sbp": 128, "hdl": 52, "ldl": 130,
"triglycerides": 160, "hba1c": 5.6
}
How history is used for scoring:
• steps (3000, today’s partial) is ignored — scoring uses steps_history avg = 7,143
• rhr (73, today) is ignored — scoring uses rhr_history avg = 74
• hrv_rmssd (65, today) is ignored — scoring uses hrv_history avg = 69
• Sleep = yesterday’s value from sleep_history[0] = 5
• SpO2 = yesterday’s value from spo2_history[0] = 98
• VO2Max = latest from vo2max_history[0] = 32
Sample Response with Wearable History
{
"total_score": {
"score": 58.0,
"color": "Yellow",
"label": "Moderate",
"explanation": "Moderate health risks present.",
"range": "55-69"
},
"wearable_score": {
"score": 52.1,
"color": "Orange",
"label": "Risk",
"explanation": "Elevated health risks.",
"range": "40-54"
},
"clinical_score": {
"score": 67.0,
"color": "Yellow",
"label": "Moderate",
"explanation": "Moderate health risks present.",
"range": "55-69"
}
}
Endpoint 2: Daily Health Refresh
Batched endpoint returning score, AI-generated wellness summaries (short + long),
top 6 score impact attributes, and echoed input values. Call once per day.
Request Body
Same input fields as /calculate, plus:
| Field | Type | Default | Description |
language |
string |
"en" |
Output language for summaries: "en" or "th" |
Wearable History (optional)
Send 7 days of wearable history to enable averaged scoring and receive formatted captions for display.
All history fields are optional — if omitted, the API falls back to scalar values (backward compatible).
| Field | Type | Description | Example |
steps_history |
number[] |
Daily step counts for past 7 days [yesterday..7d ago]. null = no data. |
[10000, 8000, 9000, 4000, 8000, 6000, 5000] |
sleep_history |
number[] |
Sleep hours for past 7 days |
[5, 4.2, 4, 6, 6.3, 6.45, 5] |
rhr_history |
number[] |
Resting heart rate (bpm) for past 7 days |
[72, 70, 71, 74, 75, 77, 80] |
spo2_history |
number[] |
Min nocturnal SpO2 (%) for past 7 days |
[98, 98, 98, 98, 99, 98, 98] |
vo2max_history |
number[] |
VO2Max (ml/kg/min) for past 7 days. Also accepts legacy vo2_max_history |
[32, 32, 32, 32, 32, 33, 33] |
hrv_history |
number[] |
HRV RMSSD (ms) for past 7 days |
[67, 69, 71, 68, 72, 70, 69] |
Array ordering: Index 0 = yesterday, index 6 = 7 days ago.
null entries mean "no data that day" — they are skipped when computing averages.
Handling Missing Days (null entries)
If the wearable had no data for a specific day, use null at that position. The API skips nulls when computing averages.
{
"steps_history": [10000, null, 9000, null, null, 6000, 5000],
"sleep_history": [null, 4.2, 4, 6, 6.3, 6.45, 5],
"rhr_history": [72, 70, 71, null, 75, 77, 80]
}
In this example:
| Field | Non-null values | Result |
steps_history | 10000, 9000, 6000, 5000 (4 values) | avg = 7,500 step/day |
sleep_history | index 0 is null → falls back to index 1 = 4.2 | YTD Night 4.2 Hrs |
rhr_history | 72, 70, 71, 75, 77, 80 (6 values) | avg = 74 bpm/day |
⚠️ Important: null must be a bare JSON literal — not a quoted string.
"steps_history": [10000, null, 9000]
"steps_history": [10000, "null", 9000]
"steps_history": [10000, , 9000]
Averaging Rules
| Metric | Rule | Scoring Input | Caption Format |
| Steps | 7-day average | Average of all non-null values | avg 7,143 step/day |
| RHR | 7-day average | Average of all non-null values | avg 74 bpm/day |
| HRV | 7-day average | Average of all non-null values | avg 69 ms/day |
| Sleep | Yesterday's value | First non-null (index 0) | YTD Night 5 Hrs |
| SpO2 | Yesterday's value | First non-null (index 0) | YTD Night 98 % |
| VO2Max | Latest available | First non-null from history | Last. 32 ml/kg/min |
Response (200 OK)
{
"score": {
"total_score": { "score": 44.8, "color": "Orange", "label": "Risk", ... },
"wearable_score": { "score": 59.4, "color": "Yellow", "label": "Moderate", ... },
"clinical_score": { "score": 22.8, "color": "Red", "label": "High Risk", ... }
},
"summary_short": "Your top priority is smoking cessation.",
"summary_long": "Your health profile shows significant cardiovascular risk factors...",
"score_impact_attributes": [
{ "rank": 1, "metric": "Smoking", "current_value": "current_ge40", "score_penalty": 12.4, "domain": "Clinical" },
{ "rank": 2, "metric": "HbA1c (%)", "current_value": 5.9, "score_penalty": 8.1, "domain": "Clinical" },
...
],
"wearable_captions": {
"steps": "avg 7,143 step/day",
"sleep_duration": "YTD Night 5 Hrs",
"rhr": "avg 74 bpm/day",
"spo2": "YTD Night 98 %",
"vo2max": "Last. 32 ml/kg/min",
"hrv": "avg 69 ms/day"
},
"input_values": { "age": 50, "sex": "M", "steps": 7142.86, ... },
"generated_at": "2026-04-14T10:00:00+00:00"
}
wearable_captions: Only present when at least one *_history array is provided.
When using legacy scalar inputs (no history), this field is null.
Score Impact Attributes
| Field | Type | Description |
rank | integer | Rank by impact (1 = highest penalty) |
metric | string | Human-readable metric name |
current_value | any | Current value of the metric |
score_penalty | number | Estimated score points lost |
domain | string | "Wearable" or "Clinical" |
Note: A maximum of 6 impact attributes are returned, sorted by score_penalty descending.
Only metrics that are actively penalizing the score appear in this list.
Sample Request with Wearable History
{
"age": 35, "sex": "M",
"weight_kg": 75, "height_cm": 175,
"steps": 3000,
"steps_history": [10000, 8000, 9000, 4000, 8000, 6000, 5000],
"rhr": 73,
"rhr_history": [72, 70, 71, 74, 75, 77, 80],
"hrv_rmssd": 65,
"hrv_history": [67, 69, 71, 68, 72, 70, 69],
"sleep_history": [5, 4.2, 4, 6, 6.3, 6.45, 5],
"spo2_history": [98, 98, 98, 98, 99, 98, 98],
"vo2max_history": [32, 32, 32, 32, 32, 33, 33],
"sbp": 128, "hdl": 52, "ldl": 130,
"triglycerides": 160, "hba1c": 5.6,
"language": "en"
}
Response includes wearable_captions: When history arrays are provided, the response adds
formatted display captions (e.g. "avg 7,143 step/day") alongside scores, summaries, and impact attributes.
Sample Response with Wearable History
{
"score": {
"total_score": {
"score": 58.0,
"color": "Yellow",
"label": "Moderate",
"explanation": "Moderate health risks present.",
"range": "55-69"
},
"wearable_score": {
"score": 52.1,
"color": "Orange",
"label": "Risk",
"explanation": "Elevated health risks.",
"range": "40-54"
},
"clinical_score": {
"score": 67.0,
"color": "Yellow",
"label": "Moderate",
"explanation": "Moderate health risks present.",
"range": "55-69"
}
},
"summary_short": "Prioritize a heart-healthy lifestyle by embracing the DASH diet, increasing omega-3 rich foods, and engaging in regular physical activity to support your overall well-being.",
"summary_long": "It's wonderful that you're focusing on your wellness journey! Your current focus areas include nourishing your body with the DASH diet, rich in fruits, vegetables, and lean proteins, while limiting less beneficial choices. Boosting your intake of omega-3 fatty acids through foods like fatty fish and incorporating at least 150 minutes of moderate exercise weekly are key steps to enhance your vitality and support your body's natural balance. Embrace these positive changes, and you'll be well on your way to feeling your best!",
"score_impact_attributes": [
{
"rank": 1,
"metric": "VO2max (mL/kg/min)",
"current_value": 32.0,
"score_penalty": 13.7,
"domain": "Wearable"
},
{
"rank": 2,
"metric": "Sleep Duration (h)",
"current_value": 5.0,
"score_penalty": 2.5,
"domain": "Wearable"
},
{
"rank": 3,
"metric": "Resting Heart Rate (bpm)",
"current_value": 74.14,
"score_penalty": 1.5,
"domain": "Wearable"
},
{
"rank": 4,
"metric": "LDL Cholesterol (mg/dL)",
"current_value": 130.0,
"score_penalty": 0.6,
"domain": "Clinical"
},
{
"rank": 5,
"metric": "Systolic Blood Pressure (mmHg)",
"current_value": 128.0,
"score_penalty": 0.6,
"domain": "Clinical"
},
{
"rank": 6,
"metric": "Triglycerides (mg/dL)",
"current_value": 160.0,
"score_penalty": 0.0,
"domain": "Clinical"
}
],
"wearable_captions": {
"steps": "avg 7,143 step/day",
"sleep_duration": "YTD Night 5 Hrs",
"rhr": "avg 74 bpm/day",
"spo2": "YTD Night 98 %",
"vo2max": "Last. 32 ml/kg/min",
"hrv": "avg 69 ms/day"
},
"input_values": {
"age": 35.0,
"sex": "M",
"weight_kg": 75.0,
"height_cm": 175.0,
"rhr": 74.14,
"hrv_rmssd": 69.43,
"sbp": 128.0,
"triglycerides": 160.0,
"hdl": 52.0,
"ldl": 130.0,
"hba1c": 5.6,
"steps": 7142.86,
"sleep_hours": 5.0,
"spo2_min": 98.0,
"vo2max": 32.0
},
"generated_at": "2026-07-09T18:13:55.786543+00:00"
}
Integration Best Practices
Recommended call pattern:
• /calculate → Call on each app open or user interaction (score gauge widget)
• /partner/daily-refresh → Call once on first morning launch (update summaries + drivers)
Partial Data
Both endpoints accept partial input — all fields are optional. The engine always returns
all 3 scores (total, wearable, clinical) regardless of how much data is provided.
Missing fields use neutral/healthy defaults in the calculation.
Field Name Aliases
For convenience, the API accepts both naming conventions:
| Primary Name | Also Accepts |
steps_7d_avg | steps |
sleep_hours_7d_avg | sleep_hours |
Code Examples
Python — GCP API Gateway
import requests
url = "https://mytofit-partner-gw-202hslfc.an.gateway.dev/calculate"
payload = {
"age": 35, "sex": "M",
"weight_kg": 70, "height_cm": 170,
"sbp": 120, "triglycerides": 150, "ldl": 120, "hba1c": 5.5,
"steps_7d_avg": 8000, "sleep_hours_7d_avg": 7.5,
"rhr": 60, "vo2max": 45, "hrv_rmssd": 50, "spo2_min": 96,
"smoking": "never", "alcohol": "none"
}
headers = {
"Content-Type": "application/json",
"X-API-Key": "YOUR_GCP_API_KEY"
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
Python — Array Payload (History)
import requests
url = "https://mytofit-partner-gw-202hslfc.an.gateway.dev/calculate"
payload = {
"age": 35, "sex": "M",
"steps_history": [10000, 8000, 9000, 4000, 8000, 6000, 5000],
"vo2max_history": [32, 32, 32, 32, 32, 33, 33],
"sleep_history": [5, 4.2, 4, 6, 6.3, 6.45, 5]
}
headers = {
"Content-Type": "application/json",
"X-API-Key": "YOUR_GCP_API_KEY"
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
JavaScript / Fetch — GCP API Gateway
const response = await fetch("https://mytofit-partner-gw-202hslfc.an.gateway.dev/calculate", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "YOUR_GCP_API_KEY"
},
body: JSON.stringify({
age: 35, sex: "M",
weight_kg: 70, height_cm: 170,
sbp: 120, rhr: 60,
steps_7d_avg: 8000, sleep_hours_7d_avg: 7.5,
smoking: "never", alcohol: "none"
})
});
const data = await response.json();
console.log(data.total_score.score, data.total_score.color);
Swift (iOS) — GCP API Gateway
let url = URL(string: "https://mytofit-partner-gw-202hslfc.an.gateway.dev/calculate")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("YOUR_GCP_API_KEY", forHTTPHeaderField: "X-API-Key")
let body: [String: Any] = [
"age": 35, "sex": "M",
"weight_kg": 70, "height_cm": 170,
"rhr": 60,
"steps_7d_avg": 8000, "smoking": "never"
]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
Kotlin (Android) — GCP API Gateway
val client = OkHttpClient()
val json = """{"age":35,"sex":"M","weight_kg":70,"height_cm":170,"rhr":60,"steps_7d_avg":8000}"""
val body = json.toRequestBody("application/json".toMediaType())
val request = Request.Builder()
.url("https://mytofit-partner-gw-202hslfc.an.gateway.dev/calculate")
.post(body)
.addHeader("X-API-Key", "YOUR_GCP_API_KEY")
.build()
val response = client.newCall(request).execute()
cURL — GCP API Gateway
curl --request POST \
--url https://mytofit-partner-gw-202hslfc.an.gateway.dev/calculate \
--header 'Content-Type: application/json' \
--header 'X-API-Key: YOUR_GCP_API_KEY' \
--data '{"age":35,"sex":"M","weight_kg":70,"height_cm":170,"sbp":120,"rhr":60,"steps_7d_avg":8000,"smoking":"never"}'
Error Handling
| Status | Meaning | Action |
200 |
Success |
Parse the JSON response body |
401 |
Invalid or missing API key |
Check your X-API-Key header |
422 |
Validation error (invalid field values) |
Check the detail array for field-level errors |
429 |
Rate limit exceeded |
Reduce call frequency. Limits: 30/min for /calculate, 15/min for /daily-refresh |
500 |
Internal server error |
Retry with exponential backoff. Contact support if persistent. |
422 responses never echo submitted health data.
Only the field name and validation message are returned — no PHI leakage.