A plain-language guide to every Data Type the Google Health catalog exposes through gohealthcli. Each entry names the sync key you pass to sync --types, the upstream record shape (sample / interval / session / daily), the OAuth scope required, and what the catalog row supports. For the canonical machine-readable list, see the README; for storage shape, see docs/data-model.md.
#How to read this page
- Sync key — the literal string accepted by
sync --types. - Shape — how the upstream returns each Data Point:
sample(point-in-time reading),interval(a value over a span),session(a user activity with start/end),daily(one row per civil date). - Scope — the OAuth scope required. Opt-in keys (
ecg,irn,nutrition,tcx, plussettings, which gates thesettings/devicesidentity commands rather than any sync type) are granted viaconnect --add-scopes …; none of them are part of the defaultconnectgrant. - Rollups — which
--rollupkinds the catalog row supports beyond raw Data Points. - Stored as — the table the row lands in (
data_pointsfor raw,rollupsfor aggregates) plus the normalized view exposed for queries andexport.
The catalog is authoritative in internal/googlehealth/catalog.go; this page is its narrative companion.
#How long does each type take to sync?
Sync cost is proportional to Data Point count. Sustained throughput measures roughly 2,000–5,000 Data Points per minute on real runs; the table plans with the conservative ~2,000/min. Densities were measured 2026-06-10 from one real account backed by a Pixel Watch 4 (continuous heart-rate sampling) — your numbers scale with what your devices record. Cursor-resumed incremental syncs cover only the gap since the last run and finish in seconds regardless of type; Rollup syncs land one row per day or window and are always trivial.
For a raw Data Point Initial Backfill, sync requests the largest safe provider page size automatically: pageSize=10000 for heart-rate, steps, and other ordinary raw Data Types, with the documented smaller pageSize=25 cap for sleep and exercise. This lowers page count and retry surface, but it cannot make continuous heart-rate history small: a high-density account still has hundreds of thousands of raw Data Points over weeks. The raw debugging command remains explicit — it sends no page size unless you pass gohealthcli raw --page-size.
Points-per-day spans orders of magnitude because a Data Point is the upstream record unit, and that unit varies by shape: a sample point is a single reading (the watch emits a heart-rate sample every ~3 seconds), an interval point is one time-bucket (a steps point covers about a minute of walking), a session point is one whole activity (a sleep point is an entire night, stage breakdown included), and a daily point is one row per civil date.
Each sync key links to its full description further down this page.
| Sync key | Shape | Points/day | Two weeks ≈ | Sync time ≈ |
|---|---|---|---|---|
heart-rate | sample | ~27,500 | ~385,000 pts | 1.5–3 h |
time-in-heart-rate-zone | interval | ~960 | ~13,400 pts | ~5 min |
active-energy-burned | interval | ~630 | ~8,800 pts | ~4 min |
activity-level | interval | ~540 | ~7,600 pts | ~4 min |
oxygen-saturation | sample | ~480 | ~6,700 pts | ~3 min |
active-minutes | interval | ~280 | ~3,900 pts | ~2 min |
steps | interval | ~260 | ~3,600 pts | ~2 min |
distance | interval | ~210 | ~2,900 pts | ~1–2 min |
heart-rate-variability | sample | ~60 | ~800 pts | under a minute |
sedentary-period | interval | ~14 | ~190 pts | seconds |
active-zone-minutes | interval | ~13 | ~180 pts | seconds |
altitude | interval | ~8 | ~120 pts | seconds |
swim-lengths-data | interval | ~7 | ~100 pts | seconds |
exercise | session | ~2 | ~30 pts | seconds |
sleep | session | ~1 | ~18 pts | seconds |
vo2-max, run-vo2-max, respiratory-rate-sleep-summary, the daily-* types | sample / daily | ~1 | ~14 pts | seconds |
Not yet measured on this account: floors, calories-in-heart-rate-zone, electrocardiogram, irregular-rhythm-notification, weight, body-fat, height, blood-glucose, core-body-temperature, and hydration-log. Most of these are sparse user-logged or per-event records (weigh-ins, ECG sessions, hydration entries) and should sync in seconds; a continuously-recording source — a CGM feeding blood-glucose, for instance — raises density and cost accordingly.
A run longer than an OAuth access token's ~1-hour lifetime survives it: the token is refreshed mid-run on the first 401 and the failed page retried, so even a two-week heart-rate backfill can run as a single --from/--to window in the standard init --oauth-client-file setup. The sync reference has the full timing prose, and sync --status watches a long run live from a second terminal.
#Activity and fitness
#Steps
- Sync key:
steps - Shape: interval
- Scope:
activity_and_fitness.readonly - Rollups:
daily,hourly,weekly,window=<duration>(1h / 1d / 7d granularities) - Stored as:
data_points(raw) orrollups(aggregated); normalized viewdaily-steps
Step counts over a time interval (typically one minute or one stride bucket from the device). The default Data Type when sync is run with no --types flag.
#Distance
- Sync key:
distance - Shape: interval
- Scope:
activity_and_fitness.readonly - Stored as:
data_points
Distance travelled over a time interval, in metres. Reconcile-capable, so --source-family wearable works.
#Floors
- Sync key:
floors - Shape: interval
- Scope:
activity_and_fitness.readonly - Rollups:
daily,hourly,weekly,window=<duration> - Stored as:
data_points(raw) orrollups(aggregated); normalized viewfloors-intervals
Floors climbed over an interval. Not yet a default sync type — opt in via --types floors until the upstream filter shape is confirmed across multiple weeks of real data. Google does not expose floors through dataPoints.list; use a Rollup mode or --source-family wearable so gohealthcli uses a supported provider endpoint.
#Altitude
- Sync key:
altitude - Shape: interval
- Scope:
activity_and_fitness.readonly - Stored as:
data_points; normalized exportaltitude-intervals
Altitude readings over an interval.
#Active energy burned
- Sync key:
active-energy-burned - Shape: interval
- Scope:
activity_and_fitness.readonly - Stored as:
data_points
Active-only energy expenditure (kilocalories) over an interval, excluding basal metabolism.
#Active minutes
- Sync key:
active-minutes - Shape: interval
- Scope:
activity_and_fitness.readonly - Stored as:
data_points; normalized exportactive-minutes-intervals
Minutes the user spent above the device's active threshold over each interval.
#Active zone minutes
- Sync key:
active-zone-minutes - Shape: interval
- Scope:
activity_and_fitness.readonly - Stored as:
data_points; normalized exportactive-zone-minutes-intervals
Minutes spent in any active heart-rate zone over an interval. Counts moderate-zone minutes once and vigorous-zone minutes twice, per the Fitbit convention.
#Activity level
- Sync key:
activity-level - Shape: interval
- Scope:
activity_and_fitness.readonly - Stored as:
data_points; normalized exportactivity-level-intervals
Per-interval activity classification (sedentary / lightly active / fairly active / very active).
#Sedentary period
- Sync key:
sedentary-period - Shape: interval
- Scope:
activity_and_fitness.readonly - Stored as:
data_points; normalized exportsedentary-period-intervals
Continuous spans of low movement the provider flagged as sedentary.
#Calories in heart-rate zone
- Sync key:
calories-in-heart-rate-zone - Shape: interval
- Scope:
activity_and_fitness.readonly - Status: catalog-known, but raw Data Point sync is not implemented because Google exposes this type only through Rollup operations.
Calories burned while in each heart-rate zone over an interval. The catalog row is kept for future debugging until gohealthcli pins and parses the Rollup payload shape.
#Time in heart-rate zone
- Sync key:
time-in-heart-rate-zone - Shape: interval
- Scope:
activity_and_fitness.readonly - Stored as:
data_points; normalized exporttime-in-heart-rate-zone-intervals
Minutes spent in each heart-rate zone (out-of-zone / fat-burn / cardio / peak) over an interval.
#VO2 max
- Sync key:
vo2-max - Shape: sample
- Scope:
activity_and_fitness.readonly - Stored as:
data_points; normalized exportvo2-max-samples
Estimated maximum oxygen uptake (mL/kg/min) at a point in time, derived from heart-rate response to exertion.
#Run VO2 max
- Sync key:
run-vo2-max - Shape: sample
- Scope:
activity_and_fitness.readonly - Stored as:
data_points; normalized exportrun-vo2-max-samples
Run-specific VO2 max estimate, fitted from outdoor run heart-rate and pace data.
#Daily VO2 max
- Sync key:
daily-vo2-max - Shape: daily
- Scope:
activity_and_fitness.readonly - Stored as:
data_points; normalized exportdaily-vo2-max
One VO2 max value per civil date, the provider's daily summary derivation.
#Swim lengths data
- Sync key:
swim-lengths-data - Shape: interval
- Scope:
activity_and_fitness.readonly - Stored as:
data_points; normalized exportswim-lengths-data-intervals
Per-length swim metrics (stroke type, length, duration) captured by waterproof wearables.
#Total calories
- Sync key:
total-calories - Status: catalog-reserved; raw sync is not supported because Google exposes total-calories only as Rollup data.
#Heart rate Data Types
#Heart rate
- Sync key:
heart-rate - Shape: sample
- Scope:
health_metrics_and_measurements.readonly - Rollups:
daily,hourly,weekly,window=<duration>(1h / 1d / 7d granularities) - Stored as:
data_pointsorrollups; normalized viewheart-rate-samples
Beats-per-minute readings at a point in time. The high-volume Data Type for any wearable, typically arriving at one sample per few seconds to minutes. Daily heart-rate Rollups are summary-history records in the rollups table and do not replace or imply a backfill of raw heart-rate samples.
Choose the Initial Backfill path by the question you need to answer:
- Exact sample history:
gohealthcli sync --types heart-rate --from 2026-01-01 --to 2026-01-15 --plain - Fast daily summary history:
gohealthcli sync --types heart-rate --rollup daily --from 2026-01-01 --to 2026-03-01 --plain - Fast hourly summary history:
gohealthcli sync --types heart-rate --rollup hourly --from 2026-01-01 --to 2026-01-15 --plain
Raw heart-rate Data Points and heart-rate Rollups write separate archive records and carry separate Sync Cursors. Running the daily Rollup path first is a good way to get trend history quickly, but a later raw sample sync still needs its own raw Initial Backfill.
#Heart-rate variability
- Sync key:
heart-rate-variability - Shape: sample
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points
Beat-to-beat variability measurements (HRV — typically RMSSD in milliseconds), a recovery and autonomic-balance proxy.
#Daily resting heart rate
- Sync key:
daily-resting-heart-rate - Shape: daily
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points; normalized viewresting-heart-rate-by-day
One resting-heart-rate estimate per civil date.
#Daily heart-rate variability
- Sync key:
daily-heart-rate-variability - Shape: daily
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points
One HRV summary per civil date.
#Daily heart-rate zones
- Sync key:
daily-heart-rate-zones - Shape: daily
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points; normalized exportdaily-heart-rate-zones
Per-day minutes spent in each heart-rate zone, the canonical daily rollup of zone-resident time.
#Heart rhythm (Tier 2)
These are gated behind opt-in scopes the user grants via gohealthcli connect --add-scopes ecg,irn. They are list-only session shapes.
#Electrocardiogram
- Sync key:
electrocardiogram - Shape: session
- Scope:
electrocardiogram.readonly(Tier 2, opt-in viaconnect --add-scopes ecg) - Stored as:
data_points; normalized exportelectrocardiogram-sessions
A user-triggered ECG measurement: start/end, classification (e.g. sinus rhythm, AFib), and the underlying samples preserved in raw JSON.
#Irregular rhythm notification
- Sync key:
irregular-rhythm-notification - Shape: session
- Scope:
irn.readonly(Tier 2 opt-in) - Stored as:
data_points; normalized exportirregular-rhythm-notifications
A provider-issued alert that the wearable detected an irregular rhythm over a span. The companion current-irn-profile view tracks per-Connection IRN onboarding state.
#Sleep and respiration
#Sleep
- Sync key:
sleep - Shape: session
- Scope:
sleep.readonly - Stored as:
data_points; normalized viewssleep-sessionsandsleep-stages
One row per sleep session, with the per-stage (LIGHT / DEEP / REM / AWAKE) breakdown preserved inside the raw JSON. The sleep-stages view explodes that array into one row per stage so downstream queries can read stage duration without parsing JSON.
#Oxygen saturation
- Sync key:
oxygen-saturation - Shape: sample
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points
Blood-oxygen (SpO2) saturation readings as a percentage, at a point in time. Wearables typically measure these only during sleep.
#Daily oxygen saturation
- Sync key:
daily-oxygen-saturation - Shape: daily
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points
One SpO2 summary per civil date (typically min / mean / max across the sleep window).
#Daily respiratory rate
- Sync key:
daily-respiratory-rate - Shape: daily
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points
One respiratory-rate summary per civil date.
#Respiratory rate sleep summary
- Sync key:
respiratory-rate-sleep-summary - Shape: sample
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points; normalized exportrespiratory-rate-sleep-summary
Breaths-per-minute summary derived from the sleep window — emitted per sleep session, not per civil date.
#Daily sleep temperature derivations
- Sync key:
daily-sleep-temperature-derivations - Shape: daily
- Scope:
sleep.readonly - Stored as:
data_points; normalized exportdaily-sleep-temperature-derivations
One row per civil date carrying the provider's nightly skin-temperature derivations.
#Exercise Data Types
#Exercise
- Sync key:
exercise - Shape: session
- Scope:
activity_and_fitness.readonly(plusgooglehealth.location.readonlyfor TCX routes, opt-in viaconnect --add-scopes tcx) - Stored as:
data_points; normalized viewsexercise-sessionsandexercise-splits; optional TCX sidecar Attachment
One row per logged workout: exercise type, start/end, distance, calories, splits, and per-stage metadata preserved in raw JSON. When the TCX scope is granted, each session also archives the upstream TCX route XML as a content-addressed sidecar (<archive>.attachments/tcx/…); see docs/data-model.md for the attachment store.
#Body measurements
#Weight
- Sync key:
weight - Shape: sample
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points; normalized viewweight-samples
One body-weight reading at a point in time, typically from a smart scale or manual entry.
#Body fat
- Sync key:
body-fat - Shape: sample
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points; normalized exportbody-fat-samples
Body-fat percentage at a point in time.
#Height
- Sync key:
height - Shape: sample
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points; normalized exportsheight-samplesandcurrent-height
Height readings; one current-height row tracks the latest value per Connection.
#Other biomarkers
#Blood glucose
- Sync key:
blood-glucose - Shape: sample
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points; normalized exportblood-glucose-samples
Glucose readings at a point in time, from a CGM or manual entry.
#Core body temperature
- Sync key:
core-body-temperature - Shape: sample
- Scope:
health_metrics_and_measurements.readonly - Stored as:
data_points; normalized exportcore-body-temperature-samples
Core-temperature readings at a point in time.
#Hydration
#Hydration log
- Sync key:
hydration-log - Shape: session
- Scope:
nutrition.readonly(opt-in viaconnect --add-scopes nutrition— never part of the defaultconnectgrant) - Stored as:
data_points; normalized viewhydration-log-sessions
A user-logged hydration entry: volume over a civil window. The only Data Type under the nutrition scope today.
#Identity, device, and settings snapshots
These ride alongside the Data Point catalog and capture per-Connection metadata. They land in the identity_snapshots table and are exposed through normalized views.
- Profile —
gohealthcli profile. Provider account profile (membership date, age, stride lengths). Stored askind='profile'; queryable via thesearchable-textview. - Settings —
gohealthcli settings. Measurement system, timezone, stride length type. Exposed via thecurrent-settingsview. - Paired devices —
gohealthcli devices. One row per paired wearable with resource name, device type, device version, and battery status/level. Exposed via thepaired-devicesview. - IRN profile —
gohealthcli irn-profile(Tier 2irn.readonlyopt-in). Tracks onboarding and enrollment state for irregular-rhythm notifications. Exposed via thecurrent-irn-profileview.
#Rollups
steps, heart-rate, and floors support --rollup daily, which calls the upstream dailyRollUp endpoint and writes to the rollups table instead of data_points — the catalog rejects --rollup daily for every other Data Type. The same three catalog rows support the windowed rollUp endpoint (--rollup hourly, --rollup weekly, or --rollup window=<duration>) at 1h / 1d / 7d granularities. Heart-rate daily Rollups are a fast daily summary-history path, not a replacement for raw heart-rate samples. Each rollup kind carries its own Sync Cursor — syncing daily aggregates does not disturb raw or hourly cursors for the same Data Type. See sync for the full flag matrix.