APIs

Working at Vital allows me to examine how different providers create their API schemas. For example, for sleep, Withings, Oura, Whoop and Apple HealthKit do something different. It’s fascinating to imagine the thought process that went to these designs.

Oura looks like this:

{
  "summary_date": "2017-11-05",
  "period_id": 0,
  "is_longest": 1,
  "timezone": 120,
  "bedtime_start": "2017-11-06T02:13:19+02:00",
  "bedtime_end": "2017-11-06T08:12:19+02:00",
  "score": 70,
  "score_total": 57,
  "score_disturbances": 83,
  "score_efficiency": 99,
  "score_latency": 88,
  "score_rem": 97,
   "score_deep": 59,
  "score_alignment": 31,
  "total": 20310,
  "duration": 21540,
  "awake": 1230,
  "light": 10260,
  "rem": 7140,
  "deep": 2910,
  "onset_latency": 480,
  "restless": 39,
  "efficiency": 94,
  "midpoint_time": 11010,
  "hr_lowest": 49,
  "hr_average": 56.375,
  "rmssd": 54
  "breath_average": 13,
  "temperature_delta": -0.06,
  "hypnogram_5min": "443432222211222333321112222222222111133333322221112233333333332232222334",
  "hr_5min": [0, 53, 51, 0, 50, 50, 49, 49, 50, 50, 51, 52, 52, 51, 53, 58, 60, 60, 59, 58, 58, 58, 58, 55, 55, 55, 55, 56, 56, 55, 53, 53, 53, 53, 53, 53, 57, 58, 60, 60, 59, 57, 59, 58, 56, 56, 56, 56, 55, 55, 56, 56, 57, 58, 55, 56, 57, 60, 58, 58, 59, 57, 54, 54, 53, 52, 52, 55, 53, 54, 56, 0],
  "rmssd_5min": [0, 0, 62, 0, 75, 52, 56, 56, 64, 57, 55, 78, 77, 83, 70, 35, 21, 25, 49, 44, 48, 48, 62, 69, 66, 64, 79, 59, 67, 66, 70, 63, 53, 57, 53, 57, 38, 26, 18, 24, 30, 35, 36, 46, 53, 59, 50, 50, 53, 53, 57, 52, 41, 37, 49, 47, 48, 35, 32, 34, 52, 57, 62, 57, 70, 81, 81, 65, 69, 72, 64, 0]
}

Compared to Whoop:

{
  "id": 93845,
  "user_id": 10129,
  "created_at": "2022-04-24T11:25:44.774Z",
  "updated_at": "2022-04-24T14:25:44.774Z",
  "start": "2022-04-24T02:25:44.774Z",
  "end": "2022-04-24T10:25:44.774Z",
  "timezone_offset": "-05:00",
  "nap": false,
  "score_state": "SCORED",
  "score": {
    "stage_summary": {
      "total_in_bed_time_milli": 30272735,
      "total_awake_time_milli": 1403507,
      "total_no_data_time_milli": 0,
      "total_light_sleep_time_milli": 14905851,
      "total_slow_wave_sleep_time_milli": 6630370,
      "total_rem_sleep_time_milli": 5879573,
      "sleep_cycle_count": 3,
      "disturbance_count": 12
    },
    "sleep_needed": {
      "baseline_milli": 27395716,
      "need_from_sleep_debt_milli": 352230,
      "need_from_recent_strain_milli": 208595,
      "need_from_recent_nap_milli": -12312
    },
    "respiratory_rate": 16.11328125,
    "sleep_performance_percentage": 98,
    "sleep_consistency_percentage": 90,
    "sleep_efficiency_percentage": 91.69533848
  }
}

The above payloads are examples from their docs. But the reality is sometimes different from what’s publicly advertised - e.g. Whoop has extra fields besides those. In particular there’s significant duplication of fields when it comes to dates and oddities related to how start and end dates are calculated.

They provide a glimpse on how APIs evolved and how much effort goes into making sure there’s no API breakage. A person that sees the discrepancy between the example in the docs and the reality, might be tempted to criticise. How many times have we done that, after laying eyes for the first time in a codebase? But it also serves as a valuable lesson and provides clues how the API is evolving.