What you need
- An iPhone on iOS 15 or later with Apple Health. Apple Watch data comes along too.
- The HealthAPI app. Free for 2 days, no card needed. Then an optional 7-day free trial when you subscribe. The preview includes 25 requests a day. When the 2 days are up, AI apps and the API stop reading your data until you choose a plan in the app. Your synced data is kept for about 5 weeks, so subscribing picks up where you left off. Pro is $4.99 a month or $49.99 a year (100 requests a day); Developer is $9.99 a month or $99.99 a year (1,000 a day). Cancel anytime in the App Store.
- Excel for Windows: Microsoft 365, or Excel 2016 or later.
Which Excel works
| Excel | Works | Why |
|---|---|---|
| Excel for Windows | Yes | Power Query's web source can send your key in a header and refresh on open or on a timer. |
| Excel for Mac | No | Microsoft's list of Mac sources has no web source, so you can't build the query there, and a workbook made on Windows may not refresh on a Mac. |
| Excel for the web | No | It can't create a web query, and Microsoft documents only manual refresh there. |
Sources per version are in Microsoft's Power Query data sources table. On a Mac or in a browser, use the Google Sheets guide instead: same columns, refreshed every morning by a free script.
Set it up in 6 steps
Copy your API key
Get HealthAPI from the App Store and let it sync. Then open Connect, tap Other tools and copy your key.Open a blank query
In Excel for Windows, choose Data, then Get Data, From Other Sources, Blank Query. The Power Query Editor opens.Paste the query
Select Advanced Editor on the Home tab, replace everything in it with the query below, put your key between the quotes on theKeyline and select Done.Connect as Anonymous
If Power Query asks how to connect to healthapi.app, choose Anonymous and select Connect. Your key already travels in the header the query sends. A preview of your days appears.Load it
Give the query a name such as Health, then select Close & Load. The days land in a table on a new sheet.Make it refresh itself
Choose Data, then Queries & Connections. Right-click the query, choose Properties, and on the Usage tab tick Refresh data when opening the file. Tick Refresh every too if you keep the workbook open all day.
The query
It asks the weekly summary for this week and the 3 before it, takes the line for each day, and leaves out days that haven't happened yet. Paste all of it into the Advanced Editor.
let
// Your HealthAPI key, from the app: Connect, then Other tools.
// It is saved inside this workbook, so keep the file to yourself.
Key = "PASTE_YOUR_KEY_HERE",
// Weeks to show, this one included. Each refresh uses one request per week.
Weeks = 4,
Api = "https://healthapi.app/api/v1",
Today = Date.From(DateTime.LocalNow()),
// The ISO week a date falls in, such as "2026-W43".
IsoWeek = (d as date) as text =>
let
Thursday = Date.AddDays(d, 3 - Date.DayOfWeek(d, Day.Monday)),
WeekNo = Number.IntegerDivide(Date.DayOfYear(Thursday) - 1, 7) + 1
in
Text.From(Date.Year(Thursday)) & "-W" & Text.PadStart(Text.From(WeekNo), 2, "0"),
// One week from HealthAPI: a record per day.
GetWeek = (week as text) as list =>
let
Response = Web.Contents(Api, [
RelativePath = "health/weekly-summary",
Query = [week = week],
Headers = [Authorization = "Bearer " & Key],
ManualStatusHandling = {400, 402, 429}
]),
Status = Value.Metadata(Response)[Response.Status],
Body = Json.Document(Response)
in
if Status = 200 then Body[daily_breakdown]
else error ("HealthAPI " & Text.From(Status) & ": " & Text.From(Body[detail]? ?? "")),
Days = List.Combine(List.Transform({0 .. Weeks - 1}, each GetWeek(IsoWeek(Date.AddDays(Today, -7 * _))))),
Rows = Table.FromRecords(List.Transform(Days, each [
Date = Date.FromText([date]),
Steps = [totals][steps],
#"Sleep (h)" = [sleep_hours],
#"Resting heart rate (bpm)" = [averages][resting_heart_rate_bpm],
#"HRV (ms)" = [averages][hrv_ms],
#"Active energy (kcal)" = [totals][active_energy_kcal],
#"Exercise (min)" = [totals][exercise_time_min],
Workouts = [workout_count],
#"Workout time (min)" = [workout_duration_minutes]
])),
UpToToday = Table.SelectRows(Rows, each [Date] <= Today),
Sorted = Table.Sort(UpToToday, {{"Date", Order.Ascending}}),
Typed = Table.TransformColumnTypes(Sorted, {
{"Date", type date}, {"Steps", Int64.Type}, {"Sleep (h)", type number},
{"Resting heart rate (bpm)", type number}, {"HRV (ms)", type number},
{"Active energy (kcal)", type number}, {"Exercise (min)", type number},
{"Workouts", Int64.Type}, {"Workout time (min)", type number}
})
in
TypedYour key goes in the Authorization header, the same way as in every API example. Weeks sets how far back the table goes.
The columns
| Column | From the API | Notes |
|---|---|---|
| Steps | totals.steps | iPhone and Apple Watch counted once, so it matches the Health app. |
| Sleep (h) | sleep_hours | Time asleep in the night that ended that morning. |
| Resting heart rate | averages.resting_heart_rate_bpm | Blank on days with no reading. |
| HRV (ms) | averages.hrv_ms | SDNN in milliseconds, as Apple Watch records it. |
| Active energy, exercise | totals.active_energy_kcal, totals.exercise_time_min | Kilocalories and Exercise ring minutes. |
| Workouts, workout time | workout_count, workout_duration_minutes | Workouts that started that day. |
Today's row is the day so far. Days are in your HealthAPI account's timezone, the one your iPhone uses. The weekly summary has more per day, such as totals.distance_m and averages.spo2_pct; add a line to the Rows step to bring one in. Every field is in the API reference.
Refresh and requests
Both settings are on the Usage tab of the query's properties, as Microsoft describes in Refresh an external data connection. Refresh data when opening the file brings the table up to date each time you open it. Refresh every repeats it on a timer, only while the workbook is open in Excel.
Each refresh uses 4 requests. Refreshing every 60 minutes through a 10-hour day uses about 40, well inside Pro's 100. On the free preview's 25 a day, refresh on open only. HealthAPI has no push, so a refresh is how new data arrives: open the HealthAPI app first if you want last night's sleep in the table right away.
If something's wrong
| You see | What to do |
|---|---|
| Power Query asks for credentials again | HealthAPI turned the key down. Copy it again from the app and paste it on the Key line. Keep the credential as Anonymous (Data, Get Data, Data Source Settings). |
HealthAPI 402 | Your free preview has ended or your plan has lapsed. Choose a plan in the HealthAPI app; your data is still there. |
HealthAPI 429 | Today's requests are used up. They reset at 00:00 UTC. |
| Zeros on the last row | Your iPhone hasn't synced since then. Open HealthAPI on your iPhone, then refresh. |
Questions
Can I export Apple Health straight to Excel?
Not as a spreadsheet. The Health app's export is a zip with one large XML file of every reading, which Excel isn't built to open at that size. With HealthAPI syncing your iPhone, the query on this page loads a tidy table with one row per day instead.
Does it work in Excel for Mac or Excel for the web?
Not as far as Microsoft documents. Neither lists Web among its Power Query sources, and Excel for the web describes only manual refresh. On a Mac or in a browser, the Google Sheets guide gets you the same table and refreshes on its own.
How many requests does it use?
One per week shown on each refresh, so 4 with the query as written. Pro allows 100 a day and the free preview 25, reset at 00:00 UTC. Editing the query also fetches again for the preview.
Is my key safe in the workbook?
It is saved in the query, and anyone who has the file can open the Advanced Editor and read it. Keep the workbook to yourself. Before you share a copy, delete the query or replace the key. If a copy got out, regenerate the key in the HealthAPI app.
Can I go back further than four weeks?
Yes. Change Weeks to the number you want, at one request per week on every refresh. For a long history, turn off Refresh every so the file only refreshes when you open it.
