507 lines
20 KiB
Markdown
507 lines
20 KiB
Markdown
|
|
[![GitHub Release][releases-shield]][releases]
|
|||
|
|
[![GitHub Activity][commits-shield]][commits]
|
|||
|
|
[![License][license-shield]](LICENSE)
|
|||
|
|
![Project Maintenance][maintenance-shield]
|
|||
|
|
|
|||
|
|
[](https://www.paypal.me/cyberjunkynl/)
|
|||
|
|
[](https://github.com/sponsors/cyberjunky)
|
|||
|
|
|
|||
|
|
# 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
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
$ ./demo.py
|
|||
|
|
📁 Exported data will be saved to the directory: 'your_data'
|
|||
|
|
📄 All API responses are written to: 'response.json'
|
|||
|
|
Attempting to login using stored tokens from: ~/.garminconnect
|
|||
|
|
Successfully logged in using stored tokens!
|
|||
|
|
|
|||
|
|
📊 Your Stats Today: 4,045 steps | 1445.0 kcal
|
|||
|
|
🐌 Time to get those legs moving!
|
|||
|
|
|
|||
|
|
==================================================
|
|||
|
|
🚴 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
|
|||
|
|
[d] ✏️ Activity Editing
|
|||
|
|
|
|||
|
|
[q] Exit program
|
|||
|
|
|
|||
|
|
Make your selection:
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## API Coverage Statistics
|
|||
|
|
|
|||
|
|
- **Total API Methods**: 144+ unique endpoints (snapshot)
|
|||
|
|
- **Categories**: 13 organized sections
|
|||
|
|
- **User & Profile**: 4 methods (basic user info, settings)
|
|||
|
|
- **Daily Health & Activity**: 10 methods (today's health data plus daily calories, resting HR and sleep ranges)
|
|||
|
|
- **Advanced Health Metrics**: 16 methods (fitness metrics, HRV, VO2, FTP range, training readiness, training zones, running tolerance)
|
|||
|
|
- **Historical Data & Trends**: 9 methods (date range queries, weekly aggregates)
|
|||
|
|
- **Activities & Workouts**: 41 methods (comprehensive activity, workout management, typed workout uploads including strength, in-place edit, scheduling, push to device, 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**: 5 methods (reporting, logout, GraphQL, health snapshot download)
|
|||
|
|
- **Training Plans**: 3 methods (plans, plan by ID, adaptive plan by ID)
|
|||
|
|
- **Golf**: 5 methods (scorecard summary, scorecard detail, shot data, club stats, user stats)
|
|||
|
|
|
|||
|
|
### 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
|
|||
|
|
|
|||
|
|
[](https://www.paypal.me/cyberjunkynl/)
|
|||
|
|
[](https://github.com/sponsors/cyberjunky)
|
|||
|
|
|
|||
|
|
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, strength), 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
|
|||
|
|
|
|||
|
|
Requires Python 3.12 or later.
|
|||
|
|
|
|||
|
|
Install from PyPI:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
pip install --upgrade garminconnect curl_cffi
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Run demo software (recommended)
|
|||
|
|
|
|||
|
|
Clone the repo, then:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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](https://pdm.fming.dev/) for dependency management and task automation.
|
|||
|
|
|
|||
|
|
> **⚠️ Important**: Create a virtual environment first on externally-managed Python installs (Debian/Ubuntu) to avoid system package conflicts.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
python3 -m venv .venv --copies
|
|||
|
|
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|||
|
|
pip install pdm
|
|||
|
|
python3 -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:**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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:**
|
|||
|
|
```bash
|
|||
|
|
~/.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](https://github.com/cyberjunky/python-garminconnect/issues/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:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
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:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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:**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
pip install twine
|
|||
|
|
# Edit with your preferred editor, or create via here-doc:
|
|||
|
|
# cat > ~/.pypirc <<'EOF'
|
|||
|
|
# [pypi]
|
|||
|
|
# username = __token__
|
|||
|
|
# password = <PyPI_API_TOKEN>
|
|||
|
|
# EOF
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```ini
|
|||
|
|
[pypi]
|
|||
|
|
username = __token__
|
|||
|
|
password = <PyPI_API_TOKEN>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Recommended: use environment variables and restrict file perms
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
chmod 600 ~/.pypirc
|
|||
|
|
export TWINE_USERNAME="__token__"
|
|||
|
|
export TWINE_PASSWORD="<PyPI_API_TOKEN>"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Publish new version:**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
pdm run publish # Build and publish to PyPI
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Alternative publishing steps:**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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](#️-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](https://github.com/cyberjunky/python-garminconnect/blob/master/docs/reference.ipynb).
|
|||
|
|
|
|||
|
|
### Python Code Examples
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
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("GARMIN_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:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
pip install garminconnect[workout]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
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")
|
|||
|
|
|
|||
|
|
# Edit it in place - keeps its id, so any schedules pointing at it stay valid
|
|||
|
|
workout_data = client.get_workout_by_id(result["workoutId"])
|
|||
|
|
workout_data["workoutName"] = "Easy Run (revised)"
|
|||
|
|
client.update_workout(result["workoutId"], workout_data)
|
|||
|
|
|
|||
|
|
# Delete a workout or remove it from the calendar
|
|||
|
|
client.delete_workout(workout_id)
|
|||
|
|
client.unschedule_workout(scheduled_workout_id)
|
|||
|
|
|
|||
|
|
# Push a workout to a device - defaults to the last workout / last used device
|
|||
|
|
client.push_workout_to_device(result["workoutId"], device_id)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Available workout classes:** `RunningWorkout`, `CyclingWorkout`, `SwimmingWorkout`, `WalkingWorkout`, `HikingWorkout`, `StrengthWorkout`, `MultiSportWorkout`, `FitnessEquipmentWorkout`
|
|||
|
|
|
|||
|
|
**Strength workouts** are rep-based. Build each exercise with `create_strength_set(category, step_order, sets, reps, rest_seconds, exercise_name="", weight_kg=None)` and identify exercises with a `category` / `exercise` pair from the bundled catalog in `garminconnect.exercises` (1,527 exercises across 47 categories, with `resolve(name)` and `find(term)` helpers):
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from garminconnect import exercises
|
|||
|
|
from garminconnect.workout import StrengthWorkout, WorkoutSegment, create_strength_set
|
|||
|
|
|
|||
|
|
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)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Helper functions:** `create_warmup_step`, `create_interval_step`, `create_distance_interval_step`, `create_recovery_step`, `create_cooldown_step`, `create_repeat_group`, `create_strength_exercise_step`, `create_strength_rest_step`, `create_strength_set`
|
|||
|
|
|
|||
|
|
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.
|
|||
|
|
|
|||
|
|
### Golf
|
|||
|
|
|
|||
|
|
The library supports reading Garmin Golf scorecards, shot-level data, club statistics, and golf user statistics.
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# List recent golf scorecards
|
|||
|
|
summary = client.get_golf_summary(limit=10)
|
|||
|
|
scorecard_id = summary["scorecardSummaries"][0]["id"]
|
|||
|
|
|
|||
|
|
# Get the full scorecard details
|
|||
|
|
scorecard = client.get_golf_scorecard(scorecard_id)
|
|||
|
|
|
|||
|
|
# Get shot-by-shot data
|
|||
|
|
# - Omit hole_numbers to receive every hole (recommended for holes 10-18).
|
|||
|
|
# - Single-digit holes work as a comma- or dash-separated list, e.g. "1,2,3".
|
|||
|
|
# Commas are normalized to dashes because Garmin only accepts '-' as the
|
|||
|
|
# separator.
|
|||
|
|
# - Double-digit holes (10-18) cannot be fetched with a filter at all; the
|
|||
|
|
# API either drops them or returns an empty response. If you request holes
|
|||
|
|
# 10-18, the library falls back to returning all 18 holes.
|
|||
|
|
shots = client.get_golf_shot_data(scorecard_id)
|
|||
|
|
shots = client.get_golf_shot_data(scorecard_id, hole_numbers="1,2,3")
|
|||
|
|
|
|||
|
|
# Club and player statistics
|
|||
|
|
club_stats = client.get_golf_club_stats()
|
|||
|
|
user_stats = client.get_golf_user_stats()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Additional Resources
|
|||
|
|
- **Simple Example**: [example.py](https://raw.githubusercontent.com/cyberjunky/python-garminconnect/master/example.py) - Getting started guide
|
|||
|
|
- **Comprehensive Demo**: [demo.py](https://raw.githubusercontent.com/cyberjunky/python-garminconnect/master/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
|
|||
|
|
|
|||
|
|
[](https://www.paypal.me/cyberjunkynl/)
|
|||
|
|
[](https://github.com/sponsors/cyberjunky)
|
|||
|
|
|
|||
|
|
**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! 🙏
|
|||
|
|
|
|||
|
|
[releases-shield]: https://img.shields.io/github/release/cyberjunky/python-garminconnect.svg?style=for-the-badge
|
|||
|
|
[releases]: https://github.com/cyberjunky/python-garminconnect/releases
|
|||
|
|
[commits-shield]: https://img.shields.io/github/commit-activity/y/cyberjunky/python-garminconnect.svg?style=for-the-badge
|
|||
|
|
[commits]: https://github.com/cyberjunky/python-garminconnect/commits/master
|
|||
|
|
[license-shield]: https://img.shields.io/github/license/cyberjunky/python-garminconnect.svg?style=for-the-badge
|
|||
|
|
[maintenance-shield]: https://img.shields.io/badge/maintainer-cyberjunky-blue.svg?style=for-the-badge
|