A read-first, script-friendly Go CLI for health data synchronized by Fitbit devices and Google Pixel Watches through the new Google Health API v4.
This is a CLI-only project. Its Go packages are internal implementation details;
the supported interface is the fitbitctl command.
Important
Google will turn down the legacy Fitbit Web API in September 2026. This project targets Google Health API v4 and Google OAuth 2.0 directly. Old Fitbit access and refresh tokens do not work with the new API; users must consent again.
- Google OAuth 2.0 authorization-code flow with PKCE, state, and automatic refresh
- Browser/loopback login for humans and a two-step headless flow for agents
- Private JSON token storage with atomic replacement
- Lossless access to every documented data-point union type
- Raw and reconciled streams, physical and civil-day rollups, and exercise TCX
- Paired Pixel Watch/Fitbit device, identity, profile, and settings inspection
- Retries for rate limits and transient server failures
- Stable JSON, top-level field projection, clean stdout, and actionable errors
- No interactive prompts or browser launches with
--no-inputor--json
Install from source with Go 1.24 or newer:
go install github.com/mathisarends/fitbitctl/cmd/fitbitctl@latestOr build from a checkout:
make build
./bin/fitbitctl --helpOn Windows without make:
go build -o bin/fitbitctl.exe ./cmd/fitbitctl- Create or select a Google Cloud project.
- Enable Google Health API.
- Configure an external OAuth consent screen and add your Google account as a test user while the app is in testing.
- Add only the Google Health read scopes you intend to use.
fitbitctlrequests all nine current read scopes by default;--scopescan narrow this. - Create a Web application OAuth client and register this exact authorized redirect URI:
http://localhost:8089/callback
Localhost HTTP redirect URIs are explicitly allowed by Google's OAuth rules.
Copy .env.example to .env and add the OAuth client credentials:
FITBITCTL_CLIENT_ID=your-client-id.apps.googleusercontent.com
FITBITCTL_CLIENT_SECRET=your-client-secret
FITBITCTL_REDIRECT_URI=http://localhost:8089/callbackSee Google's current Health API setup guide and scope list. All Google Health scopes are restricted. Public apps require OAuth verification and a security review; testing-mode limits and refresh-token expiration still apply.
For a guided terminal flow:
fitbitctl auth login --open
fitbitctl auth statusFor a headless or agent flow, the first command creates a short-lived PKCE session. Pass the resulting callback URL or authorization code to the second:
fitbitctl --json auth url
fitbitctl --json --no-input auth login --code 'CALLBACK_URL_OR_CODE'
fitbitctl --json auth statusRequest a smaller scope set when full access is unnecessary:
fitbitctl auth login --open --scopes activity,health,sleep,settingsTokens are hidden by auth status. Revealing them requires the explicit
--show-secrets flag. The default token path is fitbitctl/token.json below the
operating system's user config directory.
Discover the current data catalog first:
fitbitctl data types
fitbitctl --json --fields=id,kind,operations data typesThen query raw or reconciled data. Date-only values use the API's documented daily/civil semantics and UTC midnight for physical filters; RFC3339 timestamps preserve precise ranges.
fitbitctl data list steps --start 2026-07-01 --end 2026-07-08
fitbitctl data list heart-rate --start 2026-07-01T08:00:00+02:00 --end 2026-07-01T10:00:00+02:00 --all-pages
fitbitctl data reconcile sleep --start 2026-07-01 --end 2026-07-08 --data-source-family google-wearables
fitbitctl data list daily-resting-heart-rate --start 2026-07-01 --end 2026-08-01
fitbitctl data list oxygen-saturation --start 2026-07-01 --end 2026-07-08Aggregate high-volume streams and restrict aggregation to Google wearable data:
fitbitctl data rollup steps --start 2026-07-01T00:00:00Z --end 2026-07-08T00:00:00Z --window 1h
fitbitctl data daily-rollup heart-rate --start 2026-07-01 --end 2026-07-08 --data-source-family google-wearablesInspect account/device metadata and export a recorded workout:
fitbitctl identity
fitbitctl profile
fitbitctl settings
fitbitctl device list
fitbitctl exercise export-tcx EXERCISE_ID --output run.tcxThe API supports all historical data recorded for the account, but each request
is limited to a 14- or 90-day range depending on type and remains subject to rate
limits. --all-pages follows page tokens; it does not split an oversized date
range automatically.
Add --json to any JSON-producing command and --compact for NDJSON-friendly
single-line output. --fields selects top-level response fields and implies JSON:
fitbitctl --json --compact data list steps --start 2026-07-01 --end 2026-07-02
fitbitctl --fields=healthUserId,legacyUserId identity
fitbitctl --fields=id,operations --compact data types- stdout contains command data only.
- Human hints, login URLs, diagnostics, and errors go to stderr.
- JSON errors use
{ "error": { "code", "message", "hint", "status" } }. --fieldsrejects misspellings and reports valid choices.--no-inputguarantees that the command will not prompt or open a browser.--jsonalso disables interactive login so automation never hangs.
See docs/cli.md for the full command, filtering, output, and exit-code contract.
Pixel Watches do not send data directly to this CLI. They sync to the Google Health/Fitbit mobile app first; the API exposes data after that sync. Available types depend on the watch model, enabled features, region, permissions, and any required subscription. Medical-adjacent types such as ECG and irregular rhythm notifications may be absent even when their scopes were granted.
Health data and OAuth refresh tokens are sensitive. Do not commit .env, token
files, exported JSON, or TCX tracks. Prefer the minimum required scopes and use a
dedicated Google Cloud project whose consent screen accurately describes how
data is used.
make fmt
make test
make vet
make buildThe implementation target and API decisions are recorded in SPEC.md. Release instructions are in docs/releasing.md.