AnpherosAnpheros PlatformDevelopers

Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no cardYour first API call in 2 minutes →

WHOOP API to FHIR: sleep, recovery and HRV (with code)

Read WHOOP API v2 sleep and recovery and store them as FHIR R4 Observations: OAuth scopes, rate limits, RMSSD handling and Apple Health limits.

The WHOOP API v2 gives your app a person's sleep, recovery, strain cycles, workouts and body measurements after they approve your OAuth app. Any WHOOP account can create apps in the WHOOP Developer Dashboard. You can store each reading as a LOINC-coded FHIR R4 Observation with one standard API call. Anpheros does not connect to WHOOP's cloud itself: your code reads WHOOP, and Anpheros keeps the record with the person's consent. On phones, the WHOOP app writes sleep, resting heart rate, respiratory rate, blood oxygen and steps to Apple Health and Health Connect, but it does not write HRV to Apple Health.

Read WHOOP data and store it in FHIR

First, the person's sleep records from WHOOP (an access token from your WHOOP OAuth app with the read:sleep scope):

curl -H "Authorization: Bearer $WHOOP_TOKEN" \
     "https://api.prod.whoop.com/developer/v2/activity/sleep"

Then the sleep duration as a FHIR Observation in the person's record (sandbox key sk_test_…):

curl -X POST https://platform.anpheros.com/fhir/R4/Observation \
  -H "Authorization: Bearer $ANPHEROS_KEY" \
  -H "Content-Type: application/fhir+json" \
  -H "X-Anpheros-Author-Type: device" \
  -d '{"resourceType":"Observation","status":"final",
       "category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"activity"}]}],
       "code":{"coding":[{"system":"http://loinc.org","code":"93832-4","display":"Sleep duration"}]},
       "subject":{"reference":"Patient/<id>"},
       "effectivePeriod":{"start":"2026-11-02T23:05:00+01:00","end":"2026-11-03T06:40:00+01:00"},
       "valueQuantity":{"value":418,"unit":"min","system":"http://unitsofmeasure.org","code":"min"},
       "device":{"display":"WHOOP"}}'

The same in Python: every night becomes one Observation, sent together in one transaction Bundle.

import os, requests

FHIR = "https://platform.anpheros.com/fhir/R4"
fhir = {"Authorization": f"Bearer {os.environ['ANPHEROS_KEY']}", "Content-Type": "application/fhir+json",
        "X-Anpheros-Author-Type": "device"}

# Nights your backend already read from GET /v2/activity/sleep; map WHOOP's fields
# (listed in its API reference) to start, end and minutes asleep.
sleep_by_night = [
    {"start": "2026-11-01T23:30:00+01:00", "end": "2026-11-02T07:02:00+01:00", "minutes": 421},
    {"start": "2026-11-02T23:05:00+01:00", "end": "2026-11-03T06:40:00+01:00", "minutes": 418},
]

entries = [{
    "request": {"method": "POST", "url": "Observation"},
    "resource": {
        "resourceType": "Observation", "status": "final",
        "category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "activity"}]}],
        "code": {"coding": [{"system": "http://loinc.org", "code": "93832-4", "display": "Sleep duration"}]},
        "subject": {"reference": "Patient/<id>"},
        "effectivePeriod": {"start": n["start"], "end": n["end"]},
        "valueQuantity": {"value": n["minutes"], "unit": "min", "system": "http://unitsofmeasure.org", "code": "min"},
        "device": {"display": "WHOOP"},
    },
} for n in sleep_by_night]

r = requests.post(FHIR, headers=fhir, json={"resourceType": "Bundle", "type": "transaction", "entry": entries})
print(r.status_code, len(entries), "nights saved")

And in JavaScript (Node 18+), the night's HRV from a recovery record. WHOOP measures HRV as RMSSD, so it goes under your own code, not under LOINC 80404-7 (which is SDNN):

const fhir = { Authorization: `Bearer ${process.env.ANPHEROS_KEY}`, 'Content-Type': 'application/fhir+json' };

// From GET /v2/recovery (scope read:recovery); field names are in WHOOP's API reference.
const recoveryForDay = { day: '2026-11-03', hrvRmssdMs: 62.4 };

await fetch('https://platform.anpheros.com/fhir/R4/Observation', {
  method: 'POST', headers: fhir,
  body: JSON.stringify({
    resourceType: 'Observation', status: 'final',
    category: [{ coding: [{ system: 'http://terminology.hl7.org/CodeSystem/observation-category', code: 'vital-signs' }] }],
    code: { coding: [{ system: 'https://your-app.test/fhir/CodeSystem/wearable', code: 'hrv-rmssd' }],
            text: 'Heart rate variability (RMSSD)' },
    subject: { reference: 'Patient/<id>' },
    effectiveDateTime: `${recoveryForDay.day}T07:00:00+01:00`,
    valueQuantity: { value: recoveryForDay.hrvRmssdMs, unit: 'ms', system: 'http://unitsofmeasure.org', code: 'ms' },
    device: { display: 'WHOOP' },
  }),
});

WHOOP's response field names are in its API reference, so the examples start from values your code has already read. The Anpheros side is standard FHIR R4, so the same requests work with any FHIR server. To avoid copies when a night is read again, give each Observation a stable identifier, as shown in Wearable data in FHIR.

