python-garminconnect

repository·master·Indexed 25 days ago

https://github.com/cyberjunky/python-garminconnect

A Python 3.12+ API wrapper for Garmin Connect that allows developers to programmatically access health, fitness, device, and nutrition data. The library supports mobile SSO authentication with automatic token refresh, provides over 130 API methods across 13 categories, and includes optional Pydantic models for type-safe workout definitions and data access.

Tokens
14.6K
Snippets
19
Records
93
Agent score
83%

What's inside garminconnect

  1. Overview of accessible Garmin Connect data

    master

    The library provides programmatic access to a wide range of Garmin data:

    • Health Metrics: Heart rate, sleep, stress, body composition, SpO2, HRV.
    • Activity Data: Workouts (running, cycling, swimming, etc.), workout scheduling, training status, and performance metrics.
    • Nutrition: Daily food logs, meals, and nutrition settings.
    • Golf: Scorecard summaries, details, and shot-by-shot data.
    • Device Info: Connected devices, settings, alarms, and solar data.
    • Goals & Achievements: Personal records, badges, challenges, and race predictions.
    • Historical Data: Trends, progress tracking, and date range queries.
  2. Run the Garmin Connect API demo

    master

    To explore the library's capabilities, you can run the provided demo software. This requires cloning the repository and installing the package in editable mode with the [example] extra. The demo provides an interactive menu to access over 130 API methods across 13 categories (e.g., User & Profile, Daily Health, Activities & Workouts, etc.).

    python3 -m venv .venv --copies
    source .venv/bin/activate  # On Windows: .venv\Scripts\activate
    pip install -e ".[example]"
    
    python3 ./demo.py
  3. Create strength workouts with the exercise catalog

    master

    Strength workouts are rep-based. Use StrengthWorkout and create_strength_set to build them. To ensure correct exercise identification, use the garminconnect.exercises module to resolve exercise names into the required category/exercise pairs.

    from garminconnect import exercises
    from garminconnect.workout import StrengthWorkout, WorkoutSegment, create_strength_set
    
    # Resolve exercise name to category and exercise ID
    lat = exercises.resolve(
        "Lat Pull-down"
    )  # {'category': 'PULL_UP', 'exercise': 'LAT_PULLDOWN'}
    
    workout = StrengthWorkout(
        workoutName="Upper Body",
        estimatedDurationInSecs=0,
        workoutSegments=[
            WorkoutSegment(
                segmentOrder=1,
                sportType={"sportTypeId": 5, "sportTypeKey": "strength_training"},
                workoutSteps=[
                    create_strength_set(
                        "BENCH_PRESS", step_order=1, sets=4, reps=10, rest_seconds=120
                    ),
                    create_strength_set(
                        lat["category"],
                        step_order=4,
                        sets=3,
                        reps=12,
                        rest_seconds=90,
                        exercise_name=lat["exercise"],
                    ),
                ],
            )
        ],
    )
    client.upload_strength_workout(workout)
  4. Understand Garmin Connect Authentication

    master

    The library uses a mobile SSO flow (via sso.garmin.com/mobile/api/login) that does not require a browser.

    Key behaviors:

    • Token Exchange: Service tickets are exchanged for DI OAuth Bearer tokens (access_token and refresh_token).
    • Automatic Refresh: The library automatically refreshes tokens before they expire without user intervention.
    • Token Storage: Tokens are saved to ~/.garminconnect/garmin_tokens.json (mode 0600).
    • Resilient Login: login() attempts multiple strategies (mobile, SSO widget, web portal) and only succeeds if the token is actually accepted by the API. If a cached token is rejected, the library automatically discards it and performs a fresh login.

    Security Note: Treat the token file like a password. Avoid using long-lived environment variables for credentials; use getpass() for interactive prompts.

  5. Create and manage structured workouts

    master

    Use the garminconnect.workout module to build structured workouts (e.g., RunningWorkout, CyclingWorkout, etc.). You can define segments and steps using helper functions like create_warmup_step or create_interval_step.

    After creating a workout, you can:

    • Upload it using upload_running_workout(workout) (or specific sport methods).
    • Schedule it using schedule_workout(workout_id, date).
    • Update it using update_workout(workout_id, workout_data).
    • Delete it using delete_workout(workout_id) or unschedule_workout(scheduled_workout_id).
    • Push it to a device using push_workout_to_device(workout_id, device_id).
    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")
  6. Authenticate with Garmin Connect

    master

    To use the library, instantiate the Garmin class with your email and password, then call the .login() method. If Multi-Factor Authentication (MFA) is enabled on your account, the login process will prompt for it.

    import garminconnect
    from getpass import getpass
    
    email = input("Enter email address: ")
    password = getpass("Enter password: ")
    
    garmin = garminconnect.Garmin(email, password)
    garmin.login()
    
    # Access user info
    print(garmin.display_name)
  7. Use the `typed` namespace for validated API responses

    master

    Instead of receiving raw dict[str, Any] responses from standard methods, you can use the g.typed property to get validated Pydantic models. This is useful for accessing high-value endpoints with type safety and IDE autocompletion.

    Note: This feature is experimental. Model shapes and method signatures may change between minor releases. It is recommended to pin your version if you depend on these shapes.

    Example usage:

    from garminconnect import Garmin
    
    g = Garmin(email, password)
        g.login()
    
    # Standard method returns a dict
    raw = g.get_stats("2026-04-21")
    
    # Typed method returns a DailyStats Pydantic model
    stats = g.typed.get_stats("2026-04-21")
    print(stats.total_steps, stats.resting_heart_rate)
    from garminconnect import Garmin
    
    g = Garmin(email, password)
        g.login()
    
        raw = g.get_stats("2026-04-21")           # dict[str, Any] — unchanged
        stats = g.typed.get_stats("2026-04-21")   # DailyStats (Pydantic)
        print(stats.total_steps, stats.resting_heart_rate)
  8. Install workout model dependencies

    master

    The workout models use pydantic for type safety. While the library provides a fallback if it is missing, it is highly recommended to install it for full functionality.

    You can install it via pip:

    pip install pydantic

    Or install the workout extra for garminconnect:

    pip install garminconnect[workout]
    pip install pydantic
    # or
    pip install garminconnect[workout]
  9. Save the Garmin session to disk

    master

    You can persist your session to avoid repeated logins by using the .garth.dump() method. This saves the session data to a specified directory. By default, you can use the GARTH_HOME environment variable or specify a path manually.

    import os
    
    # Specify the directory where the session should be saved
    GARTH_HOME = os.getenv("GARTH_HOME", "~/.garth")
    garmin.garth.dump(GARTH_HOME)