REST API
The VitalTrends API lets you query your own health data programmatically. Build scripts, notebooks, automations, or pipe your data into any tool you like.
Authentication
All API requests must include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Keep your API key secret. Regenerate it from the Developer settings page if it is ever exposed.
Base URL
https://vitaltrends.net/api/v1
All endpoints return JSON. Dates are ISO 8601 strings (YYYY-MM-DD). Timestamps are UTC.
Endpoints
WHOOP
| Method | Path | Description |
|---|---|---|
| GET | /whoop/daily |
Recovery, HRV, RHR, sleep score, and strain per day |
| GET | /whoop/workouts |
Workout records with sport, duration, and heart rate zones |
| GET | /whoop/sleep |
Sleep sessions with stages and performance scores |
Withings
| Method | Path | Description |
|---|---|---|
| GET | /withings/measurements |
Weight, body fat, muscle mass, and bone mass readings |
Apple Health
| Method | Path | Description |
|---|---|---|
| GET | /apple-health/daily-summary |
Everything for a single day in one call: activity, heart, sleep stages, body, workouts |
| GET | /apple-health/daily |
Paginated daily totals across a date range (legacy wide-table shape) |
| GET | /apple-health?type=<type> |
Per-type time series. Append &include=metadata to surface sleep stages and workout details. |
Daily summary, one request for a full picture
Use this when you want the morning-dashboard shape: every metric for a given date, including a sleep stage breakdown (deep, rem, core, awake, in_bed) and full workout metadata (sport, duration, HR min/avg/max, energy, flights climbed).
| Parameter | Type | Default | Description |
|---|---|---|---|
date | date | today (UTC) | Single day, YYYY-MM-DD |
types | csv | all | Subset to include. Any of: activity, heart, sleep, body, workouts |
curl -s "https://vitaltrends.net/api/v1/apple-health/daily-summary?date=2026-04-21" \
-H "Authorization: Bearer YOUR_API_KEY" | jq .
{
"data": {
"date": "2026-04-21",
"activity": {
"steps": 12500,
"distance_km": 8.234,
"active_energy_kcal": 487.2,
"basal_energy_kcal": 1789.4,
"apple_move_time_min": 45,
"time_in_daylight_min": 87,
"apple_stand_hours": 12
},
"heart": {
"avg_heart_rate": 68,
"min_heart_rate": 52,
"max_heart_rate": 134,
"resting_heart_rate": 56,
"hrv_ms": 45.2,
"respiratory_rate": 14.2,
"spo2_pct": 97.8
},
"sleep": {
"total_minutes": 452,
"start_time": "2026-04-20T23:14:00+00:00",
"end_time": "2026-04-21T06:46:00+00:00",
"stages": {
"deep_min": 68,
"rem_min": 92,
"core_min": 256,
"light_min": 0,
"awake_min": 36,
"in_bed_min": 480,
"unknown_min": 0
},
"sources": ["Apple Watch"]
},
"body": {
"weight_kg": 75.20,
"body_fat_pct": 18.40,
"lean_body_mass_kg": 61.30,
"bmi": 23.4,
"height_m": 1.79
},
"workouts": [
{
"uuid": "018f0a3b-1c7e-7d9a-8a2b-8c5c47b3d401",
"type": "Functional Strength Training",
"sport_key": "functional_strength_training",
"start_time": "2026-04-21T07:02:00+00:00",
"end_time": "2026-04-21T07:47:00+00:00",
"duration_min": 45,
"active_energy_kcal": 320,
"distance_m": null,
"avg_heart_rate": 132,
"min_heart_rate": 85,
"max_heart_rate": 165,
"flights_climbed": 3,
"elevation_gain_m": null,
"source": "Apple Watch"
}
],
"meta": {
"unavailable_types": ["apple_exercise_time", "apple_stand_time", "vo2_max"],
"unavailable_reason": "Not ingested by the VitalTrends iOS companion app yet."
}
}
}
Per-type time series, with optional metadata
Use this when you want raw samples, a date range, or per-segment sleep stages. Pass include=metadata to reveal the metadata field (sleep stage, workout type, labels).
curl -s "https://vitaltrends.net/api/v1/apple-health?type=sleep&start=2026-04-20&end=2026-04-21&include=metadata" \
-H "Authorization: Bearer YOUR_API_KEY" | jq .
{
"data": [
{
"data_type": "sleep",
"date": "2026-04-20",
"start_time": "2026-04-21T01:45:00+00:00",
"end_time": "2026-04-21T05:45:00+00:00",
"value": 0,
"value_min": null,
"value_max": null,
"sample_count": 1,
"unit": "hr",
"source": "Apple Watch",
"metadata": { "stage": "core" }
},
{
"data_type": "sleep",
"date": "2026-04-20",
"start_time": "2026-04-20T23:15:00+00:00",
"end_time": "2026-04-21T00:15:00+00:00",
"value": 0,
"value_min": null,
"value_max": null,
"sample_count": 1,
"unit": "hr",
"source": "Apple Watch",
"metadata": { "stage": "deep" }
}
],
"links": { "first": "...", "last": null, "prev": null, "next": null },
"meta": { "current_page": 1, "from": 1, "path": "...", "per_page": 50, "to": 2 }
}
Valid type values
| Category | Values |
|---|---|
| Activity | steps, distance_walking_running, active_energy_burned, basal_energy_burned, apple_move_time, apple_stand_hour, time_in_daylight, physical_effort, swimming_stroke_count_qty, workout_effort_score, estimated_workout_effort_score |
| Heart & vitals | heart_rate, resting_heart_rate, heart_rate_variability, heart_rate_recovery, respiratory_rate, oxygen_saturation, blood_pressure_systolic, blood_pressure_diastolic, blood_glucose, body_temperature, afib_burden |
| Sleep | sleep, apple_sleeping_breathing_disturbances, sleep_apnea_event |
| Workouts | workouts |
| Body composition | body_mass, body_fat_percentage, lean_body_mass, body_mass_index, height |
| Mobility | walking_double_support, walking_steadiness, stair_ascent_speed, stair_descent_speed, six_minute_walk_distance |
| Running & cycling | running_power, cycling_power, cycling_speed |
| Water sports | underwater_depth, water_temperature, distance_paddle_sports, paddle_sports_speed, distance_rowing, rowing_speed |
| Winter sports | distance_cross_country_skiing, cross_country_skiing_speed, distance_skating_sports |
| Fitness events | low_cardio_fitness_event |
Availability depends on which HealthKit sensors and iOS version your device supports. Types that are defined but not yet ingested by the VitalTrends iOS companion app are listed in meta.unavailable_types on the daily summary response.
Query parameters
All list endpoints support the following parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
start | date | 30 days ago | Inclusive start date (YYYY-MM-DD) |
end | date | today | Inclusive end date (YYYY-MM-DD) |
per_page | integer | 50 | Records per page (max 200) |
page | integer | 1 | Page number (1-indexed) |
Example request
curl -s "https://vitaltrends.net/api/v1/whoop/daily?start=2024-01-01&per_page=7" \
-H "Authorization: Bearer YOUR_API_KEY" | jq .
Example response
{
"data": [
{
"date": "2024-01-07",
"recovery_score": 82,
"hrv_rmssd_milli": 68.4,
"resting_heart_rate": 48,
"sleep_performance_pct": 89,
"sleep_duration_minutes": 450,
"strain": 12.4
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 7,
"total": 7,
"from": 1,
"to": 7
}
}
Rate limits
The API allows 500 requests per minute per API key. If you exceed the limit, requests return 429 Too Many Requests with a Retry-After header.
Error responses
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
403 | Valid key but subscription required |
422 | Invalid query parameters |
429 | Rate limit exceeded |
500 | Server error, try again shortly |
Need an endpoint that is not listed here? Get in touch and we will consider adding it.