Go client library for the Garmin Connect API.
go get github.com/barnes-c/go-garminconnectRequires Go 1.24+.
ctx := context.Background()
client := garminconnect.NewClient(os.Getenv("HOME") + "/.garminconnect/tokens.json")
if err := client.Login(ctx, "user@example.com", "password"); err != nil {
log.Fatal(err)
}
summary, err := client.UserSummary(ctx,time.Now())
if err != nil {
log.Fatal(err)
}
fmt.Printf("Steps today: %d\n", summary.TotalSteps)Login loads a cached token from disk, refreshes it if expired, or performs a full SSO login.
If the account has MFA enabled, provide a callback via WithMFAPrompt that returns the verification code. Without it, Login returns ErrMFARequired.
client := garminconnect.NewClient(os.Getenv("HOME")+"/.garminconnect/tokens.json",
garminconnect.WithMFAPrompt(func() (string, error) {
fmt.Print("MFA code: ")
var code string
_, err := fmt.Scan(&code)
return code, err
}),
)
if err := client.Login(context.Background(), "user@example.com", "password"); err != nil {
log.Fatal(err)
}Other options: WithHTTPClient(hc), WithToken(accessToken), WithDisplayName(name).
The full method reference lives on pkg.go.dev, generated from the source. The client covers:
| Area | What it exposes |
|---|---|
| Wellness | Steps, Body Battery, stress, floors, hydration, respiration, SpO₂, intensity minutes, blood pressure |
| Heart rate | Intraday and resting heart rate |
| Sleep | Sleep stages, sleep statistics, and HRV |
| Activities | Listing, detail, splits, HR/power zones, exercise sets, weather, personal records, edit/delete, and download & upload |
| Workouts | Saved workouts, scheduling, and download & upload |
| Training | Readiness & status, VO₂ Max, endurance/hill scores, race predictions, running tolerance, lactate threshold, fitness age, cycling FTP, HR/power zone settings |
| Body composition | Weigh-ins and body composition history |
| Goals | Goals, badges, and challenges |
| Devices & gear | Registered devices, settings, solar data, and gear tracking |
| Profile | User profile, settings, and unit system |
| Women's health | Menstrual cycle and pregnancy data |
| Nutrition | Food log, meals, and goals |
| Golf | Scorecards and shot data |
Activity and workout files support FIT, GPX, TCX, KML, and CSV (see the Format* constants).
import "errors"
acts, err := client.Activities(context.Background(), 0, 10)
switch {
case errors.Is(err, garminconnect.ErrUnauthorized):
// automatic token refresh failed — call Login again to re-authenticate
case errors.Is(err, garminconnect.ErrMFARequired):
// account requires MFA — provide WithMFAPrompt on client creation
case errors.Is(err, garminconnect.ErrRateLimit):
// back off and retry
case errors.Is(err, garminconnect.ErrNoData):
// no records for the query
}
var apiErr *garminconnect.APIError
if errors.As(err, &apiErr) {
fmt.Println(apiErr.StatusCode, apiErr.Path)
}Tests replay recorded HTTP interactions — no credentials needed.
make testTo re-record cassettes against a live account:
GARMIN_EMAIL=you@example.com GARMIN_PASSWORD=secret go run ./internal/record
# Only record missing cassettes:
go run ./internal/record --missingThe tool logs in once and records one cassette per test. PII is stripped inline as each cassette is written (the recorder's BeforeSaveHook calls internal/sanitize), so cassettes are never persisted unsanitized.
A scheduled smoke-test workflow runs internal/smoke weekly against the live API to catch drift the cassettes can't see. It can also be run locally:
GARMIN_EMAIL=you@example.com GARMIN_PASSWORD=secret go run ./internal/smoke- garmin_exporter — OTel-native Prometheus exporter for Garmin Connect
- garmin-datasource — Grafana data source for Garmin Connect
Using this client in your own project? Open a PR to add it here.