Ways to get WHOOP data into FHIR

Path Status What you need
Your backend reads the WHOOP API v2, then writes FHIR to Anpheros Works today An app in the WHOOP Developer Dashboard (up to 5 per account) and the person's OAuth consent
WHOOP app → Apple Health → your iPhone app reads HealthKit → Anpheros Works today HealthKit permissions. No HRV: WHOOP does not write it to Apple Health. See Apple Health to FHIR
WHOOP app → Health Connect → your Android app → Anpheros Works today Health Connect permissions. Some types depend on the WHOOP plan; resting heart rate and respiratory rate need Peak. See Health Connect to FHIR
A direct WHOOP connector inside Anpheros On our roadmap Not available yet. Use the button below to tell us you need it

The WHOOP API in short

Access. Sign in to the WHOOP Developer Dashboard with a WHOOP account and create an app. Each account can have up to 5 apps, and you can ask for more. WHOOP's developer docs do not say whether the person needs a paid membership or whether an app is reviewed before wide release. A separate client-credentials flow exists for WHOOP's Trusted Partner endpoints.

Auth. OAuth 2.0 authorization code flow. Authorize at https://api.prod.whoop.com/oauth/oauth2/auth, exchange the code at https://api.prod.whoop.com/oauth/oauth2/token. The state you send must be 8 characters. Scopes: read:recovery, read:cycles, read:workout, read:sleep, read:profile, read:body_measurement, and offline for a refresh token. Access tokens are short-lived (the docs' example shows one hour). Refresh tokens rotate: each refresh invalidates the old access and refresh tokens.

Data Endpoint (GET, base https://api.prod.whoop.com/developer) Fields
Sleep /v2/activity/sleep, /v2/activity/sleep/{sleepId} sleep records
Recovery /v2/recovery, /v2/cycle/{cycleId}/recovery recovery, including HRV and resting heart rate
Cycles (strain) /v2/cycle, /v2/cycle/{cycleId}, /v2/cycle/{cycleId}/sleep daily strain cycles
Workouts /v2/activity/workout workouts
Body /v2/user/measurement/body height, weight, max heart rate
Profile /v2/user/profile/basic basic profile

There is no steps endpoint in the API; WHOOP writes steps to Apple Health and Health Connect instead. DELETE /v2/user/access revokes your app's access, and GET /v1/activity-mapping/{activityV1Id} turns an old v1 id into the v2 UUID. Default rate limits are 100 requests per minute and 10,000 per day, with X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers and a 429 when you go over; ask for more in the dashboard. All data endpoints are now v2 with UUID ids, and WHOOP has removed its v1 webhooks and publishes a v1 to v2 migration guide.

Sources: WHOOP API reference, getting started, OAuth, rate limiting, WHOOP and Apple Health, WHOOP and Health Connect, November 2026.

WHOOP data as FHIR Observations

WHOOP data LOINC UCUM unit Note
Sleep duration 93832-4 min one per night
Resting heart rate (recovery) 40443-4 /min one per day
HRV (recovery) see note ms WHOOP uses RMSSD; never file it under LOINC 80404-7, which is SDNN. Use your own code. See Wearable data in FHIR
Respiratory rate 9279-1 /min
Blood oxygen (SpO₂) 59408-5 %
Body weight 29463-7 kg from body measurements
Recovery score, strain none WHOOP scores with no LOINC code; keep them under your own code system or leave them out

Frequently asked questions

How do I get access to the WHOOP API?

Sign in to the WHOOP Developer Dashboard with a WHOOP account and create an app, then send people through WHOOP's OAuth flow. Each account can hold up to 5 apps, and you can ask for more.

Is the WHOOP API free?

WHOOP's developer docs do not state a price, and they do not say whether the person needs a paid membership for API data.

Does WHOOP sync HRV to Apple Health?

No. WHOOP says it does not write HRV to Apple Health, because WHOOP measures RMSSD and Apple Health stores SDNN. To get HRV, read it from the WHOOP API recovery data.

Does the WHOOP API have steps?

No steps endpoint is listed in the WHOOP API. The WHOOP app writes steps to Apple Health and Health Connect, so read them there.

What is the WHOOP API rate limit?

100 requests per minute and 10,000 per day by default. You can ask for an increase in the Developer Dashboard.

Does Anpheros connect to WHOOP directly?

Not yet. Today your code reads the WHOOP API (or HealthKit / Health Connect on the phone) and writes standard FHIR to Anpheros. A direct WHOOP connector is on our roadmap; tell us with the button on this page if you need it.

Related

Ask your AI

Paste this into Claude, ChatGPT, Cursor or Claude Code. Your assistant reads this guide and adapts it to your code.

Read https://developers.anpheros.com/guides/whoop-api.md and help me apply this guide to my own project. Use the Anpheros sandbox (base URL https://platform.anpheros.com) with my API key from the ANPHEROS_KEY environment variable, never written in the code. Give me complete, runnable code, the response I should expect, and how to fix the errors in https://developers.anpheros.com/guides/troubleshooting.md if a call fails.

Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no cardYour first API call in 2 minutes →

Product updates by email

New features, guides and the end of the beta. One email a month at most.

Send me occasional Anpheros Platform updates by email (new features, API changes, guides). I'll confirm by email and can unsubscribe in one click from any email. Privacy