Migrating Samsung Health History to Garmin Connect
Migrating Samsung Health History to Garmin Connect
Moving years of Samsung Health data to Garmin Connect using a Python-based open source tool — with fixes for schema changes in newer Samsung Health exports.
Background
When switching from a Samsung Galaxy Watch to Garmin, the official path for historical data migration is essentially nonexistent. Samsung Health has no built-in export-to-Garmin feature, and Garmin Connect won’t pull from Samsung Health directly.
The best available option is FromSamToGarm, a set of three Python scripts that convert Samsung Health export data into formats Garmin Connect can ingest. The scripts work well, but the Samsung Health export schema has changed in newer app versions and the original scripts crash against a 2025–2026 export. This post covers the full process including the fixes required to get everything working.
What Transfers
| Data Type | Method | Notes |
|---|---|---|
| Weight / BMI | CSV import | Metric units, Garmin converts |
| Daily activity (steps, calories, floors, distance) | CSV import | Intensity minutes differ between platforms |
| Recorded exercises (runs, hikes, rides, etc.) | TCX import | GPS, heart rate, duration, calories |
| Sleep | ✗ Not supported |
Preparation
01 — Export Samsung Health data
In the Samsung Health app: tap your profile → scroll to bottom → Download personal data. The export will arrive as a zip file containing a folder of CSV files and a jsons/ subdirectory for per-activity GPS and heart rate data.
Extract the zip and note the path to the inner folder — it will be named something like samsunghealth_username_YYYYMMDDHHMMSS.
02 — Install dependencies
1pip install lxml
Python’s standard library handles everything else.
03 — Clone the repo
1git clone https://github.com/t-aubin/FromSamToGarm.git
2cd FromSamToGarm
Copy the three scripts (weight.py, activity.py, exercises.py) into the Samsung Health export folder — the same directory that contains the .csv files.
Running the Scripts
Run each script from inside the Samsung Health export directory.
Weight data
1python3 weight.py
Produces weight-export-XX.csv files in the current directory.
Activity data (steps, calories, floors)
1python3 activity.py
Produces activities-export-XX.csv files.
Exercises (runs, hikes, rides)
1python3 exercises.py
Produces individual .tcx files in an exports/ subdirectory, one per recorded activity. File names are prefixed with Samsung’s activity type code:
| Code | Activity |
|---|---|
| 1001 | Walking |
| 1002 | Running |
| 11007 | Cycling |
| 13001 | Hiking |
| 15004 | Indoor fitness |
| 15005 | Other exercise |
Importing to Garmin Connect
Navigate to Garmin Connect Web and click the Upload icon (cloud with arrow) in the top right. Choose Import Data.
Weight and activity CSVs
Drag and drop the generated CSV files into the drop zone. Garmin may detect them as “Fitbit® Data” — this is expected and imports correctly. When prompted for units, select Kilograms and Centimeters / Meters / Kilometers — the scripts output metric regardless of your device settings, and Garmin will convert to your preferred display units after import.
Exercise TCX files
Drag and drop files from the exports/ folder in batches of around 100. Garmin detects and skips duplicates automatically, so re-uploading batches that had errors is safe.
Tip: import by activity type prefix (all 1002_* runs together, then 13001_* hikes, etc.) — it makes it much easier to bulk-update activity types in Garmin Connect after the import, since the TCX format only supports three sport types: Running, Biking, and Other.
Verify the import
After all CSVs are in, go to Activities → Steps, click the Reports icon, and set the range to 1 Year. Step back through years to confirm historical data landed. Do the same check for floors and calories.
Schema Fixes for 2024+ Exports
The original scripts were written against an older Samsung Health export format. Newer exports (2024 and later) have several breaking differences. The fork linked above includes all of these fixes.
exercises.py — wrong file being read
Samsung Health exports multiple CSVs with the com.samsung.shealth.exercise. prefix:
com.samsung.shealth.exercise.20260610115151.csv ← main file
com.samsung.shealth.exercise.hr_zone.20260610115151.csv
com.samsung.shealth.exercise.extension.20260610115151.csv
com.samsung.shealth.exercise.weather.20260610115151.csv
com.samsung.shealth.exercise.periodization_training_schedule.20260610115151.csv
The original glob pattern com.samsung.shealth.exercise.*.csv matches all of them. Python’s glob returns results in filesystem order, which in practice was picking up periodization_training_schedule instead of the main file — resulting in “Found 1 exercises.”
Fix: use com.samsung.shealth.exercise.20*.csv to match only the timestamped main file.
exercises.py — column prefix changes
In newer exports, datauuid and start_time in the exercise CSV now carry the full com.samsung.health.exercise. column prefix. The original field mapping assumed these were bare column names and silently returned empty strings for every row.
exercises.py — UTF-8 BOM
The exercise CSV is encoded with a UTF-8 BOM. Opening with encoding="utf-8-sig" strips it cleanly.
exercises.py — live/location data field format
Older exports stored 0 or 1 in the live_data and location_data columns. Newer exports store the actual JSON filename (e.g. c3ede461-d87a-6a0d-db88-14587eb097cd.com.samsung.health.exercise.live_data.json). The presence check needed to detect a non-empty, non-"0" value rather than a truthy integer.
activity.py — timestamp format change
The day_time field in the activity summary CSV changed from a Unix millisecond timestamp to a human-readable datetime string (2026-01-30 00:00:00.000). The fix tries integer parsing first and falls back to string slicing.
activity.py — floors KeyError
A bug in merge_data caused a KeyError when a date appeared in the floors data but not in the calories data. The fix initializes the date entry before writing to it.
activity.py — empty floors field
Some rows have no floors value recorded. The original script wrote an empty string, which Garmin’s importer rejected. The fix defaults missing fields to 0 before writing.
weight.py — empty height field
Weight entries logged by third-party apps (MyFitnessPal, etc.) often have no height value. The fix carries the last known height forward for BMI calculation and skips rows with no weight value rather than raising a ValueError.
Notes
- Garmin’s importer throws transient errors on individual files — retrying usually works on the second or third attempt
- Some days may show negative calories in Garmin Connect after import. Creating a manual activity with 0 calories for the affected day triggers a recalculation that fixes it
- Activity types will show as Running, Biking, or Other after import (TCX format limitation) — use the activity type code in the filename to identify and correct them in bulk