Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no cardYour first API call in 2 minutes →
Garmin API to FHIR: heart rate, sleep and steps (with code)
Read Garmin Health API or Personal API data and store heart rate, sleep and steps as FHIR R4 Observations, with access tiers, auth and LOINC mapping.
The Garmin Health API, part of the Garmin Connect Developer Program, gives approved companies a person's daily summaries, sleep, stress, Pulse Ox, respiration and HRV after the person consents. The self-serve Garmin Personal API gives you your own Garmin data. You can store each reading as a LOINC-coded FHIR R4 Observation with one standard API call. Anpheros does not connect to Garmin's cloud itself: your code reads Garmin, and Anpheros keeps the record with the person's consent. On Android, Garmin Connect shares activity data with other apps through Health Connect.
Read Garmin data and store it in FHIR
Garmin publishes the business API specifications (Health API, Activity API) only inside its Developer Portal, after approval, so this page does not copy those paths. The public part is the Garmin Personal API, for your own account. Create a key in Garmin Connect, then exchange it for a 24-hour token:
curl -X POST "https://personal.connectapi.garmin.com/personalapitoken-service/personal-api/personal-api-key/token" \
-H "Authorization: ApiKey $GARMIN_PERSONAL_API_KEY"
With that token, the Personal API returns the night's sleep at GET /sleep-service/sleep/dailySleep?date=2026-11-03 and daily steps at GET /usersummary-service/stats/steps/daily/{startDate}/{endDate} (base https://personal.connectapi.garmin.com). Send the token the way Garmin's Personal API documentation shows.
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:20:00+01:00","end":"2026-11-03T06:55:00+01:00"},
"valueQuantity":{"value":455,"unit":"min","system":"http://unitsofmeasure.org","code":"min"},
"device":{"display":"Garmin watch (Garmin Connect)"}}'
The same in Python: daily step totals become one Observation per day, 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"}
TZ = "+01:00" # the person's UTC offset
# Daily step totals your backend already has from Garmin (Health API push or the Personal API steps endpoint).
steps_by_day = {"2026-11-01": 8412, "2026-11-02": 10233}
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": "41950-7", "display": "Number of steps in 24 hour Measured"}]},
"subject": {"reference": "Patient/<id>"},
"effectivePeriod": {"start": f"{day}T00:00:00{TZ}", "end": f"{day}T23:59:59{TZ}"},
"valueQuantity": {"value": steps, "unit": "{steps}", "system": "http://unitsofmeasure.org", "code": "{steps}"},
"device": {"display": "Garmin watch (Garmin Connect)"},
},
} for day, steps in steps_by_day.items()]
r = requests.post(FHIR, headers=fhir, json={"resourceType": "Bundle", "type": "transaction", "entry": entries})
print(r.status_code, len(entries), "days saved")
And in JavaScript (Node 18+), one day's heart rate with its minimum and maximum:
const fhir = { Authorization: `Bearer ${process.env.ANPHEROS_KEY}`, 'Content-Type': 'application/fhir+json' };
// From Garmin: a daily summary (Health API) or the Personal API dailyHeartRate endpoint.
const heartRateForDay = { day: '2026-11-02', avg: 68, min: 49, max: 141 };
const bpm = (value) => ({ value, unit: '/min', system: 'http://unitsofmeasure.org', code: '/min' });
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: 'http://loinc.org', code: '8867-4', display: 'Heart rate' }] },
subject: { reference: 'Patient/<id>' },
effectivePeriod: { start: `${heartRateForDay.day}T00:00:00+01:00`, end: `${heartRateForDay.day}T23:59:59+01:00` },
valueQuantity: bpm(heartRateForDay.avg),
component: [
{ code: { text: 'minimum' }, valueQuantity: bpm(heartRateForDay.min) },
{ code: { text: 'maximum' }, valueQuantity: bpm(heartRateForDay.max) },
],
device: { display: 'Garmin watch (Garmin Connect)' },
}),
});
Garmin's response field names are documented in its Developer Portal and in the Personal API specification, 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 day is synced again, give each Observation a stable identifier, as shown in Wearable data in FHIR.
Ways to get Garmin data into FHIR
| Path | Status | What you need |
|---|---|---|
| Garmin pushes Health API data to your backend, which writes FHIR to Anpheros | Works today | Business approval in the Garmin Connect Developer Program and the person's consent. The Starter tier is push only, with no historical data |
| Your backend reads the Garmin Personal API, then writes FHIR to Anpheros | Works today | Your own Garmin account only. Garmin says it is not for commercial apps that serve many users |
| Garmin Connect → Health Connect → your Android app → Anpheros | Works today | Health Connect permissions. Garmin documents activity data but does not list the exact types. See Health Connect to FHIR |
| Garmin Connect → Apple Health → your iPhone app → Anpheros | Works today | Garmin Connect writes to Apple Health when the person turns it on; Garmin does not publish the full list of types. See Apple Health to FHIR |
| A direct Garmin connector inside Anpheros | On our roadmap | Not available yet. Use the button below to tell us you need it |
The Garmin Health API and Personal API in short
Access. The Garmin Connect Developer Program has three tiers. Personal is self-serve and covers only your own Health, Activity and Women's Health data, by pull. Business Starter needs an application; it opens the Activity, Courses, Health, Training and Women's Health APIs, push only, with no historical data and limited user capacity. Business Enterprise adds the Golf API, push or ping/pull, at least one month of history, unlimited users and technical support, on terms negotiated case by case. Garmin's program page says approved companies get free access. After approval you get the Developer Portal, and Garmin puts a typical integration at 1 to 4 weeks.
Auth. The business APIs use OAuth 2.0 with PKCE; Garmin's specification is dated July 2025. Its token URL is https://diauth.garmin.com/di-oauth2-service/oauth/token, and the person consents at https://connect.garmin.com/oauth2Confirm. Token lifetimes and permission names are not public. The Personal API uses a key created in Garmin Connect settings, valid for 30, 90 or 180 days or one year and shown once. Each token from that key lasts 24 hours, with no refresh token.
| Data | Endpoint (Personal API, GET, base https://personal.connectapi.garmin.com) |
Fields |
|---|---|---|
| Sleep | /sleep-service/sleep/dailySleep?date=YYYY-MM-DD, /sleep-service/sleep/dailySleepSummariesByDate?startDate=&endDate= |
one night, or up to 31 days |
| Heart rate | /wellness-service/wellness/dailyHeartRate?date=YYYY-MM-DD |
one day |
| HRV | /hrv-service/hrv/{calendarDate}, /hrv-service/hrv/daily/{fromDate}/{toDate} |
one day or a range |
| Steps | /usersummary-service/stats/steps/daily/{startDate}/{endDate} |
up to 28 days |
| SpO₂ | /wellness-service/wellness/daily/spo2acclimation/{calendarDate} |
one day |
| Also | body battery, stress, hydration, weight, daily summaries, menstrual cycle, activities and FIT files, fitness age |
The business Health API covers daily summaries, epochs, sleep with a sleep score, stress, body composition, Pulse Ox, respiration, overnight HRV summaries on some devices, and a Health Snapshot summary. The Activity API returns JSON and FIT files. Field names for both are in the Developer Portal. Garmin does not publish rate limits for either tier, and a portal post limits backfill history to two years per user. The business tiers work by push: Garmin sends new data to your endpoint (Enterprise can also use ping/pull).
Sources: Garmin Connect Developer Program overview, program FAQ, Personal API FAQ and specification, OAuth 2.0 PKCE specification, Garmin press release on Health Connect, November 2026.
Garmin data as FHIR Observations
| Garmin data | LOINC | UCUM unit | Note |
|---|---|---|---|
| Sleep duration | 93832-4 | min |
one per night |
| Steps | 41950-7 | {steps} |
one per day |
| Heart rate | 8867-4 | /min |
daily average, with minimum and maximum as components |
| Pulse Ox (SpO₂) | 59408-5 | % |
|
| Respiration | 9279-1 | /min |
|
| Body weight (body composition) | 29463-7 | kg |
|
| HRV summaries | see note | ms |
Garmin's public pages do not name the statistic; do not file it under LOINC 80404-7 unless it is SDNN. See Wearable data in FHIR |
| Stress, sleep score, Body Battery | none | Garmin scores with no LOINC code; keep them under your own code system or leave them out |
Frequently asked questions
Is the Garmin Health API free?
Garmin's program page says approved companies get free access to the Garmin Connect Developer Program. Enterprise terms are negotiated case by case, and Garmin publishes no price for that tier.
How do I get access to the Garmin Health API?
Apply through Garmin's business developer access form. After approval you get the Developer Portal, where the Health and Activity API specifications live.
What is the Garmin Personal API?
A self-serve API for your own Garmin data. You create a key in Garmin Connect and exchange it for a 24-hour token. Garmin says it is not meant for commercial apps that serve many users.
What is the Garmin Health API rate limit?
Garmin does not publish rate limits for the business or the Personal API. A Garmin portal post limits backfill history to two years per user.
Does Garmin Connect sync to Health Connect or Apple Health?
Garmin says Garmin Connect shares activity data with other Android apps through Health Connect, without listing every type. We found no Garmin source for Apple Health, so this page does not rely on it.
Does Anpheros connect to Garmin directly?
Not yet. Today your code reads the Garmin API (or Health Connect on the phone) and writes standard FHIR to Anpheros. A direct Garmin connector is on our roadmap; tell us with the button on this page if you need it.
Related
- Wearable APIs: every brand compared
- Wearable data in FHIR (Apple Health, Health Connect)
- Apple Health (HealthKit) to FHIR
- Health Connect to FHIR
- Oura API to FHIR · Polar AccessLink API to FHIR · COROS API to FHIR
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/garmin-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.Open in ClaudeOpen in ChatGPTThis guide as MarkdownBuild the whole integration with an AI coding tool
Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no cardYour first API call in 2 minutes →