Now that I’ve got my MCP connection set-up, I want to make sure there is consistency in the metrics I get between the new way (json file export) and the old way (MCP).
I recently ran a small experiment: I took raw JSON exports of two of my Suunto workouts (a treadmill session and a trail run) and compared them, field by field, against the same workout pulled live through Suunto’s API. I expected the two to basically be the same data in different packaging. Instead, they told two different stories.
The workouts
A 53-minute hill-repeats treadmill run and a ~110-minute, 12 km trail run with about 566 m of ascent, both recorded on a Suunto 9 Peak Pro. Same workoutKey on both sides, so this is unambiguously the same session — just viewed through two different lenses.
Story one: the core numbers agree
Duration, energy burned, EPOC, training effect, heart-rate zone durations, and speed-zone thresholds all matched cleanly between the raw file and the API once I normalized units (the file stores some values in Hz and Kelvin where the API returns bpm and °C-equivalent). So at the level of “how hard was this workout,” both sources agree completely.
Story two: the two sources aren’t looking at the same data at all
This is where it got interesting. It’s not that the numbers differed — it’s that each source contains data the other simply doesn’t have.
What only the raw file has
- A raw R-R interval stream — 6,859 individual beat-to-beat heart intervals, in milliseconds. This is the actual heartbeat-by-heartbeat data you’d need to compute your own HRV metrics (RMSSD, DFA-alpha1, whatever your method of choice is).
- Barometric pressure — absolute and sea-level-adjusted, sampled every second.
- Battery telemetry — charge, current, voltage — the watch quietly logging its own health throughout the run.
- Per-second vertical speed and raw temperature.
- A “Windows” structure breaking the workout into phases with Avg/Min/Max for heart rate, speed, altitude, and cadence in each phase.
What only the API has
- Training Stress Score, computed three separate ways — HR-based, MET-based, and a Dynamic-DFA method. None of these scores exist in the raw file; they’re server-side derived analytics.
- An explicit aerobic/anaerobic time split (ZoneSense), derived from that R-R data but not present as a summary in the file itself.
- A smoothed DFA-alpha1 HRV value per record — again, computed from the raw beats, but only the derived number shows up via the API.
- A plain-English sport/sub-sport label (“running” / “treadmill”) — the file only encodes this as a bare numeric activity ID.
In short: the file is closer to the sensor, the API is closer to the coach. If you want to do your own HRV analysis, you need the file. If you want Suunto’s opinion on how trained you are, you need the API.
One quiet inconsistency worth flagging
The file uses null for fields like ascent and descent when there was no elevation change; the API returns 0 for the same fields. Same real-world meaning, different convention — the kind of thing that’s easy to miss until it silently breaks a script that checks if (ascent) instead of if (ascent !== null).
Round two: a trail run
I ran the same comparison on a second workout — a ~110-minute, 12 km trail run with about 566 m of ascent, instead of an indoor treadmill session. Outdoor, GPS-tracked, variable terrain: more sensors are active, so I expected more to compare. That held up.
The core numbers agreed just as cleanly as before — distance, duration, ascent/descent, energy, EPOC, training effect, step count, average and max heart rate, cadence, and speed all matched between the file and the API. But three new things showed up that the treadmill run hadn’t revealed.
A real unit bug, not just a formatting quirk
The API’s compact FIT summary reported ascent and descent as 0.566 m and 0.545 m — off by a factor of 1,000 from the correct 565.8 m and 544.9 m, which the file has and which the API’s own full workout-summary endpoint also gets right. This looks like a genuine kilometre-vs-metre bug in one specific API endpoint’s FIT parsing, not a difference in convention like the null-vs-0 case.
Outdoor-only metrics, in both directions
Being outdoors switched on data that simply doesn’t exist on a treadmill:
- GPS position — but only on about 23% of samples. The rest had no fix at all, almost certainly from tree cover on the trail. That’s a data-quality detail only visible in the raw file; the API just hands you a clean route with the gaps smoothed over.
- GPS confidence metrics — estimated horizontal and vertical position error, satellite count, and signal strength per sample. None of this is exposed anywhere in the API.
- Live cadence and distance samples — both were empty throughout the treadmill file; outdoors, the footpod/accelerometer stream populates them properly.
And in the other direction, the API added something new too: an actual weather record — temperature, wind speed and direction, humidity — that isn’t in the file at all. It’s almost certainly appended server-side by matching the workout’s GPS location and timestamp against a weather service after the fact.
The recovery-time and ZoneSense inconsistencies weren’t one-offs
Both quirks from the treadmill comparison showed up again here. The API’s top-level recovery time (98,720s) still disagrees with its own summary-level recovery time (78,720s, which matches the file) — the same internal split, on a completely different workout. And the aerobic/anaerobic ZoneSense split between file and API diverged by even more this time (roughly 500-plus seconds each way, versus about 20 seconds on the treadmill run) — suggesting that gap scales with how variable the effort is, rather than being random noise.
Net result: the sensor-vs-coach split holds up as a general pattern, but it’s not static — what shows up on each side depends on the activity itself, and at least one of the differences (that unit bug) is a bug worth guarding against rather than just a convention to normalize.
Why this matters
If you’re building anything on top of wearable data — a training-load dashboard, a custom HRV tracker, a coaching tool — “just use the API” and “just parse the export” are not interchangeable choices. They give you different raw material entirely. Same workout, two genuinely different datasets.
Leave a Reply