Skip to content

cyberjunky/python-garminconnect

Repository files navigation

GitHub Release GitHub Activity License Project Maintenance

Donate via PayPal Sponsor on GitHub

Python: Garmin Connect

The Garmin Connect API library comes with two examples:

  • example.py - Simple getting-started example showing authentication, token storage, and basic API calls
  • demo.py - Comprehensive demo providing access to 130+ API methods organized into 13 categories for easy navigation
$ ./demo.py
πŸƒβ€β™‚οΈ Full-blown Garmin Connect API Demo - Main Menu
==================================================
Select a category:

  [1] πŸ‘€ User & Profile
  [2] πŸ“Š Daily Health & Activity
  [3] πŸ”¬ Advanced Health Metrics
  [4] πŸ“ˆ Historical Data & Trends
  [5] πŸƒ Activities & Workouts
  [6] βš–οΈ Body Composition & Weight
  [7] πŸ† Goals & Achievements
  [8] ⌚ Device & Technical
  [9] 🎽 Gear & Equipment
  [0] πŸ’§ Hydration & Wellness
  [a] πŸ”§ System & Export
  [b] πŸ“… Training plans
  [c] β›³ Golf

  [q] Exit program

Make your selection:

API Coverage Statistics

  • Total API Methods: 134+ unique endpoints (snapshot)
  • Categories: 13 organized sections
  • User & Profile: 4 methods (basic user info, settings)
  • Daily Health & Activity: 9 methods (today's health data)
  • Advanced Health Metrics: 12 methods (fitness metrics, HRV, VO2, training readiness, running tolerance)
  • Historical Data & Trends: 9 methods (date range queries, weekly aggregates)
  • Activities & Workouts: 38 methods (comprehensive activity, workout management, typed workout uploads, scheduling, import, edit description / exercise sets)
  • Body Composition & Weight: 8 methods (weight tracking, body composition)
  • Goals & Achievements: 15 methods (challenges, badges, goals)
  • Device & Technical: 7 methods (device info, settings)
  • Gear & Equipment: 7 methods (gear management, tracking)
  • Hydration & Wellness: 12 methods (hydration, nutrition, blood pressure, menstrual)
  • System & Export: 4 methods (reporting, logout, GraphQL)
  • Training Plans: 3 methods (plans, plan by ID, adaptive plan by ID)
  • Golf: 3 methods (scorecard summary, scorecard detail, shot data)

Interactive Features

  • Enhanced User Experience: Categorized navigation with emoji indicators
  • Smart Data Management: Interactive weigh-in deletion with search capabilities
  • Comprehensive Coverage: All major Garmin Connect features are accessible
  • Error Handling: Robust error handling with user-friendly prompts
  • Data Export: JSON export functionality for all data types

Donate via PayPal Sponsor on GitHub

A comprehensive Python3 API wrapper for Garmin Connect, providing access to health, fitness, and device data.

πŸ“– About

This library enables developers to programmatically access Garmin Connect data including:

  • Health Metrics: Heart rate, sleep, stress, body composition, SpO2, HRV
  • Activity Data: Workouts, typed workout uploads (running, cycling, swimming, walking, hiking), workout scheduling, exercises, training status, performance metrics, import-style uploads (no Strava re-export)
  • Nutrition: Daily food logs, meals, and nutrition settings
  • Golf: Scorecard summaries, scorecard details, shot-by-shot data
  • Device Information: Connected devices, settings, alarms, solar data
  • Goals & Achievements: Personal records, badges, challenges, race predictions
  • Historical Data: Trends, progress tracking, date range queries

Compatible with all Garmin Connect accounts. See https://connect.garmin.com/

πŸ“¦ Installation

Install from PyPI:

pip install --upgrade garminconnect curl_cffi

Run demo software (recommended)

Clone the repo, then:

python3 -m venv .venv --copies
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
pip install -e ".[example]"

python3 ./example.py   # simple getting-started example
python3 ./demo.py      # comprehensive demo (130+ API methods)

πŸ› οΈ Development

This project uses PDM for dependency management and task automation.

⚠️ Important: Create a virtual environment first on externally-managed Python installs (Debian/Ubuntu) to avoid system package conflicts.

python3 -m venv .venv --copies
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
pip install pdm
python -m pdm install --group :all
pre-commit install --install-hooks  # optional but recommended

Note: Using python -m pdm instead of pdm avoids PATH issues on some Windows setups where pip install pdm places the pdm executable outside the directories on PATH. Once pdm install has run, subsequent pdm run ... commands work normally because the venv's Scripts/ directory is on PATH while the venv is active.

Development commands:

pdm run format      # Auto-format code (isort, black, ruff --fix)
pdm run lint        # Check code quality (isort, ruff, black, mypy)
pdm run codespell   # Check spelling
pdm run test        # Run test suite
pdm run testcov     # Run tests with coverage report
pdm run all         # Run all checks (lint + codespell + pre-commit + test)
pdm run clean       # Clean build artifacts and cache files
pdm run build       # Build package for distribution
pdm run publish     # Build and publish to PyPI
pdm run --list      # Show all available commands

Run pdm run format && pdm run lint && pdm run test before submitting PRs.

πŸ” Authentication

Authentication uses the same mobile SSO flow as the official Garmin Connect Android app. No browser is needed.

How it works:

  1. First login: Authenticates via sso.garmin.com/mobile/api/login using the Android app's client ID. If MFA is required, a callback (prompt_mfa) prompts for the one-time code.
  2. Token exchange: The service ticket is exchanged for DI OAuth Bearer tokens (access_token + refresh_token) via diauth.garmin.com. Tokens are stored at ~/.garminconnect/garmin_tokens.json.
  3. Auto-refresh: Before each API request the library checks whether the DI token is about to expire and refreshes it automatically β€” no user interaction required.

Session lifetime:

  • DI tokens auto-refresh indefinitely as long as the refresh token remains valid.
  • A full re-login with credentials (and possibly MFA) is only needed if the refresh token itself expires or is revoked.

Token storage:

~/.garminconnect/garmin_tokens.json   # saved automatically, mode 0600

The containing directory is restricted to mode 0700. Treat the token file like a password: the refresh token can provide persistent account access. Avoid putting a Garmin password in shell history or a long-lived environment variable; prefer getpass() or another interactive secret prompt.

Resilient login (multi-strategy + token validation):

login() tries several authentication strategies in order (mobile, SSO widget, web portal β€” each with and without TLS impersonation) and only declares success when the resulting token is actually accepted by the API. If a strategy obtains a token the API later rejects (a region/account-specific condition β€” see #369), the library transparently falls through to the next strategy. Set Garmin(..., verify_login=False) to restore the legacy "first token wins" behavior.

Cached-token gotcha & self-healing: when a tokenstore is supplied, login() loads those tokens before the strategy chain and short-circuits if they load β€” so stale/poisoned cached tokens used to fail every run. The library now detects this: if cached tokens are rejected by the API, it discards them and performs a fresh credential login automatically. To force a clean slate yourself (e.g. between a failed resume and a retry), call:

g.logout()            # clears in-memory auth + cached tokens (uses GARMINTOKENS)
g.logout(tokenstore)  # or pass an explicit path

logout() removes only the local garmin_tokens.json file and preserves its directory and unrelated files. It does not revoke a token that has already been issued by Garmin. Revoke account access from Garmin's account/security settings if a token may have been copied or exposed.

What running it locally does

This is an unofficial client for Garmin's web services; it does not pair with the Garmin Connect phone app or connect directly to a watch. When you call login(), your credentials and MFA code are sent over HTTPS to Garmin's login service. The library receives an access/refresh token pair and, when a token store is supplied, caches it locally for later sessions. Subsequent API methods send that token to Garmin and can read or change the same account data that the selected method targets.

The library does not automatically download an entire account. The demo writes responses, activity downloads, and health reports only when you select those actions. Demo exports are stored under your_data/ with owner-only directory and file permissions. Run the project in a dedicated virtual environment, read the method you plan to call, and start with read-only methods. Upload, edit, delete, schedule, hydration, and weigh-in methods can change Garmin account data.

πŸ§ͺ Testing

The default suite is credential-free and excludes live-account integration tests:

pdm run test        # Run all tests
pdm run testcov     # Run tests with coverage report

To explicitly run live integration tests, use a test account if possible. This can create local VCR recordings and includes methods that may mutate the account:

export GARMIN_EMAIL="you@example.com"
read -s GARMIN_PASSWORD && export GARMIN_PASSWORD
pdm run pytest -m integration --vcr-record=once
unset GARMIN_PASSWORD

VCR recordings are ignored by Git because Garmin responses contain sensitive health, activity, location, and account data. Do not commit them.

πŸ“¦ Publishing

For package maintainers:

Setup PyPI credentials:

pip install twine
# Edit with your preferred editor, or create via here-doc:
# cat > ~/.pypirc <<'EOF'
# [pypi]
# username = __token__
# password = <PyPI_API_TOKEN>
# EOF
[pypi]
username = __token__
password = <PyPI_API_TOKEN>

Recommended: use environment variables and restrict file perms

chmod 600 ~/.pypirc
export TWINE_USERNAME="__token__"
export TWINE_PASSWORD="<PyPI_API_TOKEN>"

Publish new version:

pdm run publish    # Build and publish to PyPI

Alternative publishing steps:

pdm run build      # Build package only
pdm publish        # Publish pre-built package

🀝 Contributing

We welcome contributions! Here's how you can help:

  • Report Issues: Bug reports and feature requests via GitHub issues
  • Submit PRs: Code improvements, new features, documentation updates
  • Testing: Help test new features and report compatibility issues
  • Documentation: Improve examples, add use cases, fix typos

Before contributing:

  1. Set up your dev environment (see Development above)
  2. Format and lint: pdm run format && pdm run lint
  3. Run tests: pdm run test
  4. Follow existing code style and patterns

Jupyter Notebook

Explore the API interactively with our reference notebook.

Python Code Examples

import os
from getpass import getpass
from datetime import date
from garminconnect import Garmin

# First run: logs in and saves tokens to ~/.garminconnect
# Subsequent runs: loads saved tokens and auto-refreshes
client = Garmin(
    os.getenv("EMAIL"),
    getpass("Garmin password: "),
    prompt_mfa=lambda: input("MFA code: "),
)
client.login("~/.garminconnect")

# Get today's stats
today = date.today().isoformat()
stats = client.get_stats(today)

# Get heart rate data
hr_data = client.get_heart_rates(today)
print(f"Resting HR: {hr_data.get('restingHeartRate', 'n/a')}")

Typed Workouts (Pydantic Models)

The library includes optional typed workout models for creating type-safe workout definitions:

pip install garminconnect[workout]
from garminconnect.workout import (
    RunningWorkout, WorkoutSegment,
    create_warmup_step, create_interval_step, create_distance_interval_step,
    create_cooldown_step,
    create_repeat_group,
)

# Create a structured running workout
workout = RunningWorkout(
    workoutName="Easy Run",
    estimatedDurationInSecs=1800,
    workoutSegments=[
        WorkoutSegment(
            segmentOrder=1,
            sportType={"sportTypeId": 1, "sportTypeKey": "running"},
            workoutSteps=[create_warmup_step(300.0)]
        )
    ]
)

# Upload and optionally schedule it
result = client.upload_running_workout(workout)
client.schedule_workout(result["workoutId"], "2026-03-20")

# Delete a workout or remove it from the calendar
client.delete_workout(workout_id)
client.unschedule_workout(scheduled_workout_id)

Available workout classes: RunningWorkout, CyclingWorkout, SwimmingWorkout, WalkingWorkout, HikingWorkout, MultiSportWorkout, FitnessEquipmentWorkout

Helper functions: create_warmup_step, create_interval_step, create_distance_interval_step, create_recovery_step, create_cooldown_step, create_repeat_group

Use create_distance_interval_step(600.0, step_order=1) for interval steps that should end after a distance in meters instead of after a duration.

Additional Resources

  • Simple Example: example.py - Getting started guide
  • Comprehensive Demo: demo.py - All 130+ API methods
  • API Documentation: Comprehensive method documentation in source code
  • Test Cases: Real-world usage examples in tests/ directory

πŸ™ Acknowledgments

Special thanks to all contributors who have helped improve this project:

  • Community Contributors: Bug reports, feature requests, and code improvements
  • Issue Reporters: Helping identify and resolve compatibility issues
  • Feature Developers: Adding new API endpoints and functionality
  • Documentation Authors: Improving examples and user guides

This project thrives thanks to community involvement and feedback.

πŸ’– Support This Project

If you find this library useful for your projects, please consider supporting its continued development and maintenance:

🌟 Ways to Support

  • ⭐ Star this repository - Help others discover the project
  • πŸ’° Financial Support - Contribute to development and hosting costs
  • πŸ› Report Issues - Help improve stability and compatibility
  • πŸ“– Spread the Word - Share with other developers

πŸ’³ Financial Support Options

Donate via PayPal Sponsor on GitHub

Why Support?

  • Keeps the project actively maintained
  • Enables faster bug fixes and new features
  • Supports infrastructure costs (testing, AI, CI/CD)
  • Shows appreciation for hundreds of hours of development

Every contribution, no matter the size, makes a difference and is greatly appreciated! πŸ™

About

Python 3 API wrapper for Garmin Connect to get statistics and set activities

Topics

Resources

Security policy

Stars

Watchers

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages