Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no cardYour first API call in 2 minutes →
Google Health API to FHIR: Fitbit and Pixel Watch data in your app
Read Fitbit and Pixel Watch data with the Google Health API, the Fitbit Web API successor, and store it as FHIR R4 Observations, with curl, Python and JS code.
The Google Health API is the successor of the Fitbit Web API. It gives your app a person's steps, sleep, heart rate, HRV, SpO₂ and blood glucose from Fitbit devices and Pixel Watches after they approve your Google OAuth app. Any developer can start from a Google Cloud project, but every scope is restricted: an unverified app is capped at 100 users, and a public app needs Google's verification and a yearly CASA security assessment. You can store each reading as a LOINC-coded FHIR R4 Observation with one standard API call. Anpheros does not connect to Google's cloud itself: your code reads the Google Health API (or Health Connect on the phone), and Anpheros keeps the record with the person's consent. On Android, the Google Health app (formerly the Fitbit app) also writes to Health Connect. Google documents no sync to Apple Health.
Read Google Health API data and store it in FHIR
First, the latest nights of sleep from the Google Health API (a Google OAuth access token with the https://www.googleapis.com/auth/googlehealth.sleep.readonly scope):
curl -G "https://health.googleapis.com/v4/users/me/dataTypes/sleep/dataPoints" \
-H "Authorization: Bearer $GOOGLE_TOKEN" \
--data-urlencode 'filter=sleep.interval.civil_end_time >= "2026-11-01"' \
--data-urlencode 'pageSize=25'
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":"Pixel Watch (via Google Health API)"}}'
The same in Python: steps per day, sent together in one transaction Bundle.
import os, requests
GOOGLE = "https://health.googleapis.com/v4/users/me/dataTypes"
FHIR = "https://platform.anpheros.com/fhir/R4"
google = {"Authorization": f"Bearer {os.environ['GOOGLE_TOKEN']}"}
fhir = {"Authorization": f"Bearer {os.environ['ANPHEROS_KEY']}", "Content-Type": "application/fhir+json"}
r = requests.get(f"{GOOGLE}/steps/dataPoints", headers=google, params={
"filter": 'steps.interval.start_time >= "2026-11-01T00:00:00Z"', "pageSize": 10000})
def steps_per_day(google_json):
"""Return [("2026-11-01", 8412), ...]. Map the data point fields from Google's steps data type reference."""
raise NotImplementedError
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": day, "end": day},
"valueQuantity": {"value": steps, "unit": "{steps}", "system": "http://unitsofmeasure.org", "code": "{steps}"},
"device": {"display": "Fitbit (via Google Health API)"},
},
} for day, steps in steps_per_day(r.json())]
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+), daily HRV. Google's HRV is RMSSD, so it gets its own code, not LOINC 80404-7 (that code is SDNN):
const google = { Authorization: `Bearer ${process.env.GOOGLE_TOKEN}` };
const fhir = { Authorization: `Bearer ${process.env.ANPHEROS_KEY}`, 'Content-Type': 'application/fhir+json' };
const url = new URL('https://health.googleapis.com/v4/users/me/dataTypes/daily-heart-rate-variability/dataPoints');
url.searchParams.set('filter', 'daily_heart_rate_variability.date >= "2026-11-01"');
const json = await (await fetch(url, { headers: google })).json();
// Returns [{ day: '2026-11-01', ms: 41 }, ...]. Map the fields from Google's HRV data type reference.
const readDailyHrv = (json) => { throw new Error('map Google HRV data points here'); };
for (const { day, ms } of readDailyHrv(json)) {
await fetch('https://platform.anpheros.com/fhir/R4/Observation', {
method: 'POST', headers: { ...fhir, 'X-Anpheros-Author-Type': 'device' },
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', display: 'Heart rate variability (RMSSD)' }] },
subject: { reference: 'Patient/<id>' },
effectivePeriod: { start: day, end: day },
valueQuantity: { value: ms, unit: 'ms', system: 'http://unitsofmeasure.org', code: 'ms' },
device: { display: 'Pixel Watch (via Google Health API)' },
}),
});
}
Data type names (sleep, steps, daily-heart-rate-variability) and filter fields (sleep.interval.civil_end_time, steps.interval.start_time, daily_heart_rate_variability.date) are from Google's Google Health API reference. The fields inside each data point are on Google's data type pages, so the two mapping functions are left for you to fill in. The Anpheros side is standard FHIR R4, so the same requests work with any FHIR server.
Ways to get Google Health API data into FHIR
| Path | Status | What you need |
|---|---|---|
| Your backend reads the Google Health API, then writes FHIR to Anpheros | Works today | A Google Cloud project, restricted googlehealth.* scopes and the person's consent. Up to 100 users until Google verifies your app; a public app needs a yearly CASA security assessment |
| Google Health app (formerly Fitbit) → Health Connect → your Android app → Anpheros | Works today | Health Connect permissions. See Health Connect to FHIR |
| Fitbit or Pixel Watch → Apple Health → your iPhone app → Anpheros | Not available | Not documented by Google. On iPhone, read the Google Health API from your backend instead |
| The legacy Fitbit Web API | Not available | Turned off in late October / early November 2026; calls return 410 Gone. See Fitbit Web API shutdown |
| A direct Google Health connector inside Anpheros | On our roadmap | Not available yet. Use the button below to tell us you need it |
The Google Health API in short
Access. Self-service, through a Google Cloud project. Data comes from all Fitbit devices and Pixel Watches, with a reconciled stream across sources. All scopes are restricted: an unverified app can have at most 100 users. To go public, your app needs OAuth app verification and an annual third-party CASA security assessment based on OWASP ASVS. An existing HITRUST, SOC 2 or ISO 27001 certificate can speed it up.
Auth. Google OAuth 2.0. Authorize at https://accounts.google.com/o/oauth2/v2/auth, exchange the code at https://oauth2.googleapis.com/token. Access tokens last 1 hour (Fitbit's lasted 8). Ask for access_type=offline to get a refresh token. Refresh tokens expire after 6 months without use, under time-based grants, or while the app is in "Testing" mode. Scopes start with https://www.googleapis.com/auth/googlehealth.: activity_and_fitness, health_metrics_and_measurements, sleep and nutrition, each as .readonly or .writeonly, plus profile, settings, ecg.readonly, irn.readonly, location.readonly and a few write-only scopes.
Moving from Fitbit. Old Fitbit tokens cannot be migrated, so every person must consent again. User IDs change too: GET /v4/users/me/identity maps the legacy Fitbit user ID to the new healthUserId.
| Data | dataType (GET /v4/users/me/dataTypes/{dataType}/dataPoints, base https://health.googleapis.com) |
Scope |
|---|---|---|
| Sleep | sleep |
sleep |
| Steps | steps |
activity_and_fitness |
| Heart rate | heart-rate, daily-resting-heart-rate |
health_metrics_and_measurements |
| HRV (RMSSD) | heart-rate-variability, daily-heart-rate-variability |
not stated by Google |
| SpO₂ | oxygen-saturation, daily-oxygen-saturation |
health_metrics_and_measurements |
| Blood glucose | blood-glucose (mg/dL) |
health_metrics_and_measurements |
The list call takes an AIP-160 filter, pageSize (default 1,440, maximum 10,000; 25 for sleep and exercise), pageToken and dataSourceFamily. Other methods: :rollUp, :dailyRollUp, :reconcile, get, create, patch, batchDelete and exportExerciseTcx. Webhooks work through projects.subscribers. A v4beta preview channel, with skin temperature, has existed since 1 October 2026.
Rate limits: 300 requests per minute per user, 120,000 per minute and 86.4 million per day per project. Unverified apps get at most 100 users at 2.5 QPS each. Over the limit, the API answers 429. If you expect more than 10,000 sign-ups or re-authorizations in 24 hours, ask Google for more OAuth quota in advance. The new API returns fewer SpO₂ points than the Fitbit Web API did for the same time range.
Recent changes. Launched on 24 March 2026. Webhooks and dataPoints.get arrived on 14 April. On 26 May, write-only scopes replaced the read/write scopes, ECG and IRN scopes were added and rate limits were published. Women's health data types came on 17 August, v4beta on 1 October and the dataSourceFamily filter on 14 October. Fitbit Web API support ended on 30 September, and the API was turned off in late October / early November 2026. The Google Fit APIs are supported only until the end of 2026. Google plans more Health Connect data types for Q4 2026 and Q1 2027.
Sources: About the Google Health API, migration guide, data access, scopes, app verification, rate limits, release notes, dataPoints.list reference, Google Health app and Health Connect, November 2026.
Google Health API data as FHIR Observations
| Google Health API data | LOINC | UCUM unit | Note |
|---|---|---|---|
Steps (steps) |
41950-7 | {steps} |
one per day; sum the day or use :dailyRollUp |
Sleep (sleep) |
93832-4 | min |
stages: 93831-6 deep, 93830-8 light, 93829-0 REM |
Heart rate (heart-rate) |
8867-4 | /min |
daily average, minimum and maximum rather than every sample |
Resting heart rate (daily-resting-heart-rate) |
40443-4 | /min |
one per day |
SpO₂ (oxygen-saturation, daily-oxygen-saturation) |
59408-5 | % |
category vital-signs |
Blood glucose (blood-glucose) |
2339-0 | mg/dL |
Google returns mg/dL |
HRV (heart-rate-variability) |
see note | ms |
Google reports RMSSD; do not file it under LOINC 80404-7, which is SDNN. See Wearable data in FHIR |
Frequently asked questions
What replaced the Fitbit Web API?
The Google Health API, which Google calls the next generation of the Fitbit Web API. It uses Google OAuth 2.0 and covers Fitbit devices and Pixel Watches. The Fitbit Web API was turned off in late October / early November 2026; see Fitbit Web API shutdown.
Is the Google Health API free?
Google does not publish a price for the API. A public app needs a yearly CASA security assessment by a third-party assessor, which is paid, but Google does not give an amount.
How many users can my Google Health API app have?
Up to 100 while it is unverified. After OAuth app verification and a CASA assessment, the cap goes away; per-user and per-project rate limits still apply.
Can I reuse my Fitbit OAuth tokens?
No. Fitbit tokens cannot be migrated: every person signs in again with Google OAuth. Use GET /v4/users/me/identity to match the old Fitbit user ID to the new healthUserId.
Can I get Fitbit data on iPhone?
Through the cloud, yes: your backend reads the Google Health API for iPhone users too. Google does not document a sync from the Google Health app to Apple Health.
Does Anpheros connect to Google Health or Fitbit directly?
Not yet. Today your code reads the Google Health API (or Health Connect on the phone) and writes standard FHIR to Anpheros. A direct 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
- Fitbit Web API shutdown · Samsung Health to FHIR · Oura 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/google-health-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 →