Garmin-intervals-bridge – your complete Garmin FIT files and wellness data in Intervals.icu, with or without the official Garmin integration

Update 9 Oct: Garmin has changed its partner terms and David expects Intervals will have to block Garmin-sourced data in its API ([API thread](API access to Intervals.icu - #752 by david)). Files you upload yourself are most likely not affected (“likely ok” – [David](API access to Intervals.icu - #759 by david)). The bridge’s upload mode does exactly that, verified live today: official import off, every new activity’s original, unfiltered FIT uploaded as your own file, Intervals computes Stamina, Recovery Time, VO₂max, Performance Condition, Training Effect, Sweat Loss and your other custom items from the complete file itself. Nothing flagged as edited, and apps you authorize keep seeing your activities. Docs: README → “Upload mode”.



TL;DR – Since late September Garmin filters its own metrics out of the FIT files it sends to Intervals (Stamina, Recovery Time, VO₂max, Performance Condition, Training Effect, Sweat Loss …). This open-source tool fetches the original file from Garmin Connect and adds the missing data to the activity the official sync already created – no duplicates, nothing deleted, training load untouched. It also syncs the daily wellness values the official sync doesn’t deliver (night SpO₂, respiration, sleeping HR, Body Battery, HRV details, sleep stages, stress, readiness, nutrition, total daily burn, scores, race predictions, fitness age …), can backfill the past, and brings 19 ready-made charts in the chart library (search “Garmin Bridge”). Linux, container, or Windows with one command. MIT, self-hosted, your data stays with you.

pip install garmin-intervals-bridge

An officially imported run after the bridge ran – everything in this row had been stripped by Garmin:

activity-fields

How it works

  • The official Garmin → Intervals sync stays switched on. The bridge polls Intervals once a minute (one tiny request), and when a new Garmin activity appears it downloads the original FIT through Garmin Connect (byte-identical to the browser export – verified), compares it with the file Intervals received, and writes only what is missing via PUT /activity/{id} and PUT /activity/{id}/streams.
  • It fills your custom fields and streams: it reads your own custom item definitions (fit_session_field, fit_record_field, the m.f_138 stream scripts) and takes the values from the original. Nothing Garmin-specific is hard-coded, so the public community fields (Recovery Time, Stamina, VO2MaxGarmin, Sweatloss …) just work. A stream is only written after the alignment has been proven on a stream both sides already have (heart rate).

activity-stream-ga-speed

  • Wellness: 22 Garmin daily endpoints, mapped into Intervals’ native fields where they exist (spO2, respiration, sleeping HR, weight, body fat, kcal consumed, carbs/protein/fat …) and into Garmin… custom fields for the rest. Values already present are never overwritten; locked days are skipped; today’s running totals wait until the day is over.
  • Backfill: backfill --scope wellness --from 2025-01-01 --apply walks the past at a polite pace (on my account night SpO₂ had silently stopped arriving in July 2025 – it’s all back now). Activities from the past the same way – and in upload mode, backfill --scope activities --mode upload --from 2026-01-01 --apply uploads the originals of past activities that Intervals doesn’t have.
  • Everything is a dry run until you add --apply; the first run prints exactly what it would write.

Charts – 19 charts in the chart library, one per topic: readiness & recovery, sleep stages, sleep score & sleeping HR, SpO₂ & respiration, stress & Body Battery, HRV detail, nutrition intake vs. burn, macro energy share, weekly/monthly energy balance with weight, endurance & hill scores, VO₂max & fitness age, race predictions, intensity minutes & sweat loss, steps, body composition, skin temperature. Each uses one axis per unit and carries standing values (VO₂max, scores …) across days without a measurement. Fitness page → a tab → custom charts → search “Garmin Bridge”.




Running it – Linux: systemd timers for an unprivileged user (hardened units included), or the container image. Windows: pip install garmin-intervals-bridge, login, run --apply – a ten-minute guide for non-technical people is in the repo (docs/WINDOWS.md).

Honest caveats – Garmin’s endpoints are private and may change or rate-limit (the tool backs off and tells you). Writing streams sets icu_intervals_edited on the activity and the API can’t reset it. Verified on one account so far (fenix 8, Edge 1040); reports from other devices are very welcome.

@david – two API wishes from building this: (1) a webhook on new activity/file upload would let the bridge react instantly instead of polling once a minute – faster for users, fewer requests for you; (2) a way to write custom streams without flagging the intervals as edited (or to reset icu_intervals_edited).

5 Likes

Nice one - I’ll give it a try to fill the missing data again. :slight_smile:

Regarding the first point:
Webhooks work only for registered apps. From a security standpoint this makes much sense.

1 Like

This looks like a really useful project for Garmin users. Getting the missing data back and keeping the official sync free of duplicates is a great idea. The dry-run option is also helpful for checking changes before applying them.

1 Like

Thanks – let me know how it goes!

You’re right about the webhooks – that’s why it’s a question for David rather than a feature request to myself. Two ways I could see: a webhook for API-key integrations, or registering the bridge as an app so each user authorizes it once instead of pasting an API key (which would also be the nicer setup). Let’s see what he thinks.

Thanks George! Dry run first was the rule from day one – the first real write into an activity only happened after a before/after diff of every field. Feedback from other devices is very welcome.

Wow what a timing, considering the mail some of just received in regards to the updated T&Cs from Garmin. Even though you probably don’t have all the details on that either yet, what’s your take on this and do you think your bridge could be a potential solution to that?

1 Like

Good timing indeed, though not the kind anyone wanted. My take, with the caveat that nobody outside Garmin has the full picture yet:

The clause only governs data that Intervals receives through Garmin’s partner API. What you upload yourself is your file – David said as much in the API thread (“these are likely ok”). So yes, the bridge is one way through: switch the official Garmin import off in Intervals and let it upload the original, unfiltered FIT of every new activity as your own upload. I verified that today on my account – Intervals computes Stamina, Recovery Time, VO₂max, Performance Condition, Training Effect, Sweat Loss and the other custom items from the complete file itself, nothing is flagged as edited, and third-party apps see a plain user upload. Wellness (SpO₂, sleep, HRV, Body Battery, nutrition, scores …) works the same way, because the bridge reads it from your own Garmin account, not from Intervals’ Garmin connection.

What it is not: a fix for apps that read Garmin-sourced data from other people’s Intervals accounts – each athlete has to run the bridge (or any uploader) themselves. And the Garmin side is private API with your own login; Garmin’s own terms frown on automated access, so it’s your call, with the risk of account suspension if Garmin ever decides to act on it (no known case so far).

For now I keep the official import on and the bridge in enrich mode; the upload mode is documented and ready for the day the block lands: GitHub - futureweb/garmin-intervals-bridge: Puts back what Garmin strips from the FIT files it sends to Intervals.icu - and syncs the wellness data the official sync misses · GitHub → “Upload mode”.

1 Like

Thanks for your view on things, so quickly afer David’s post.

I’ve been working on exactly such an app for a while now and gathered a small number of active users already so obviously I am concerned about the possible (not yet confirmed) implications of these changes. The upload mode does look interesting as a, let’s call it a first inspiration, but looking at Garmin and their way of restricting access to their data step by step, I reckon it won’t be long until they block out this as well (fingers crossed I am wrong). Also, would I want to be responsible for any account suspension for any of my users? Def. not…

And I would understand if Garmin just wanted to make sure all access to their data happens via their own API, via their own terms etc. fair enough but as far as I understood, they don’t really allow any new developers to their developer program. So not sure what their goal is here…

:money_bag::money_bag::money_bag:

My hunch is that you (developer of third party app) at some point will be able to pay for the privilege of accessing a user’s Garmin data.

They’ll want to extract payment no matter whether the user relies on Garmin (Connect+) or a third party app to look at their Garmin based data. For now they’ll shut down the “hubs” that exist as secondary sources for Garmin data. The logical conclusion to this wall-building around their data is that every third party platform will pay a tax at some point. They’re just tightening the noose bit by bit.

Just a hunch, of course.

1 Like

The goal is clear, after acquiring TrainingPeaks Garmin will want to exclude other training platforms and push ‘GarminPeaks’

1 Like

Training Peaks has already started getting Wellness data “just like that” with no invitation from me! I don’t mind; it just started appearing.

Fair concerns – and the last one is exactly why I built this as a self-hosted tool and not as a service. Everyone runs it with their own Garmin login, on their own machine, and decides for themselves; the README says plainly that Garmin’s terms don’t allow automated access and that the account risk is your own. For an app acting on behalf of its users I see it the same way you do – that’s a different situation, and not one I’d want to be in either.

On “they’ll block this as well”: what you upload to Intervals is your file and outside Garmin’s reach; what they could close is the way the file leaves Connect. That’s why the bridge archives every original FIT and every wellness day locally – whatever happens later, what you already have stays yours.