CLI for Google Health API v4 — read steps, heart rate, exercise, sleep, weight, and 35+ health data types with agent-first JSON output
triggers
["get my step count from google health","fetch heart rate data from google fit","read my sleep data using google health api","export exercise data from google health","set up google health cli authentication","query my weight history from google health","retrieve daily health metrics from google fit","import health data using ghealth command"]
CLI for the Google Health API v4 built for AI agents and developers. Supports 40+ verified health data types including steps, heart rate, exercise, sleep, weight, SpO2, HRV, ECG, blood glucose, and nutrition. Outputs deterministic JSON with agent-friendly exit codes.
Installation
git clone https://github.com/Google-Health-API/google-health-cli.git
cd google-health-cli
go build -o ghealth .
Move the binary to your PATH:
sudomv ghealth /usr/local/bin/
Authentication Setup
Interactive Setup
ghealth setup
Walks through:
GCP project ID
OAuth credentials (download from Console as Desktop application)
Health API enablement
Scope selection
Browser-based OAuth login
Credentials stored in ~/.config/ghealth/ (override with GHEALTH_CONFIG_DIR):
# Recent heart rate readings
ghealth data heart-rate list --from today --limit 10
# Heart rate for specific date range
ghealth data heart-rate list --from 2026-03-22 --to 2026-03-29 --limit 100
# Oxygen saturation readings
ghealth data oxygen-saturation list --from 2026-06-01 --limit 20
# Recent weight measurements
ghealth data weight list --limit 10
Rollup Data (Aggregated)
Critical: For steps, distance, and other interval types, use daily-rollup to get actual values. list returns time intervals without totals.
# List recent exercises
ghealth data exercise list --from 2026-03-01 --limit 20
# Get specific exercise
ghealth data exercise get --id <exercise-id>
# Export exercise track as TCX
ghealth data exercise export-tcx --id <id> --output ride.tcx
# Export as CSV for data analysis
ghealth data exercise export-tcx --id <id> --output ride.csv --as csv
# CSV columns: time, activity, lap, sport, latitude_deg, longitude_deg, altitude_m, # distance_m, heart_rate_bpm, cadence_rpm, speed_mps, watts# Stream to stdout
ghealth data exercise export-tcx --id <id> --output - --as csv | head
Sleep Data
# Recent sleep sessions (summary)
ghealth data sleep list --limit 5
# Sleep with stage-by-stage breakdown
ghealth data sleep list --limit 5 --detail
# Specific sleep session
ghealth data sleep get --id <sleep-id>
Pagination
All list commands support pagination:
# First page (default limit 500)
ghealth data heart-rate list --from 2026-06-15 --limit 500
# Returns: {"dataPoints": [...], "nextPageToken": "ABC"}# Next page
ghealth data heart-rate list --from 2026-06-15 --limit 500 --page-token ABC
Writing Data
Create Exercise
ghealth data exercise create \
--start-time 2026-03-28T14:00:00Z \
--end-time 2026-03-28T15:30:00Z \
--type running \
--calories 450 \
--distance-meters 8000 \
--avg-heart-rate 152 \
--notes "Morning run in the park"
Update Exercise
ghealth data exercise update \
--id <exercise-id> \
--notes "Evening run - felt great" \
--calories 475
# Extract step counts
ghealth data steps daily-rollup --from 2026-03-22 --to 2026-03-29 | jq -r '.dataPoints[].countSum'# Get total steps for week
ghealth data steps daily-rollup --from 2026-03-22 --to 2026-03-29 | jq '[.dataPoints[].countSum | tonumber] | add'# Average heart rate
ghealth data heart-rate list --from today --limit 100 | jq '[.dataPoints[].beatsPerMinute | tonumber] | add / length'
Dry Run Mode
# Preview API request without executing
ghealth data steps daily-rollup --from today --dry-run
Raw API Response
# Get unprocessed API response
ghealth data heart-rate list --from today --limit 10 --raw
Exit Codes
0 — Success
1 — General error
2 — Authentication/validation error
3 — API error
4 — Data not found
5 — Configuration error
Critical Data Handling Rules
Missing Days vs. Zero Values
For presence-aware types (altitude, distance, floors, steps, total-calories):
Missing date → device not worn/synced — render as "no data", NOT zero
countSum: "0" → true zero (device worn, no activity)
# Example output:# {"dataPoints": [# {"date": "2026-03-28", "countSum": "9037"}, # Active day# {"date": "2026-03-27", "countSum": "0"}, # Worn but sedentary# # 2026-03-26 missing entirely # Not worn - DON'T treat as 0# ]}
Never average over absent days as if they were zeros — this silently deflates statistics.
Time Formats
Use ISO 8601: 2026-03-28T14:00:00Z
Shortcut: today, yesterday
Date-only: 2026-03-28 (interprets as start of day in user's timezone)
ghealth data steps daily-rollup --from 2026-03-22 --to today
ghealth data heart-rate list --from yesterday --limit 50
Common Workflows
Weekly Step Summary
#!/bin/bash# Get last 7 days of step data
WEEK_AGO=$(date -d '7 days ago' +%Y-%m-%d)
TODAY=$(date +%Y-%m-%d)
ghealth data steps daily-rollup --from $WEEK_AGO --to $TODAY | \
jq -r '.dataPoints[] | "\(.date): \(.countSum) steps"'
Export All Exercise Data
#!/bin/bash# Export all exercises from a month
ghealth data exercise list --from 2026-03-01 --to 2026-03-31 | \
jq -r '.dataPoints[].id' | \
whilereadid; do
ghealth data exercise export-tcx --id"$id" --output "exercise_${id}.csv" --as csv
done
Daily Health Dashboard
#!/bin/bash# Fetch today's key metricsecho"=== Health Metrics for $(date +%Y-%m-%d) ==="echo -n "Steps: "
ghealth data steps daily-rollup --from today --to today | jq -r '.dataPoints[0].countSum // "no data"'echo -n "Average Heart Rate: "
ghealth data heart-rate list --from today --limit 500 | \
jq '[.dataPoints[].beatsPerMinute | tonumber] | add / length | round'echo -n "Weight: "
ghealth data weight list --limit 1 | jq -r '.dataPoints[0].weightGrams // "no data"'
Data Reconciliation
Check for data conflicts and resolve:
# Reconcile heart rate data
ghealth data heart-rate reconcile --from 2026-03-01 --to 2026-03-31
# Reconcile with conflict resolution strategy
ghealth data exercise reconcile --from 2026-03-01 --strategy newest
Troubleshooting
No OAuth Credentials
If you see exit code 5 with JSON error:
{"error":{"type":"config","code":5,"message":"No OAuth client_secret.json configured","next_steps":["Open https://console.cloud.google.com/apis/credentials","Create or select a Google Cloud project","Enable the Google Health API","Create OAuth client ID with Application type: Desktop app","Download the client_secret JSON","Run: ghealth setup --client-secret /path/to/client_secret.json"]}}
Get instructions without error:
ghealth setup --instructions
Token Refresh Issues
# Check auth status
ghealth auth status
# Force refresh
ghealth auth refresh
# Re-authenticate
ghealth auth login
Empty Data Results
Check:
Date range is correct (use --from and --to)
Using correct command (daily-rollup vs list)
OAuth scopes include necessary permissions
Device has synced data
# Verify scopes
ghealth auth status | jq -r '.scopes'# Check available types
ghealth schema types
Rate Limiting
The API has rate limits. Space out requests:
# Add delays between bulk operationsforidin $(ghealth data exercise list | jq -r '.dataPoints[].id'); do
ghealth data exercise get --id"$id"sleep 1
done
Schema Exploration
# List all available data types
ghealth schema types
# Get details for specific type
ghealth schema type --name steps
# View available operations for a type
ghealth schema type --name exercise | jq -r '.operations[]'
Best Practices for Agents
Always use daily-rollup for totals — list returns intervals, not sums
Handle pagination — check for nextPageToken in responses
Respect missing data — don't coalesce absent days to zero
Use exit codes — check $? for error handling
Parse with jq — all output is structured JSON under .dataPoints
Set explicit date ranges — --from and --to prevent unbounded queries
Use --dry-run for validation before destructive operations
Store credentials securely — use environment variables for tokens