HealthSave API Contract

Updated April 2026

HealthSave can sync Apple Health data to any self-hosted server that implements this contract. Configure the app with the base server URL only, such as https://health.example.com; HealthSave appends the endpoint paths below.

  • Base URL: enter only the origin or base path in the app.
  • Authentication: optional x-api-key header.
  • Ingest: POST /api/apple/batch receives metric batches.
  • Status: GET /api/apple/status returns a flat metric dictionary.

Endpoints

Method Path Purpose
GET /health Minimal process health check.
GET /api/health App-friendly health check.
POST /api/apple/batch Receives one HealthKit metric batch.
GET /api/apple/status Returns per-metric server counts for the sync status screen.

Authentication

If your backend requires an API key, HealthSave sends it with protected requests as an x-api-key header.

x-api-key: your-api-key

Health Checks

Both health endpoints should return a simple success object.

{
  "status": "ok"
}

Batch Ingest

POST /api/apple/batch receives one metric at a time. A successful 2xx response is enough for the app; the response body below matches the reference backend.

{
  "metric": "heart_rate",
  "batch_index": 0,
  "total_batches": 1,
  "samples": [
    {
      "date": "2026-04-10T12:00:00Z",
      "qty": 72,
      "source": "Apple Watch"
    }
  ]
}
{
  "status": "processed",
  "metric": "heart_rate",
  "batch": 0,
  "total_batches": 1,
  "records": 1
}

Quantity Samples

Quantity-like metrics use date, qty, and source. Some compatible servers also accept unit, but HealthSave does not require it on every sample.

Sleep Samples

{
  "metric": "sleep_analysis",
  "samples": [
    {
      "startDate": "2026-04-09T23:20:00Z",
      "endDate": "2026-04-10T06:45:00Z",
      "value": 3,
      "source": "Apple Watch"
    }
  ]
}

Workout Samples

{
  "metric": "workouts",
  "samples": [
    {
      "name": "Running",
      "start": "2026-04-10T07:00:00Z",
      "end": "2026-04-10T07:45:00Z",
      "duration": 2700,
      "source": "Apple Watch",
      "activeEnergy": 420,
      "distance": 6500,
      "avgHeartRate": 145,
      "maxHeartRate": 178,
      "heartRateData": [
        {"date": "2026-04-10T07:01:00Z", "qty": 132}
      ],
      "route": [
        {
          "latitude": 41.01,
          "longitude": 28.97,
          "altitude": 42.0,
          "speed": 2.8,
          "timestamp": "2026-04-10T07:01:00Z"
        }
      ]
    }
  ]
}

Activity Summary Samples

{
  "metric": "activity_summaries",
  "samples": [
    {
      "date": "2026-04-10",
      "activeEnergyBurned": 540,
      "activeEnergyBurnedGoal": 600,
      "appleExerciseTime": 42,
      "appleExerciseTimeGoal": 30,
      "appleStandHours": 12,
      "appleStandHoursGoal": 12
    }
  ]
}

Blood Pressure Correlations

Blood pressure batches use metric: "blood_pressure" at the batch level. Each sample carries its own inner metric value so systolic and diastolic readings remain distinct.

{
  "metric": "blood_pressure",
  "samples": [
    {
      "metric": "blood_pressure_systolic",
      "date": "2026-04-10T09:00:00Z",
      "qty": 120,
      "source": "Blood Pressure Monitor"
    },
    {
      "metric": "blood_pressure_diastolic",
      "date": "2026-04-10T09:00:00Z",
      "qty": 80,
      "source": "Blood Pressure Monitor"
    }
  ]
}

Category Event Samples

Category events use date, qty, source, and, when available, endDate plus rawValue. For duration-based events, qty is the duration in seconds and rawValue keeps the raw HealthKit category value. For instant events, qty is the raw category value.

{
  "metric": "mindful_session",
  "samples": [
    {
      "date": "2026-04-10T08:00:00Z",
      "endDate": "2026-04-10T08:15:00Z",
      "qty": 900,
      "rawValue": 0,
      "source": "Apple Watch"
    }
  ]
}

ECG Samples

ECG batches can include start, end, classification, numberOfVoltageMeasurements, samplingFrequency, and averageHeartRate. Store them if your backend has an ECG model, or accept the batch for compatibility.

Status Contract

GET /api/apple/status must return a flat JSON object. Every top-level key is treated as a metric name, and every top-level value should be an object with at least count. oldest and newest are optional.

{
  "heart_rate": {
    "count": 123,
    "oldest": "2026-04-01T08:00:00Z",
    "newest": "2026-04-10T12:00:00Z"
  },
  "heart_rate_variability": {
    "count": 45,
    "oldest": "2026-04-01T08:00:00Z",
    "newest": "2026-04-10T12:00:00Z"
  },
  "quantity_samples": {
    "count": 900
  }
}

