# 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.

Source: https://developers.anpheros.com/guides/google-health-api

**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):

```bash
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_…`):

```bash
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.

```python
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):

```js
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](https://developers.anpheros.com/guides/health-connect-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](https://developers.anpheros.com/guides/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 |

<!-- visual:cta-wear-google-health -->

## 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](https://developers.google.com/health/about), [migration guide](https://developers.google.com/health/migration), [data access](https://developers.google.com/health/migration/data-access), [scopes](https://developers.google.com/health/scopes), [app verification](https://developers.google.com/health/app-verification), [rate limits](https://developers.google.com/health/rate-limits), [release notes](https://developers.google.com/health/release-notes), [dataPoints.list reference](https://developers.google.com/health/reference/rest/v4/users.dataTypes.dataPoints/list), [Google Health app and Health Connect](https://support.google.com/fitbit/answer/14506680?hl=en), 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](https://developers.anpheros.com/guides/wearable-data-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](https://developers.anpheros.com/guides/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](https://developers.anpheros.com/guides/wearable-apis)
- [Wearable data in FHIR (Apple Health, Health Connect)](https://developers.anpheros.com/guides/wearable-data-fhir)
- [Apple Health (HealthKit) to FHIR](https://developers.anpheros.com/guides/apple-health-fhir)
- [Health Connect to FHIR](https://developers.anpheros.com/guides/health-connect-fhir)
- [Fitbit Web API shutdown](https://developers.anpheros.com/guides/fitbit-web-api-shutdown) · [Samsung Health to FHIR](https://developers.anpheros.com/guides/samsung-health-fhir) · [Oura API to FHIR](https://developers.anpheros.com/guides/oura-api)
