{"id":46906085,"url":"https://github.com/arvarik/whoop-go","last_synced_at":"2026-03-11T01:12:59.789Z","repository":{"id":340468313,"uuid":"1166161895","full_name":"arvarik/whoop-go","owner":"arvarik","description":"Lightweight whoop api client written in Go","archived":false,"fork":false,"pushed_at":"2026-02-25T03:45:50.000Z","size":6772,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-25T06:23:17.959Z","etag":null,"topics":["client","go","whoop"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arvarik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-24T23:58:02.000Z","updated_at":"2026-02-25T03:45:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/arvarik/whoop-go","commit_stats":null,"previous_names":["arvarik/whoop-go"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/arvarik/whoop-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arvarik%2Fwhoop-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arvarik%2Fwhoop-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arvarik%2Fwhoop-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arvarik%2Fwhoop-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arvarik","download_url":"https://codeload.github.com/arvarik/whoop-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arvarik%2Fwhoop-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30365231,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T21:41:54.280Z","status":"ssl_error","status_checked_at":"2026-03-10T21:40:59.357Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["client","go","whoop"],"created_at":"2026-03-11T01:12:59.355Z","updated_at":"2026-03-11T01:12:59.781Z","avatar_url":"https://github.com/arvarik.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# whoop-go\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/arvarik/whoop-go/whoop.svg)](https://pkg.go.dev/github.com/arvarik/whoop-go/whoop)\n[![CI](https://github.com/arvarik/whoop-go/actions/workflows/ci.yml/badge.svg)](https://github.com/arvarik/whoop-go/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Go Report Card](https://goreportcard.com/badge/github.com/arvarik/whoop-go)](https://goreportcard.com/report/github.com/arvarik/whoop-go)\n\n`whoop-go` is a production-grade, highly robust Go client library for integrating with the [WHOOP API v2](https://developer-dashboard.whoop.com).\n\nEngineered with resiliency in mind, it natively handles strict WHOOP rate limits (100 req/min) through transparent token buckets and exponential backoff jitter. It securely extracts HTTP webhooks using HMAC-SHA256 validations, and surfaces strongly-typed structs mapping the complex WHOOP domain (Cycles, Workouts, Sleep, and Recovery) via seamless pagination iterators.\n\n**Disclaimer:** This is an unofficial open-source library and is not affiliated with, endorsed by, or supported by WHOOP.\n\n## Features\n\n- **Built-in Resilience**: Implements an intrinsic thread-safe token bucket rate-limiter enforcing the 100 req/min and 10,000 req/day WHOOP API quotas. Automatically intercepts HTTP `429 Too Many Requests` responses, sleeping utilizing randomized exponential backoffs before retrying safely.\n- **Webhook Verifier**: Features `whoop.ParseWebhook(r, secret)`, dynamically digesting inbound HTTP requests, safely streaming payloads, validating `X-Whoop-Signature` HMAC-SHA256 authenticity hashes without memory leaks, and returning structured skinny webhook types (`workout.updated`, `cycle.updated`, etc.).\n- **Iterator Pagination**: Converts cumbersome `next_token` URL query cursor traversals into a deeply idiomatic Go iterator pattern utilizing `.NextPage(ctx)`.\n- **Zero External Dependencies**: Outside of the foundational Golang `golang.org/x/time/rate` token bucket algorithm, the client is strictly built upon Go standard primitives (`net/http`, `crypto/hmac`).\n\n## Component Architecture\n\nThe library is modularized by functional domains to provide strict operational boundaries:\n\n- `client.go`: Centralizes the configurable HTTP `Client`, injects global functional options (`WithToken`, `WithBackoffBase`), and integrates the `rateLimiter`.\n- `ratelimit.go`: Enforces concurrency-safe request throttling.\n- `webhooks.go`: Provides high-performance, single-pass webhook stream consumption and signature validation.\n- `profile.go`: Manages athlete basic profiles and highly granular `BodyMeasurement` data.\n- `cycle.go`: Maps the overarching physiological day with calculated `Strain` and `HeartRate` arrays.\n- `sleep.go` \u0026 `recovery.go`: Exposes complex sleep staging, restorative data markers, and systemic nervous recovery scorings.\n- `workout.go`: Maps chronological activity zone durations and absolute metric loads.\n\n## Installation\n\nYou need Go `1.22` or higher installed.\n\n```bash\ngo get github.com/arvarik/whoop-go/whoop\n```\n\n## Quick Start: The Webhook Example\n\nThe repository includes a fully-functional Webhook + REST architecture example at [`cmd/example/main.go`](cmd/example/main.go). This script demonstrates how to securely parse live webhooks and trigger a secondary data scrape pipeline instantly.\n\n### Running the Example\n```bash\n# Copy the example .env and fill in your credentials\ncp .env.example .env\nvim .env   # Add your WHOOP_OAUTH_TOKEN and WHOOP_WEBHOOK_SECRET\nsource .env\n\n# Start the Webhook Listener on Port 8080\nmake build-local\n./bin/example\n```\n\n### Getting an OAuth Token\n\nThe WHOOP API uses the OAuth 2.0 Authorization Code flow. The [`cmd/auth/main.go`](cmd/auth/main.go) helper script handles the full flow, including **automatic token refresh** so you only need to sign in once.\n\n1. Ensure your application has `http://localhost:8081/callback` added as a **Redirect URI** in the [WHOOP Developer Dashboard](https://developer-dashboard.whoop.com). If you're using a different redirect URI, set `WHOOP_REDIRECT_URI`.\n2. Export your credentials:\n```bash\nexport WHOOP_CLIENT_ID=\"your_client_id\"\nexport WHOOP_CLIENT_SECRET=\"your_client_secret\"\n# Optional: only needed if your redirect URI differs from http://localhost:8081/callback\n# export WHOOP_REDIRECT_URI=\"http://localhost:3000/callback\"\n```\n3. Run the auth helper:\n```bash\ngo run cmd/auth/main.go\n```\n4. **First run**: Open the printed URL in your browser, sign in, and authorize. The script saves your session to `.whoop_token.json`.\n5. **Subsequent runs**: The script automatically refreshes your token using the saved session — no browser login needed.\n\n\u003e **Note:** Access tokens expire after **1 hour**. Simply re-run the script and it will silently refresh without opening a browser. The `.whoop_token.json` file is gitignored and should never be committed.\n\n### Verifying Credentials with cURL\n\nBefore building an integration, you can validate your OAuth token directly against the WHOOP API:\n\n```bash\n# Set your token\nexport WHOOP_OAUTH_TOKEN=\"your_oauth2_token_here\"\n\n# 1. Fetch your basic profile\ncurl -s -H \"Authorization: Bearer $WHOOP_OAUTH_TOKEN\" \\\n  \"https://api.prod.whoop.com/developer/v2/user/profile/basic\" | jq\n\n# 2. Fetch your body measurements\ncurl -s -H \"Authorization: Bearer $WHOOP_OAUTH_TOKEN\" \\\n  \"https://api.prod.whoop.com/developer/v2/user/measurement/body\" | jq\n\n# 3. List recent physiological cycles (last 10)\ncurl -s -H \"Authorization: Bearer $WHOOP_OAUTH_TOKEN\" \\\n  \"https://api.prod.whoop.com/developer/v2/cycle?limit=10\" | jq\n\n# 4. List recent workouts\ncurl -s -H \"Authorization: Bearer $WHOOP_OAUTH_TOKEN\" \\\n  \"https://api.prod.whoop.com/developer/v2/activity/workout?limit=10\" | jq\n\n# 5. List recent sleep events\ncurl -s -H \"Authorization: Bearer $WHOOP_OAUTH_TOKEN\" \\\n  \"https://api.prod.whoop.com/developer/v2/activity/sleep?limit=10\" | jq\n\n# 6. List recent recovery scores\ncurl -s -H \"Authorization: Bearer $WHOOP_OAUTH_TOKEN\" \\\n  \"https://api.prod.whoop.com/developer/v2/recovery?limit=10\" | jq\n```\n\n### Under the Hood\n1. A WHOOP event triggers your `webhook.updated` skinny payload to exactly `:8080/whoop/webhook`.\n2. The `whoop.ParseWebhook()` helper validates the signature and asserts the `event.Type`.\n3. An explicit asynchronous `processWorkout()` GoRoutine utilizes `client.Workout.GetByID(ctx, event.ID)` to query the full `Workout` schema for local logging and storage!\n\n## Example API Calls\n\nThe library provides individual services attached to the core `Client`. Below are comprehensive examples showing basic interactions.\n\n### 1. Initializing the Client\n\nConfiguration is heavily customizable utilizing the Functional Options pattern.\n\n```go\nimport \"github.com/arvarik/whoop-go/whoop\"\n\nclient := whoop.NewClient(\n    whoop.WithToken(\"your_oauth_token\"),\n    whoop.WithMaxRetries(5),               // Automatically retry failures or 429 limits up to 5 times\n    whoop.WithBaseURL(\"https://custom.proxy.example.com\"), // Optional: override base URL\n)\n```\n\n### 2. Validating Webhooks\n\n```go\nhttp.HandleFunc(\"/whoop/webhook\", func(w http.ResponseWriter, r *http.Request) {\n    event, err := whoop.ParseWebhook(r, \"my_webhook_secret_key\")\n    if err != nil {\n        log.Printf(\"Fraudulent webhook prevented: %v\", err)\n        w.WriteHeader(http.StatusUnauthorized)\n        return\n    }\n\n    log.Printf(\"Received genuine webhook event! Type: %s, ID: %s\", event.Type, event.ID)\n})\n```\n\n### 3. Fetching Cycles via Iterator Pagination\n\nThe WHOOP API caps returns at 50 arrays. You sequentially traverse all history easily via the `NextPage(ctx)` cursor logic.\n\n```go\n// Fetch the initial block of Cycles\npage, err := client.Cycle.List(ctx, \u0026whoop.ListOptions{Limit: 10})\nif err != nil {\n    log.Fatal(err)\n}\n\nfor {\n    // Process current batch\n    for _, cycle := range page.Records {\n        fmt.Printf(\"Cycle: %d, Strain: %.1f\\n\", cycle.ID, cycle.Score.Strain)\n    }\n\n    // Traverse pagination seamlessly!\n    page, err = page.NextPage(ctx)\n    if err != nil {\n        if errors.Is(err, whoop.ErrNoNextPage) {\n            break // Done!\n        }\n        log.Fatal(err)\n    }\n}\n```\n\n## Local Development / First Time Setup\n\nIf you are contributing to this library, you should run the `setup` command immediately after cloning. This automatically configures standard Git hooks to invoke the Go linter before allowing commits:\n\n```bash\n# Sets `core.hooksPath` to `.githooks` enabling the pre-commit action automatically\nmake setup\n```\n\nRun test coverage utilizing mocked REST architectures safely disconnected from Live APIs:\n\n```bash\nmake test\n```\n\n## Migrating from v0.2.0 → v0.3.0 (v1 → v2 API)\n\nAs of `v0.3.0`, this library targets the **WHOOP API v2**. The v1 API has been deprecated by WHOOP — several v1 endpoints (Sleep, Workout, Recovery) now return HTTP 404.\n\nKey breaking changes:\n- `Sleep.ID` and `Workout.ID` changed from `int` → `string` (UUIDs)\n- `Recovery.SleepID` changed from `int` → `string` (UUID)\n- `WebhookEvent.ID` changed from `int` → `string` (UUID)\n- Workout zone durations JSON key renamed from `zone_duration` → `zone_durations`\n- New fields added: `ScoreState`, `SportName`, `V1ID`, `CycleID` (on Sleep), and `ScopeOffline`\n\n## License\n\nThis project is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farvarik%2Fwhoop-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farvarik%2Fwhoop-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farvarik%2Fwhoop-go/lists"}