Fast, script-friendly CLI for Garmin Connect. Access activities, health data, body composition, workouts, courses, devices, gear, goals, badges, and more. JSON-first output, multiple accounts, and secure credential storage built in.
- Activities — list, search, view details, download (FIT/GPX/TCX/KML/CSV), upload, create manual entries (with date), rename, retype, delete, set exercise sets for strength training, list activity types
- Exercise Catalog — browse Garmin's exercise categories and exercises for strength training
- Health Data — daily summaries, steps, heart rate, resting HR, sleep, stress, HRV, SpO2, respiration, body battery, floors, training readiness/status, VO2max, fitness age, race predictions, endurance/hill scores, intensity minutes, lactate threshold, cycling FTP
- Body Composition — weight tracking, body fat, muscle mass, blood pressure, FIT file encoding for composition uploads
- Workouts — list, view, download as FIT, upload from JSON, update in place from JSON, create with sport types and targets (pace/HR/power/cadence), schedule (add/list/remove), delete
- Courses — list courses, view favorites, get full course detail, download as FIT/GPX, import GPX files as new courses, send courses directly to a device, delete courses
- Devices — list registered devices, view settings, solar data, alarms, primary/last-used device
- Gear — list gear, usage stats, linked activities, defaults per activity type, link/unlink to activities
- Goals & Badges — active goals, earned/available/in-progress badges, challenges, personal records
- Training — browse and view training plans
- Profile — user profile and settings
- Hydration — view and log daily water intake
- Nutrition — view daily food logs, meals, and nutrition settings
- Events — list, add, and delete calendar events (races, group rides, etc.) with goals and training priority support
- Wellness — menstrual cycle data, pregnancy summary
- Agent-ready - ships a bundled agent skill (
skills/gccli/) that plugs into AI assistants like Claude Code, Codex, Hermes, and OpenClaw so you can drive Garmin Connect in plain language - Multiple accounts — manage multiple Garmin accounts via
--accountflag - Secure credential storage using OS keyring (macOS Keychain, Linux Secret Service, encrypted file fallback)
- Auto-refreshing tokens — authenticate once, tokens refresh automatically on 401
- Resilient networking — automatic retry on 429/5xx with exponential backoff, circuit breaker for fault tolerance
- Shell completion — generated scripts for bash, zsh, fish, and PowerShell
- Parseable output — JSON and plain/TSV modes for scripting and automation
brew install bpauli/tap/gccliRequires Go 1.24+.
git clone https://github.com/bpauli/gccli.git
cd gccli
make buildThe binary is output to ./bin/gccli.
Run:
./bin/gccli --helpHelp:
gccli --helpshows top-level command groups.- Drill down with
gccli <group> --help(and deeper subcommands). - Make shortcut:
make run -- --help(ormake run -- activities --help).
gccli auth login you@example.comThis opens your browser for Garmin SSO authentication. The DI OAuth tokens are stored securely in your system keyring.
For servers or environments without a browser:
gccli auth login you@example.com --headlessYou'll be prompted for your password. If your account has two-factor authentication:
gccli auth login you@example.com --headless --mfa-code 123456Login automatically saves your email as the default account, so you don't need to pass --account or set GCCLI_ACCOUNT on every command.
gccli auth status
gccli activities list --limit 5gccli ships a ready-to-use agent skill at skills/gccli/SKILL.md. It teaches an AI assistant the full command surface - auth, activities, health, body composition, workouts, courses, devices, gear, goals, events, and more - so you can ask in plain language and let the agent drive the CLI:
- "How did I sleep last week?"
- "Download my last three runs as GPX."
- "Log today's strength session: bench press 3x12@20kg, lat pulldowns 3x12@41kg."
- "Build me a tempo run and schedule it for Saturday."
The skill uses the portable SKILL.md format (frontmatter + Markdown) and works with any agent runtime that supports skills, including:
- Claude Code - Anthropic's terminal-native coding agent
- Codex - OpenAI's coding agent
- Hermes - agent runtime
- OpenClaw - the skill's frontmatter carries install metadata (Homebrew / build-from-source) so OpenClaw can provision the
gcclibinary automatically
Clone the repo and point your agent at the bundled skill directory (copy or symlink it into wherever your agent discovers skills):
git clone https://github.com/bpauli/gccli.git
# then copy or symlink gccli/skills/gccli into your agent's skills directoryMake sure the gccli binary is installed (see Installation) and you have authenticated once with gccli auth login. Set GCCLI_ACCOUNT so the agent doesn't need to pass --account on every call. The skill instructs the agent to use --json output when parsing results and to confirm before destructive actions.
The skill includes a guided workflow for logging free-text strength sessions: the agent fetches Garmin's exercise catalog (gccli exercises list --json), maps your described exercises to Garmin's CATEGORY/EXERCISE_NAME pairs, confirms the mapping with you, creates the activity, and records each set via gccli activity exercise-sets set.
gccli stores your OAuth tokens in a keyring backend. Default is auto-detection (best available backend for your OS/environment).
List current auth state:
gccli auth statusPrint the raw OAuth2 access token (useful for scripting):
gccli auth tokenRemove stored credentials:
gccli auth removeBackends:
- auto (default) — picks the best backend for the platform
- keychain — macOS Keychain
- secret-service — Linux D-Bus (GNOME Keyring, KWallet)
- file — encrypted on-disk keyring (fallback)
Set backend via environment variable:
export GCCLI_KEYRING_BACKEND=fileOr in the config file (config.json):
{
"keyring_backend": "file"
}On Linux, if D-Bus is unavailable, gccli automatically falls back to the file backend.
For Garmin China accounts (garmin.cn):
export GCCLI_DOMAIN=garmin.cnOr in config.json:
{
"domain": "garmin.cn"
}The account is resolved in this order:
--accountflagGCCLI_ACCOUNTenvironment variable- Default account saved by
gccli auth login(stored inconfig.json)
# Via flag
gccli activities list --account you@example.com
# Via environment
export GCCLI_ACCOUNT=you@example.com
gccli activities list
# Via login default (no flag or env var needed)
gccli auth login you@example.com
gccli activities list- Default: human-friendly tables on stdout.
--plain: stable TSV on stdout (best for piping).--json/-j: JSON on stdout (best for scripting).- Human-facing messages (errors, warnings, info) go to stderr.
- Colors are enabled by default in TTY mode and disabled for
--jsonand--plain.
Config path:
- macOS:
~/Library/Application Support/gccli/config.json - Linux:
~/.config/gccli/config.json(or$XDG_CONFIG_HOME/gccli/config.json)
Example:
{
"default_account": "you@example.com",
"keyring_backend": "file",
"domain": "garmin.com"
}| Variable | Description |
|---|---|
GCCLI_ACCOUNT |
Default account email (overrides config file default) |
GCCLI_DOMAIN |
Garmin domain (garmin.com or garmin.cn) |
GCCLI_JSON |
Enable JSON output (1, true, yes) |
GCCLI_PLAIN |
Enable plain/TSV output (1, true, yes) |
GCCLI_COLOR |
Color mode: auto, always, never |
GCCLI_KEYRING_BACKEND |
Keyring backend: keychain, secret-service, file |
OAuth tokens are stored securely in your system's keyring:
- macOS: Keychain Access
- Linux: Secret Service (GNOME Keyring, KWallet)
- Fallback: Encrypted on-disk file store
The CLI uses github.com/99designs/keyring for secure storage.
Tokens are keyed by email (gccli:token:<email>) and never stored in plaintext files.
gccli auth login <email> # Log in via browser SSO
gccli auth login <email> --headless # Log in via email/password
gccli auth login <email> --headless --mfa-code <code> # With MFA
gccli auth status # Show auth state and token expiry
gccli auth token # Print OAuth2 access token
gccli auth export # Export credentials (for transfer to another machine)
gccli auth import <token> # Import credentials from another machine
gccli auth remove # Remove stored credentials# List and search
gccli activities list --limit 20 --start 0
gccli activities list --type running
gccli activities count
gccli activities search --start-date 2024-01-01 --end-date 2024-12-31
# View details
gccli activity summary <id>
gccli activity details <id>
gccli activity splits <id>
gccli activity typed-splits <id>
gccli activity split-summaries <id>
gccli activity weather <id>
gccli activity hr-zones <id>
gccli activity power-zones <id>
gccli activity exercise-sets <id>
gccli activity exercise-sets set <id> -e "BENCH_PRESS/BARBELL_BENCH_PRESS:12@20:d30:r60" -e "BENCH_PRESS/BARBELL_BENCH_PRESS:10@25:d25"
gccli activity gear <id>
# Download and upload
gccli activity download <id> --format fit
gccli activity download <id> --format gpx --output track.gpx
gccli activity upload ./activity.fit
# Create and modify
gccli activity create --name "Morning Run" --type running --duration 30m --distance 5000
gccli activity create --name "Upper Body" --type strength_training --duration 34m --date 2026-03-13T19:01:00
gccli activity rename <id> "New Name"
gccli activity retype <id> --type-id 1 --type-key running
gccli activity delete <id>
gccli activity delete <id> --force
# Activity types
gccli activities types # List all Garmin activity types# Daily summary
gccli health summary # Today
gccli health summary yesterday
gccli health summary 2024-06-15
gccli health summary 3d # 3 days ago
# Vitals
gccli health steps # Step chart for today
gccli health steps daily --start 2024-01-01 --end 2024-01-31
gccli health steps weekly --weeks 4
gccli health hr [date] # Heart rate
gccli health rhr [date] # Resting heart rate
gccli health floors [date] # Floors climbed
gccli health sleep [date] # Sleep data
gccli health respiration [date]
gccli health spo2 [date] # Blood oxygen
gccli health hrv [date] # Heart rate variability
# Stress and recovery
gccli health stress # Stress for today
gccli health stress weekly --weeks 4
gccli health body-battery [date]
gccli health body-battery range --start 2024-01-01 --end 2024-01-07
gccli health training-readiness [date]
gccli health training-status [date]
# Fitness metrics
gccli health fitness-age [date]
gccli health max-metrics [date] # VO2max and more
gccli health lactate-threshold
gccli health cycling-ftp
gccli health race-predictions
gccli health race-predictions range --start 2024-01-01 --end 2024-06-30
gccli health endurance-score [date]
gccli health hill-score [date]
gccli health intensity-minutes [date]
gccli health intensity-minutes weekly --start 2024-01-01 --end 2024-01-31
# Wellness events
gccli health events [date]
gccli health lifestyle [date]# View data
gccli body composition # Today
gccli body composition --start 2024-01-01 --end 2024-01-31
gccli body weigh-ins --start 2024-01-01 --end 2024-01-31
gccli body daily-weigh-ins [date]
# Add data
gccli body add-weight 75.5 --unit kg
gccli body add-weight 166.4 --unit lbs
gccli body add-composition 75.5 --body-fat 15.2 --muscle-mass 35.0
# Blood pressure
gccli body blood-pressure --start 2024-01-01 --end 2024-01-31
gccli body add-blood-pressure --systolic 120 --diastolic 80 --pulse 65
# Delete entries
gccli body delete-weight <pk> --date 2024-01-15
gccli body delete-blood-pressure <version> --date 2024-01-15gccli workouts list --limit 20
gccli workouts detail <id>
gccli workouts download <id> --output workout.fit
gccli workouts upload ./workout.json # See JSON structure below
gccli workouts update <id> ./workout.json # Replace an existing workout, keeping its ID
gccli workouts schedule add <id> <YYYY-MM-DD>
gccli workouts schedule list <YYYY-MM-DD>
gccli workouts schedule list --start 2024-06-01 --end 2024-06-30
gccli workouts schedule remove <schedule-id>
gccli workouts schedule remove <schedule-id> --force
gccli workouts delete <id>
# Create a running workout with pace targets
gccli workouts create "Easy 30min Run" --type run \
--step "warmup:5m@pace:5:30-6:00" \
--step "run:20m@pace:5:00-5:30" \
--step "cooldown:5m"
# Running with heart rate targets
gccli workouts create "HR Zone Run" --type run \
--step "warmup:10m" \
--step "run:20m@hr:140-160" \
--step "cooldown:10m"
# Cycling with power targets
gccli workouts create "FTP Intervals" --type bike \
--step "warmup:10m" \
--step "run:5m@power:250-280" \
--step "recovery:3m" \
--step "run:5m@power:250-280" \
--step "cooldown:10m"
# Imperial paces (miles)
gccli workouts create "Easy 30min Run" --type run \
--step "warmup:5m@pace:8:51-9:39" \
--step "run:20m@pace:8:03-8:51" \
--step "cooldown:5m" \
--unit mi
# Strength workout (no targets)
gccli workouts create "Full Body" --type strength \
--step "warmup:5m" \
--step "run:30m" \
--step "cooldown:5m"Updating a workout in place. gccli workouts update sends a full replacement, not a patch: whatever the file leaves out is gone from the workout. Start from the current server copy rather than from a hand-written fragment:
gccli workouts detail 123456 > workout.json
# edit workout.json
gccli workouts update 123456 workout.jsonThe workout keeps its ID and any calendar entries that point at it. workoutId in the file is set from the ID argument before the request goes out, so a payload written for upload works as-is; if the file already names a different workout, gccli warns and still updates the one given on the command line.
Workout JSON structure for gccli workouts upload:
{
"workoutName": "Tempo Run",
"sportType": {
"sportTypeId": 1,
"sportTypeKey": "running"
},
"workoutSegments": [
{
"segmentOrder": 1,
"sportType": {
"sportTypeId": 1,
"sportTypeKey": "running"
},
"workoutSteps": [
{
"type": "ExecutableStepDTO",
"stepOrder": 1,
"stepType": { "stepTypeId": 1, "stepTypeKey": "warmup" },
"endCondition": { "conditionTypeId": 2, "conditionTypeKey": "time" },
"endConditionValue": 600,
"targetType": { "workoutTargetTypeId": 1, "workoutTargetTypeKey": "no.target" }
},
{
"type": "ExecutableStepDTO",
"stepOrder": 2,
"stepType": { "stepTypeId": 3, "stepTypeKey": "interval" },
"endCondition": { "conditionTypeId": 2, "conditionTypeKey": "time" },
"endConditionValue": 1200,
"targetType": { "workoutTargetTypeId": 6, "workoutTargetTypeKey": "pace.zone" },
"targetValueOne": 3.333333,
"targetValueTwo": 3.030303
},
{
"type": "ExecutableStepDTO",
"stepOrder": 3,
"stepType": { "stepTypeId": 2, "stepTypeKey": "cooldown" },
"endCondition": { "conditionTypeId": 2, "conditionTypeKey": "time" },
"endConditionValue": 600,
"targetType": { "workoutTargetTypeId": 1, "workoutTargetTypeKey": "no.target" }
}
]
}
]
}Key reference values:
- Sport types: running (1), cycling (2), other (3), swimming (4), strength_training (5), cardio_training (6), yoga (7), pilates (8), hiit (9), multi_sport (10), mobility (11)
- Step types: warmup (1), cooldown (2), interval (3), recovery (4), rest (5), other (7)
- Target types: no.target (1), power.zone (2), cadence (3), heart.rate.zone (4), pace.zone (6)
- End condition: time (2) — value in seconds
- Pace values: in m/s (e.g., 5:00/km = 1000/300 = 3.333 m/s)
# List all exercise categories
gccli exercises list
# List exercises in a category
gccli exercises list -c BENCH_PRESS
# Full catalog as JSON
gccli exercises list --jsongccli courses list # List all courses
gccli courses favorites # List favorite courses
gccli courses detail <id> # View full course detail
gccli courses download <id> # Download course as FIT (default)
gccli courses download <id> --format gpx # Download course as GPX
gccli courses download <id> -o ride.fit # Download to a custom path
gccli courses import route.gpx # Import GPX as new course (default: cycling, private)
gccli courses import route.gpx --name "Ride" # Import with custom name
gccli courses import route.gpx --type hiking # Override activity type
gccli courses import route.gpx --privacy 1 # Import as public course
gccli courses send <course-id> <device-id> # Send course to a device
gccli courses delete <id> # Delete a course
gccli courses delete <id> -f # Delete without confirmationgccli devices list
gccli devices settings <device-id>
gccli devices primary
gccli devices last-used
gccli devices alarms
gccli devices solar <device-id> --start 2024-06-01 --end 2024-06-30gccli gear list
gccli gear stats <uuid>
gccli gear activities <uuid> --limit 20
gccli gear defaults
gccli gear link <uuid> <activity-id>
gccli gear unlink <uuid> <activity-id>gccli goals list
gccli goals list --status active
gccli badges earned
gccli badges available
gccli badges in-progress
gccli challenges list
gccli challenges badge
gccli recordsgccli profile
gccli profile settingsgccli hydration [date] # View hydration data
gccli hydration add 500 # Log 500ml of water
gccli hydration add 500 --date 2024-06-15gccli nutrition [date] # View daily food log
gccli nutrition meals [date] # View meal summaries
gccli nutrition settings [date] # View nutrition settingsgccli training plans --locale en
gccli training plan <id>gccli events list # List upcoming events
gccli events list --start 2024-01-01 --limit 50
gccli events list --sort eventDate_desc
gccli events delete <id> # Delete an event (with confirmation)
gccli events delete <id> -f # Delete without confirmation
# Add a race event
gccli events add --name "Berlin Marathon" --date 2026-09-27 --type running \
--race --location "Berlin, Germany" --distance 42.195km \
--time 09:15 --timezone Europe/Berlin
# Add an event with a goal and training priority
gccli events add --name "Spring 10K" --date 2026-05-10 --type running \
--race --distance 10km --goal 40m --trainingEvent flags:
- Required:
--name,--date(YYYY-MM-DD, today, +30d),--type(running, trail_running, cycling, gravel_cycling, mountain_biking, swimming, triathlon, multi_sport, hiking, walking, fitness_equipment, motorcycling, winter_sport, other) - Optional:
--race,--location,--time(HH:MM),--timezone,--distance(e.g. 10km, 26.2mi, 400m),--private,--note,--url - Goal & training priority:
--goal(duration, e.g. 50m, 1h30m, 2400s),--primaryor--training(mutually exclusive)
gccli wellness menstrual-cycle --start-date 2024-01-01 --end-date 2024-03-31
gccli wellness menstrual-summary --start-date 2024-01-01 --end-date 2024-03-31
gccli wellness pregnancy-summarygccli reload # Reload today's data
gccli reload 2024-06-15 # Reload specific dateGenerate completion scripts for your shell:
gccli completion bash
gccli completion zsh
gccli completion fish
gccli completion powershellTo enable completions, add the appropriate line to your shell profile:
Bash (~/.bashrc):
source <(gccli completion bash)Zsh (~/.zshrc):
source <(gccli completion zsh)Fish (~/.config/fish/config.fish):
gccli completion fish | sourcePowerShell ($PROFILE):
gccli completion powershell | Invoke-ExpressionCompletions cover all commands, subcommands, flags, and enum flag values (e.g. --color auto|always|never).
Human-readable output with colors:
$ gccli activities list --limit 3
ID NAME TYPE DATE
123456789 Morning Run running 2024-06-15
123456780 Evening Walk walking 2024-06-14
123456771 Cycling Workout cycling 2024-06-13Machine-readable output for scripting and automation:
$ gccli --json activities list --limit 3
[
{
"activityId": 123456789,
"activityName": "Morning Run",
"activityType": {"typeKey": "running"},
"startTimeLocal": "2024-06-15 07:30:00"
},
...
]Data goes to stdout, errors and progress to stderr for clean piping:
gccli --json activities list --limit 100 | jq '.[] | select(.activityType.typeKey == "running")'Stable, tab-separated output for scripting:
$ gccli --plain activities list --limit 3
123456789 Morning Run running 2024-06-15
123456780 Evening Walk walking 2024-06-14
123456771 Cycling Workout cycling 2024-06-13gccli --json activities search --start-date 2024-05-15 --end-date 2024-06-15 --limit 100 | \
jq -r '.[] | select(.activityType.typeKey == "running") | .activityId' | \
while read id; do
gccli activity download "$id" --format gpx --output "run_${id}.gpx"
donegccli --json health summary | jq '{
steps: .totalSteps,
calories: .totalKilocalories,
distance_km: (.totalDistanceMeters / 1000),
active_minutes: .moderateIntensityMinutes + .vigorousIntensityMinutes
}'gccli --json body weigh-ins --start 2024-01-01 --end 2024-06-30 | \
jq -r '.dailyWeightSummaries[] | [.summaryDate, .weight.value] | @tsv'latest=$(gccli --json activities list --limit 1 | jq -r '.[0].activityId')
gccli activity download "$latest" --format fitAll commands support these flags:
| Flag | Description |
|---|---|
--account <email> |
Account to use (overrides GCCLI_ACCOUNT and config default) |
--json, -j |
Output JSON to stdout |
--plain |
Output stable TSV to stdout |
--color <mode> |
Color mode: auto, always, never (default: auto) |
--version |
Print version information |
--help |
Show help for any command |
Many commands accept flexible date formats:
| Format | Example | Description |
|---|---|---|
YYYY-MM-DD |
2024-06-15 |
Explicit date |
today |
Today's date | |
yesterday |
Yesterday's date | |
Nd |
3d, 7d |
N days ago |
After cloning, install tools:
make toolsBuild, test, and lint:
make build # Build to ./bin/gccli
make test # Unit tests with race detector
make lint # golangci-lint
make fmt # Format with goimports + gofumpt
make ci # Full CI gate (fmt-check + lint + test)Run individual tests:
go test -run TestFuncName ./internal/pkg/...End-to-end tests hit the real Garmin Connect API. They require a .env file with test credentials:
cp .env.example .env
# Edit .env with your test account credentials
make test-e2eWarning: E2E tests create and delete activities/workouts. Use a dedicated test account.
Inspired by gogcli. API coverage based on python-garminconnect (113 API methods).
Platforms: macOS (amd64, arm64), Linux (amd64, arm64). macOS builds with CGO enabled for Keychain access.
MIT
- Garmin Connect
- python-garminconnect — API reference
- gogcli — architecture inspiration
Architecture inspired by gogcli by Peter Steinberger. API coverage modeled after python-garminconnect by Ron Klinkien.
Not affiliated with Garmin. Garmin and Garmin Connect are trademarks of Garmin Ltd. or its subsidiaries.