An open API service indexing awesome lists of open source software.

https://github.com/bpauli/gccli

A command-line interface for Garmin Connect — activities, health, courses, workouts, devices, and more — with JSON output and secure credential storage.
https://github.com/bpauli/gccli

cli fitness garmin garmin-connect golang health

Last synced: 3 months ago
JSON representation

A command-line interface for Garmin Connect — activities, health, courses, workouts, devices, and more — with JSON output and secure credential storage.

Awesome Lists containing this project

README

          

# gccli — Garmin Connect in your terminal.

[![ci](https://github.com/bpauli/gccli/actions/workflows/ci.yml/badge.svg)](https://github.com/bpauli/gccli/actions/workflows/ci.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/bpauli/gccli)](https://goreportcard.com/report/github.com/bpauli/gccli)
[![Docs](https://img.shields.io/badge/docs-gccli.sh-blue)](https://gccli.sh)
[![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-support-yellow?logo=buymeacoffee)](https://buymeacoffee.com/mail7j)

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.

## Features

- **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
- **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, create with sport types and targets (pace/HR/power/cadence), schedule (add/list/remove), delete
- **Courses** — list courses, view favorites, get full course detail, 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
- **Multiple accounts** — manage multiple Garmin accounts via `--account` flag
- **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

## Installation

### Homebrew (macOS / Linux)

```bash
brew install bpauli/tap/gccli
```

### Build from Source

Requires Go 1.24+.

```bash
git clone https://github.com/bpauli/gccli.git
cd gccli
make build
```

The binary is output to `./bin/gccli`.

Run:

```bash
./bin/gccli --help
```

Help:

- `gccli --help` shows top-level command groups.
- Drill down with `gccli --help` (and deeper subcommands).
- Make shortcut: `make run -- --help` (or `make run -- activities --help`).

## Quick Start

### 1. Log In (Browser SSO)

```bash
gccli auth login you@example.com
```

This opens your browser for Garmin SSO authentication. The OAuth tokens are stored securely in your system keyring.

### 2. Log In (Headless)

For servers or environments without a browser:

```bash
gccli auth login you@example.com --headless
```

You'll be prompted for your password. If your account has two-factor authentication:

```bash
gccli auth login you@example.com --headless --mfa-code 123456
```

Login automatically saves your email as the default account, so you don't need to pass `--account` or set `GCCLI_ACCOUNT` on every command.

### 3. Test Authentication

```bash
gccli auth status
gccli activities list --limit 5
```

## Authentication & Secrets

### Accounts and Tokens

`gccli` stores your OAuth tokens in a keyring backend. Default is auto-detection (best available backend for your OS/environment).

List current auth state:

```bash
gccli auth status
```

Print the raw OAuth2 access token (useful for scripting):

```bash
gccli auth token
```

Remove stored credentials:

```bash
gccli auth remove
```

### Keyring Backend

Backends:

- **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:

```bash
export GCCLI_KEYRING_BACKEND=file
```

Or in the config file (`config.json`):

```json
{
"keyring_backend": "file"
}
```

On Linux, if D-Bus is unavailable, `gccli` automatically falls back to the file backend.

### Garmin Domain (China)

For Garmin China accounts (garmin.cn):

```bash
export GCCLI_DOMAIN=garmin.cn
```

Or in `config.json`:

```json
{
"domain": "garmin.cn"
}
```

## Configuration

### Account Selection

The account is resolved in this order:

1. `--account` flag
2. `GCCLI_ACCOUNT` environment variable
3. Default account saved by `gccli auth login` (stored in `config.json`)

```bash
# 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
```

### Output

- 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 `--json` and `--plain`.

### Config File

Config path:

- macOS: `~/Library/Application Support/gccli/config.json`
- Linux: `~/.config/gccli/config.json` (or `$XDG_CONFIG_HOME/gccli/config.json`)

Example:

```json
{
"default_account": "you@example.com",
"keyring_backend": "file",
"domain": "garmin.com"
}
```

### Environment Variables

| 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` |

## Security

### Credential Storage

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](https://github.com/99designs/keyring) for secure storage.

Tokens are keyed by email (`gccli:token:`) and never stored in plaintext files.

## Commands

### Authentication

```bash
gccli auth login # Log in via browser SSO
gccli auth login --headless # Log in via email/password
gccli auth login --headless --mfa-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 # Import credentials from another machine
gccli auth remove # Remove stored credentials
```

### Activities

```bash
# 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
gccli activity details
gccli activity splits
gccli activity typed-splits
gccli activity split-summaries
gccli activity weather
gccli activity hr-zones
gccli activity power-zones
gccli activity exercise-sets
gccli activity exercise-sets set -e "BENCH_PRESS/BARBELL_BENCH_PRESS:12@20:d30:r60" -e "BENCH_PRESS/BARBELL_BENCH_PRESS:10@25:d25"
gccli activity gear

# Download and upload
gccli activity download --format fit
gccli activity download --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 "New Name"
gccli activity retype --type-id 1 --type-key running
gccli activity delete
gccli activity delete --force
```

### Health Data

```bash
# 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]
```

### Body Composition

```bash
# 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 --date 2024-01-15
gccli body delete-blood-pressure --date 2024-01-15
```

### Workouts

```bash
gccli workouts list --limit 20
gccli workouts detail
gccli workouts download --output workout.fit
gccli workouts upload ./workout.json # See JSON structure below
gccli workouts schedule add
gccli workouts schedule list
gccli workouts schedule list --start 2024-06-01 --end 2024-06-30
gccli workouts schedule remove
gccli workouts schedule remove --force
gccli workouts delete

# 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"
```

**Workout JSON structure** for `gccli workouts upload`:

```json
{
"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)

### Exercise Catalog

```bash
# 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 --json
```

### Courses

```bash
gccli courses list # List all courses
gccli courses favorites # List favorite courses
gccli courses detail # View full course detail
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 # Send course to a device
gccli courses delete # Delete a course
gccli courses delete -f # Delete without confirmation
```

### Devices

```bash
gccli devices list
gccli devices settings
gccli devices primary
gccli devices last-used
gccli devices alarms
gccli devices solar --start 2024-06-01 --end 2024-06-30
```

### Gear

```bash
gccli gear list
gccli gear stats
gccli gear activities --limit 20
gccli gear defaults
gccli gear link
gccli gear unlink
```

### Goals, Badges & Challenges

```bash
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 records
```

### Profile

```bash
gccli profile
gccli profile settings
```

### Hydration

```bash
gccli hydration [date] # View hydration data
gccli hydration add 500 # Log 500ml of water
gccli hydration add 500 --date 2024-06-15
```

### Nutrition

```bash
gccli nutrition [date] # View daily food log
gccli nutrition meals [date] # View meal summaries
gccli nutrition settings [date] # View nutrition settings
```

### Training Plans

```bash
gccli training plans --locale en
gccli training plan
```

### Events

```bash
gccli events list # List upcoming events
gccli events list --start 2024-01-01 --limit 50
gccli events list --sort eventDate_desc
gccli events delete # Delete an event (with confirmation)
gccli events delete -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 --training
```

**Event 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), `--primary` or `--training` (mutually exclusive)

### Wellness

```bash
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-summary
```

### Data Reload

```bash
gccli reload # Reload today's data
gccli reload 2024-06-15 # Reload specific date
```

### Shell Completion

Generate completion scripts for your shell:

```bash
gccli completion bash
gccli completion zsh
gccli completion fish
gccli completion powershell
```

To enable completions, add the appropriate line to your shell profile:

**Bash** (`~/.bashrc`):
```bash
source <(gccli completion bash)
```

**Zsh** (`~/.zshrc`):
```bash
source <(gccli completion zsh)
```

**Fish** (`~/.config/fish/config.fish`):
```fish
gccli completion fish | source
```

**PowerShell** (`$PROFILE`):
```powershell
gccli completion powershell | Invoke-Expression
```

Completions cover all commands, subcommands, flags, and enum flag values (e.g. `--color auto|always|never`).

## Output Formats

### Table (default)

Human-readable output with colors:

```bash
$ 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-13
```

### JSON

Machine-readable output for scripting and automation:

```bash
$ 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:

```bash
gccli --json activities list --limit 100 | jq '.[] | select(.activityType.typeKey == "running")'
```

### Plain (TSV)

Stable, tab-separated output for scripting:

```bash
$ 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-13
```

## Examples

### Export all runs from the last month

```bash
gccli --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"
done
```

### Daily health check script

```bash
gccli --json health summary | jq '{
steps: .totalSteps,
calories: .totalKilocalories,
distance_km: (.totalDistanceMeters / 1000),
active_minutes: .moderateIntensityMinutes + .vigorousIntensityMinutes
}'
```

### Track weight over time

```bash
gccli --json body weigh-ins --start 2024-01-01 --end 2024-06-30 | \
jq -r '.dailyWeightSummaries[] | [.summaryDate, .weight.value] | @tsv'
```

### Download the latest activity

```bash
latest=$(gccli --json activities list --limit 1 | jq -r '.[0].activityId')
gccli activity download "$latest" --format fit
```

## Global Flags

All commands support these flags:

| Flag | Description |
| --- | --- |
| `--account ` | Account to use (overrides `GCCLI_ACCOUNT` and config default) |
| `--json`, `-j` | Output JSON to stdout |
| `--plain` | Output stable TSV to stdout |
| `--color ` | Color mode: `auto`, `always`, `never` (default: `auto`) |
| `--version` | Print version information |
| `--help` | Show help for any command |

## Date Format Reference

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 |

## Development

After cloning, install tools:

```bash
make tools
```

Build, test, and lint:

```bash
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:

```bash
go test -run TestFuncName ./internal/pkg/...
```

### E2E Tests (Live Garmin API)

End-to-end tests hit the real Garmin Connect API. They require a `.env` file with test credentials:

```bash
cp .env.example .env
# Edit .env with your test account credentials
make test-e2e
```

**Warning:** E2E tests create and delete activities/workouts. Use a dedicated test account.

## Architecture

Inspired by [gogcli](https://github.com/steipete/gogcli). API coverage based on [python-garminconnect](https://github.com/cyberjunky/python-garminconnect) (113 API methods).

Platforms: macOS (amd64, arm64), Linux (amd64, arm64). macOS builds with CGO enabled for Keychain access.

## License

MIT

## Links

- [Garmin Connect](https://connect.garmin.com)
- [python-garminconnect](https://github.com/cyberjunky/python-garminconnect) — API reference
- [gogcli](https://github.com/steipete/gogcli) — architecture inspiration

## Credits

Architecture inspired by [gogcli](https://github.com/steipete/gogcli) by Peter Steinberger. API coverage modeled after [python-garminconnect](https://github.com/cyberjunky/python-garminconnect) by Ron Klinkien.

---

Not affiliated with Garmin. Garmin and Garmin Connect are trademarks of Garmin Ltd. or its subsidiaries.