Query Withings body measurements, activity, sleep, and workout data via the bundled withings CLI.
When to Use
Activate when the user asks about their Withings data:
- Body metrics (weight, BMI, body fat, blood pressure, heart rate)
- Daily activity (steps, calories, distance, active duration)
- Sleep sessions (score, stages, duration, breathing rate)
- Workout sessions (run/walk/cycle/etc.)
Tooling
Three subcommands cover most needs:
| Subcommand | Purpose |
|---|---|
withings status |
Connection state ({ok, status, connect_url?, disconnect_url?, reason?}). |
withings list-fields --category <CAT> |
Field list for a category (snake_case, units in the name). |
withings query --category <CAT> --start-date <YYYY-MM-DD> [--end-date --interval --fields] |
Typed snake_case records. Returns a JSON array. |
Categories
daily-metrics— Daily rollup of Activity (steps/calories/distance/HR) + Measures (weight/BP/body composition). Supports--interval hourly|daily|weekly(defaults todaily). Aggregation rules: sum for counters (step_count,active_energy_burned_kcal,distance_walking_running_meters), avg forhr_average_bpm, max forhr_max_bpm, last-of-bucket for body measurements (weight/BP/body fat etc.).sleep— One row per sleep session (score, stages, duration, HR, breathing).workout— One row per workout session (translatedworkout_typename, duration, distance, calories, HR).
Use list-fields to discover available fields per category.
Output
JSON to stdout. Datetimes are local YYYY-MM-DD HH:MM:SS per record's timezone. Session rows include id (prefixed withings_<id>), start_datetime, end_datetime, timezone. daily-metrics buckets include date / hour / week_start + record_count instead of id.
Examples
# Recent workouts
withings query --category workout --start-date 2026-05-01
# Weight trend (last reading per day)
withings query --category daily-metrics --start-date 2026-04-01 --fields body_mass_kg
# Weekly step totals
withings query --category daily-metrics --start-date 2026-04-01 --interval weekly --fields step_count
Auth
Before any Withings API call:
- Run
withings status. - If
statusisnot_connected, share[Connect Withings](<connect_url>)exactly — never paste the raw URL. - After the user completes the callback, re-run
withings statusand proceed only whenconnected.
For disconnect: run withings disconnect and share [Disconnect Withings](<disconnect_url>) if present.
Never print tokens or credentials.
Operating Rules
- Treat linking as one-time onboarding — don't re-prompt for auth unless calls keep failing.
- Prefer
query --category <CAT>over the legacy commands. Field names are snake_case with units in the name (body_mass_kg,step_count,hr_average_bpm,sleep_total_duration_sec) — never expose Withings's numeric meas-type IDs to the user. - If unsure of a field name, run
withings list-fields --category <CAT>BEFORE composingquery --fields. Withings-native names (calories,distance,hr_average,weight) are NOT valid — they're translated to snake_case with units (energy_burned_kcal,distance_meters,hr_average_bpm,body_mass_kg). An empty filter result usually means the field name was wrong, not that the data is missing. - Default to the last 7 days when the user asks for recent trends without specifying dates.
- For broad trends, use
--category daily-metrics --interval weeklyinstead of pulling every reading. The--intervalflag already aggregates per the rules above — do NOT apply additional aggregation (sum/avg) to the bucketed output client-side. - If
queryreturns[], say so clearly — never invent values. If the user is asking for a metric not surfaced bylist-fields, drop to the legacy escape hatch (measures --meas-types <ID>or a more specific subcommand likeheart-list; see references/commands.md for the meas-type ID table).
Legacy / Advanced Commands
Pre-query per-endpoint passthroughs that return raw {ok, status, body: <Withings native>} envelopes. Use only as an escape hatch for data not exposed by query — e.g. a meas-type missing from the translation table (withings measures --meas-types 130 for AFib ECG result), high-frequency sleep sensor data (withings sleep), or ECG signals (withings heart-list / heart-get).
Quick reference for the other passthroughs:
withings activity— raw daily Activity rows.withings sleep-summary— raw nightly sleep summaries with Withings-native field names.withings intraday— minute-resolution activity sensor data.withings devices— list paired Withings devices.
Full command matrix, meas-type IDs, and Withings → Muse field mapping: references/commands.md.
- Permission-withheld data: while the user's "Read measurements and heart
data" permission is deny or ask,
query --category daily-metricswraps its rows in{"withheld": {...}, "records": [...]}with body metrics (weight, blood pressure, SpO2, heart measurements) removed, and a request for only those fields fails withdata_class_excluded. Key off thewithheldmarker: when present, the permission is restricting those fields — say so instead of "no readings". When the user actually needs a withheld metric and the permission requires approval (withheld.reasonisrequires_approval, or a body-metrics-only query failed withdata_class_excludedsaying it requires approval), runwithings measures --category 1 --start-date <d> --end-date <d>, which is governed by that permission and shows the user the approval prompt. Sleep and workout results are never wrapped; a response without the marker is an ordinary result.