Explainer
A Garmin export is 1,258 files. Fifty-four of them are your workouts.
I unpacked my own Garmin account export and read every file. Where each number lives, why the .fit filenames are useless, and the ten lines that fix that.
Every time Garmin Connect refuses to show you something — the decimal on your VO2 max, what your race prediction said three weeks ago, which factor dragged today’s training readiness down — the number usually exists. Garmin computed it, stored it, and decided not to print it.
The account export is where it lives. It is also badly documented, and most of the guides describing it are copying each other rather than opening one. So I opened mine and read all 73 entries.
Three ways out, only one of them complete
| Route | What you get | Good for |
|---|---|---|
| Single activity → gear icon → Export Original | One .fit file — every recorded data point | One run you want to analyse or re-upload |
| Single activity → gear icon → TCX / GPX / KML | Track plus some or no sensor data | Moving a route somewhere else |
| Activities list → export control | A CSV, one row per activity | A quick spreadsheet of distances and times |
| Account Settings → Export Your Data | The whole archive described below | Anything Connect will not show you |
The account export is not instant — mine arrived by email as a download link, not while I waited. Request it before you need it.
What actually arrives
A 6.2 MB zip that unpacks to 7.9 MB. Seventy-three entries: 40 files and 33 empty folders.
The empty ones are AVIATIONCLOUD, NAVIONICS, DI_TACX, INREACH, DI_BASEBALL_IMPACT,
FLY_GARMIN and twenty-seven more. Garmin ships every customer the shape of its entire product
catalogue and fills in the two or three folders that apply. It looks alarming and means nothing.
Everything that matters to a runner sits under DI_CONNECT/.
The map
This is the part no guide gives you: which file holds which number. Row counts are from my own export — two months, one watch.
| File under DI_CONNECT/ | What is in it | Rows in minemeasured |
|---|---|---|
| DI-Connect-Metrics/RunRacePredictions_*.json | Daily 5K, 10K, half and marathon predictions, in seconds | 187 |
| DI-Connect-Metrics/TrainingReadinessDTO_*.json | Readiness score plus each factor's own percentage | 178 |
| DI-Connect-Metrics/MetricsAcuteTrainingLoad_*.json | Acute and chronic load per day, plus ACWR status | 131 |
| DI-Connect-Metrics/TrainingHistory_*.json | Daily training status and the phrase behind it | 131 |
| DI-Connect-Metrics/MetricsMaxMetData_*.json | VO2 max — rounded, and the raw value it came from | 26 |
| DI-Connect-Fitness/*_summarizedActivities.json | Every activity, 97 fields each | 54 |
| DI-Connect-Aggregator/UDSFile_*.json | Daily rollup: body battery, stress, resting HR, steps | 68 |
| DI-Connect-Wellness/*_sleepData.json | Nightly sleep stages and scores | 69 |
| DI-Connect-Wellness/*_fitnessAgeData.json | Fitness age and the inputs behind it | 63 |
| DI-Connect-Wellness/*_AbnormalHrEvents.json | Every high-heart-rate alert, with its threshold | 49 |
| DI-Connect-Wellness/*_healthStatusData.json | Overnight metric outliers | 42 |
| DI-Connect-Wellness/*_bioMetrics_latest.json | Lactate threshold heart rate and pace, FTP | 1 |
| DI-Connect-Uploaded-Files/UploadedFiles_0-_Part1.zip | A nested zip of .fit files — see below | 1258 |
Two things to know about the naming. The date range in a file name is the range that file covers, so
you can tell at a glance how far back each metric goes. And DI-Connect-Fitness files carry your
email address in the name — including in the 1,258 file names inside the nested zip.
The .fit archive is a trap
UploadedFiles_0-_Part1.zip unpacks to 1,258 .fit files and 13.3 MB. I had recorded 54
activities in that period. So what are the other 1,204?
Every FIT file states its own type in its first record. Sorted by that:
| Type in the file | Filesmeasured | What it is |
|---|---|---|
| activity | 54 | An actual workout |
| monitoring_b | 712 | All-day step, heart rate and intensity monitoring |
| 44 | 316 | Unnamed in the public FIT profile |
| 49 | 74 | Unnamed in the public FIT profile |
| 68 | 47 | Unnamed in the public FIT profile |
| 79 | 42 | Unnamed in the public FIT profile |
| 41 | 13 | Unnamed in the public FIT profile |
The median file is 1.5 kB. Ninety-six per cent of the files are your watch quietly filing housekeeping records.
Now the part that costs people an afternoon. The file names look like this:
your.email@example.com_471118721667.fit
No date. No sport. No distance. And that number is not the activity ID. Activity IDs in
summarizedActivities.json are eleven digits (24063976426); the file names carry a twelve-digit
upload ID. I checked all 1,258 against all 54: not one matched. Guides that tell you to pair
them up by ID are describing something that does not work.
The only reliable way in is to open each file and ask it what it is:
import fitdecode, glob, os, shutil
os.makedirs("workouts", exist_ok=True)
for path in glob.glob("*.fit"):
with fitdecode.FitReader(path) as fit:
for frame in fit:
if frame.frame_type == fitdecode.FIT_FRAME_DATA and frame.name == "file_id":
head = {f.name: f.value for f in frame.fields}
break
if head.get("type") != "activity":
continue
stamp = head["time_created"].strftime("%Y-%m-%d_%H%M")
shutil.copy(path, f"workouts/{stamp}.fit")
pip install fitdecode, run it inside the unpacked folder, and you get 54 files named by the moment
the watch started recording. From there the sport, the distance and everything else is one lookup
away in summarizedActivities.json, matched on the start time.
What is in here that Connect will not show you
Four things I have actually used:
- Training readiness, taken apart. The watch shows one score.
TrainingReadinessDTOshows the six inputs separately — sleep score, sleep history, recovery time, HRV, stress history and acute load — each with its own percentage, every day. That turns “why is it 42 today” from a guess into a lookup. - VO2 max with decimals. The rounded number is in the same file as the raw value it came from, and the conversion is exact. I wrote that one up separately: Garmin says your VO2 max has no decimals.
- Every race prediction you have ever had. Connect shows today’s.
RunRacePredictionshas all of them, one row per day, which is what let me plot what a new watch’s predictor does in its first two months. - Per-activity fields Connect hides. Each of the 97 fields per activity includes time in each of the seven heart rate zones, time in each of the six power zones, ground contact time, vertical ratio, grade-adjusted speed, normalised power, and your own RPE and feel entries. That is where the numbers behind my running power analysis came from.
What was empty in mine, and why that proves nothing
gear.json and trainingPlan.json both contained an empty list. I own no registered gear and
follow no Garmin plan, so I cannot tell you whether Garmin exports those properly or not — only
that an empty file is not automatically Garmin withholding something. The same caution applies to
anything you expect and do not find: check whether you ever created it before concluding it was
left out.
Before you upload this anywhere
There is a whole genre of posts telling you to drop this zip into a chatbot and ask it about your training. Look at what you would be handing over first.
customer_data/customer.json holds your full name, date of birth and email address.
DI_CONNECT/DI-Connect-User/user_contact.json holds your phone number. Neither has anything to do
with running.
Every training number worth analysing is in DI-Connect-Metrics/, DI-Connect-Fitness/ and
DI-Connect-Wellness/. Send those folders and leave the rest on your own disk. It costs you one
drag and a delete.
Sources
- Garmin Connect web, Account Settings → Export Your Data — the request route
- My own Garmin account export, requested 23 August 2026, covering 17 June to 23 August 2026:
40 files, 33 empty folders, 1,258
.fitfiles, 54 activities - The FIT file type of all 1,258 files read with fitdecode 0.11.0