Documentation

Keely for developers

Conventions.

What every answer has in common, so your app can read any of them the same way.

Updated

On this page
  1. JSON and CORS
  2. Dates and times
  3. Units
  4. Missing is null
  5. Not allowed is named
  6. as_of and methodology
  7. Sample data
  8. Versions

JSON and CORS

JSON, UTF-8, Cache-Control: no-store. CORS is open (Access-Control-Allow-Origin: *), so a browser app can call the API directly.

Dates and times

Dates (2026-09-27) are the person’s local dates, as the Keely app stores them. Times are UTC, ISO 8601 with a Z. Nothing is converted on the server, so a night’s start and end are the instants it ran, and its date is the local morning it ended on. Date ranges (from, to) are inclusive.

Units

Units are in field names: hrv_ms, resting_hr_bpm, asleep_minutes, skin_temp_deviation_c.

Missing is null

A date without a night has recovery: null, with the reason in missing. A Recovery still calibrating says so in its status and note. An activity the person added for a time the band has no heart rate for keeps its sport and time, with Load and heart rate null, never zero.

Not allowed is named

When parts of an answer need scopes you don’t have, those keys are left out and listed in not_allowed; when the whole answer does, the API answers 403 insufficient_scope. An empty table never reads as “nothing happened”. Permissions has the detail.

as_of and methodology

Every answer carries as_of, the band’s last sync to the person’s iPhone (null before the first), and methodology, the page that explains every number: meetkeely.com/methodology. GET /v1/explain/{topic} gives the same in words and as a formula, per number.

Sample data

Answers from Keely’s review account also carry sample_data: true and a sample_note. Real accounts never do: Keely shows nothing it didn’t measure.

Versions

The version is in the path, /v1, and GET /v1 answers it as version; every webhook body carries it as api_version. Within a version, fields and event types may be added (a subscription to all types hears a new one without a change), so read what you need and let the rest pass. What changes, and when, is in the changelog.