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

References