Do not return {"status":"ok","counts":{...}} from this endpoint. That wrapped shape makes status look like a metric and makes counts look like one metric instead of the metric dictionary.

Metric Catalog

HealthSave accepts compatible backends that store unknown metrics generically. The names below are the source-derived catalog the app can send.

Heart & Cardiovascular

heart_rate, resting_heart_rate, walking_heart_rate_average, heart_rate_variability, heart_rate_recovery, atrial_fibrillation_burden, vo2_max, oxygen_saturation, respiratory_rate, peripheral_perfusion_index

Blood Pressure & Metabolic

blood_pressure, blood_pressure_systolic, blood_pressure_diastolic, blood_glucose, insulin_delivery, blood_alcohol_content, number_of_alcoholic_beverages

Activity & Movement

step_count, distance_walking_running, distance_cycling, distance_swimming, distance_wheelchair, distance_downhill_snow_sports, distance_cross_country_skiing, distance_paddle_sports, distance_rowing, distance_skating_sports, flights_climbed, swimming_stroke_count, push_count, nike_fuel, apple_exercise_time, apple_stand_time, apple_move_time, active_energy_burned, basal_energy_burned, number_of_times_fallen

Mobility, Running & Cycling

walking_speed, walking_step_length, walking_asymmetry, walking_double_support, stair_ascent_speed, stair_descent_speed, apple_walking_steadiness, six_minute_walk_test_distance, running_power, running_speed, running_stride_length, running_vertical_oscillation, running_ground_contact_time, cycling_speed, cycling_power, cycling_cadence, cycling_functional_threshold_power

Sport Speeds, Effort & Body

cross_country_skiing_speed, paddle_sports_speed, rowing_speed, physical_effort, workout_effort_score, estimated_workout_effort_score, body_temperature, wrist_temperature, basal_body_temperature, body_mass, body_fat_percentage, bmi, lean_body_mass, height, waist_circumference, electrodermal_activity

Respiratory, Sleep, Environment & Water

forced_expiratory_volume_1, forced_vital_capacity, peak_expiratory_flow_rate, inhaler_usage, sleep_analysis, sleeping_breathing_disturbances, environmental_audio_exposure, headphone_audio_exposure, environmental_sound_reduction, uv_exposure, time_in_daylight, underwater_depth, water_temperature

Nutrition

dietary_energy_consumed, dietary_protein, dietary_fat_total, dietary_fat_saturated, dietary_fat_monounsaturated, dietary_fat_polyunsaturated, dietary_carbohydrates, dietary_sugar, dietary_fiber, dietary_cholesterol, dietary_sodium, dietary_potassium, dietary_calcium, dietary_iron, dietary_magnesium, dietary_phosphorus, dietary_zinc, dietary_manganese, dietary_copper, dietary_selenium, dietary_chromium, dietary_molybdenum, dietary_chloride, dietary_biotin, dietary_vitamin_a, dietary_vitamin_b6, dietary_vitamin_b12, dietary_vitamin_c, dietary_vitamin_d, dietary_vitamin_e, dietary_vitamin_k, dietary_folate, dietary_niacin, dietary_pantothenic_acid, dietary_riboflavin, dietary_thiamin, dietary_iodine, dietary_water, dietary_caffeine

Structured Types

workouts, activity_summaries, ecg

Category Events

high_heart_rate_event, low_heart_rate_event, irregular_heart_rhythm_event, low_cardio_fitness_event, mindful_session, handwashing_event, toothbrushing_event, environmental_audio_exposure_event, headphone_audio_exposure_event, apple_walking_steadiness_event

Reproductive Health & Symptoms

menstrual_flow, intermenstrual_bleeding, ovulation_test_result, cervical_mucus_quality, sexual_activity, contraceptive, pregnancy, pregnancy_test_result, lactation, progesterone_test_result, infrequent_menstrual_cycles, irregular_menstrual_cycles, persistent_intermenstrual_bleeding, prolonged_menstrual_periods, bleeding_after_pregnancy, bleeding_during_pregnancy, abdominal_cramps, acne, appetite_changes, generalized_body_ache, bloating, breast_pain, chest_tightness_or_pain, chills, constipation, coughing, diarrhea, dizziness, fainting, fatigue, fever, headache, heartburn, hot_flashes, lower_back_pain, loss_of_smell, loss_of_taste, mood_changes, nausea, pelvic_pain, rapid_pounding_or_fluttering_heartbeat, runny_nose, shortness_of_breath, sinus_congestion, skipped_heartbeat, sleep_changes, sore_throat, vomiting, wheezing, bladder_incontinence, dry_skin, hair_loss, vaginal_dryness, memory_lapse, night_sweats, sleep_apnea_event