Queries Apple Health data stored in a local SQLite database. Use this skill to read heart rate, steps, SpO2, VO2 Max, sleep, workouts, resting heart rate, HR...
name: healthsync
description: Queries Apple Health data stored in a local SQLite database. Use this skill to read heart rate, steps, SpO2, VO2 Max, sleep, workouts, resting heart rate, HRV, blood pressure, active/basal energy, body metrics, mobility, running metrics, mindful sessions, wrist temperature, and more. Can query via the healthsync CLI or directly via SQLite. Read-only β never write to the database.
metadata:
author: sidv
version: "1.1"
compatibility: Requires healthsync binary. Database at ~/.healthsync/healthsync.db must be populated via healthsync parse.
healthsync β Apple Health Data Query Skill
Installing healthsync
# macOS and Linux (recommended)
curl -fsSL https://healthsync.sidv.dev/install | bash
Or via Go
go install github.com/BRO3886/healthsync@latest
After installing the binary, parse your Apple Health export:
# Export from Health app β profile picture β Export All Health Data
healthsync parse ~/Downloads/export.zip
Install this skill into your agent:
# Claude Code or Codex
healthsync skills install
OpenClaw
healthsync skills install --agent openclaw
Query Apple Health export data stored in a local SQLite database. This skill is read-only β never INSERT, UPDATE, DELETE, or DROP anything.
Important Constraints
READ ONLY β You must NEVER write to the database. No INSERT, UPDATE, DELETE, DROP, ALTER, or any write operations.
Two query methods: CLI (healthsync query) or direct SQLite (sqlite3 ~/.healthsync/healthsync.db)
Prefer CLI for simple queries. Use direct SQLite for complex aggregations, joins, or custom SQL.
sqlite3 ~/.healthsync/healthsync.db "SELECT date(start_date) as day, SUM(value) as total_steps FROM steps GROUP BY day ORDER BY day DESC LIMIT 7"
Average resting heart rate per week
sqlite3 ~/.healthsync/healthsync.db "SELECT strftime('%Y-W%W', start_date) as week, ROUND(AVG(value),1) as avg_rhr FROM resting_heart_rate GROUP BY week ORDER BY week DESC LIMIT 12"
CLI Reference
healthsync query
| Flag | Description | Default |
|------|-------------|---------|
| --from | Filter records from this date (inclusive) | β |
| --to | Filter records to this date (inclusive) | β |
| --limit | Maximum records to return | 50 |
| --format | Output format: table, json, csv | table |
| --total | Deduplicated daily totals (steps, active-energy, basal-energy only) | false |
| --db | Override database path | ~/.healthsync/healthsync.db |
Start HTTP server for receiving uploads. (Informational β do not start unless the user asks.)
Endpoints:
POST /api/upload β Upload .zip or .xml (multipart form, field: file). Returns 202, parses async.
GET /api/upload/status β Poll parse progress.
GET /api/health/{table}?from=&to=&limit= β Query data as JSON.
Database Schema
Standard quantity tables
Schema: id, source_name, start_date, end_date, value REAL, unit TEXT, created_at
Applies to all tables except blood_pressure, sleep, mindful_sessions, stand_hours, and workouts.
CREATE TABLE resting_heart_rate (
id INTEGER PRIMARY KEY AUTOINCREMENT,
source_name TEXT NOT NULL,
start_date TEXT NOT NULL,
end_date TEXT NOT NULL,
value REAL NOT NULL,
unit TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(source_name, start_date, end_date, value)
);
CREATE TABLE blood_pressure (
id INTEGER PRIMARY KEY AUTOINCREMENT,
source_name TEXT NOT NULL,
start_date TEXT NOT NULL,
end_date TEXT NOT NULL,
systolic REAL NOT NULL, -- mmHg
diastolic REAL NOT NULL, -- mmHg
unit TEXT NOT NULL, -- "mmHg"
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(source_name, start_date, end_date, systolic, diastolic)
);
Category tables β no unit column
Applies to: sleep, mindful_sessions, stand_hours
CREATE TABLE sleep (
id INTEGER PRIMARY KEY AUTOINCREMENT,
source_name TEXT NOT NULL,
start_date TEXT NOT NULL,
end_date TEXT NOT NULL,
value TEXT NOT NULL, -- e.g. HKCategoryValueSleepAnalysisAsleepCore
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(source_name, start_date, end_date, value)
);
workouts
CREATE TABLE workouts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
activity_type TEXT NOT NULL,
source_name TEXT NOT NULL,
start_date TEXT NOT NULL,
end_date TEXT NOT NULL,
duration REAL,
duration_unit TEXT,
total_distance REAL,
total_distance_unit TEXT,
total_energy_burned REAL,
total_energy_burned_unit TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(activity_type, start_date, end_date, source_name)
);
Date Format
All dates stored as text: 2024-01-15 08:30:00 +0530. Filter with date prefix β 2024-01-01 works via SQLite string comparison.
Sleep Stage Values
| Value | Meaning |
|-------|---------|
| HKCategoryValueSleepAnalysisInBed | In bed |
| HKCategoryValueSleepAnalysisAsleepCore | Core sleep |
| HKCategoryValueSleepAnalysisAsleepDeep | Deep sleep |
| HKCategoryValueSleepAnalysisAsleepREM | REM sleep |
| HKCategoryValueSleepAnalysisAwake | Awake |
| HKCategoryValueSleepAnalysisAsleepUnspecified | Unspecified |
SELECT strftime('%Y-W%W', start_date) as week,
ROUND(AVG(value), 1) as avg_rhr
FROM resting_heart_rate
GROUP BY week ORDER BY week DESC LIMIT 12;
HRV trend
SELECT date(start_date) as day, ROUND(AVG(value), 1) as hrv_ms
FROM hrv
GROUP BY day ORDER BY day DESC LIMIT 30;
Blood pressure history
SELECT date(start_date) as day,
ROUND(AVG(systolic), 1) as avg_sys,
ROUND(AVG(diastolic), 1) as avg_dia
FROM blood_pressure
GROUP BY day ORDER BY day DESC LIMIT 30;
Body weight trend
SELECT date(start_date) as day, value as kg
FROM body_mass
ORDER BY day DESC LIMIT 30;
Sleep duration per night
SELECT date(start_date) as night,
ROUND(SUM((julianday(end_date) - julianday(start_date)) * 24), 1) as hours
FROM sleep
WHERE value LIKE '%Asleep%'
GROUP BY night ORDER BY night DESC LIMIT 14;
Average heart rate per day
SELECT date(start_date) as day,
ROUND(AVG(value), 1) as avg_hr,
MIN(value) as min_hr,
MAX(value) as max_hr
FROM heart_rate
GROUP BY day ORDER BY day DESC LIMIT 30;
Workout summary
SELECT activity_type, COUNT(*) as count,
ROUND(AVG(duration), 1) as avg_min,
ROUND(SUM(total_energy_burned)) as total_kcal
FROM workouts
GROUP BY activity_type ORDER BY count DESC;
Weekly VO2 Max trend
SELECT strftime('%Y-W%W', start_date) as week,
ROUND(AVG(value), 2) as avg_vo2
FROM vo2_max
GROUP BY week ORDER BY week DESC LIMIT 12;
Mindfulness minutes per week
SELECT strftime('%Y-W%W', start_date) as week,
ROUND(SUM((julianday(end_date) - julianday(start_date)) * 1440), 0) as minutes
FROM mindful_sessions
GROUP BY week ORDER BY week DESC LIMIT 12;
Limitations
Read-only β This skill must never write to the database
No real-time data β Data is only as fresh as the last healthsync parse run
Date filtering is string-based β Timezone offsets are part of the stored date string
SpO2 values are fractions β 0.98 means 98%, not 98
Blood pressure is paired β systolic and diastolic are stored together in one row per measurement
Category tables have no unit column β sleep, mindful_sessions, stand_hours store text values, not numeric
sqlite3 ~/.healthsync/healthsync.db "SELECT date(start_date) as day, SUM(value) as total_steps FROM steps GROUP BY day ORDER BY day DESC LIMIT 7"
Average resting heart rate per week
sqlite3 ~/.healthsync/healthsync.db "SELECT strftime('%Y-W%W', start_date) as week, ROUND(AVG(value),1) as avg_rhr FROM resting_heart_rate GROUP BY week ORDER BY week DESC LIMIT 12"