andyreagan.com

Whoop

Backlinks: Fitness Tech | Sleep monitor | Wearables > Whoop

Whoop whoop! Super accurate fitness and recovery tracker.

Bicep band is a MUST.

See Whoop Heart Rate Accuracy.

Looks like I've used (on and off) <2020-10-21 Wed>–current, using bands 3 through 5.

Whoop band 3

My first band. Lives in a drawer in the basement.

Habit tracking

Used it consistently for habit tracking for a few years. It's one of those things that you get out of it what you put into it.

The monthly impact reports are pretty great.

Whoop labs

Did this once. Cool experience. Wrote about it on Reddit, I think.

Wore at least two whoops on each wrist and a Polar H10 as a reference. Did some light weightlifting and running.

Code to pull from official API

Two-phase OAuth: build an authorization URL, complete consent in the browser, then exchange the returned code for an access token.

Each python source block is executed by ob-python via the org-babel-python-command set in the file-local variables at the bottom of this header uv run --no-project --with requests python -. uv provisions an ephemeral environment with requests and pipes the block body in over stdin. No virtualenv setup is required.

Credentials come from 1Password via op read.

Step 1: build the authorization URL

Open the printed URL in a browser, authorize, and copy the code query parameter from the redirect.

import secrets
import subprocess
import urllib.parse


def op_read(ref):
    return subprocess.check_output(['op', 'read', ref], text=True).strip()


client_id = op_read('op://Private/Whoop API/client_id')

auth_endpoint = 'https://api.prod.whoop.com/oauth/oauth2/auth'
query_params = {
    'response_type': 'code',
    'state': secrets.token_urlsafe(16),
    'client_id': client_id,
    'scope': 'offline read:recovery read:sleep read:workout',
    'redirect_uri': 'http://localhost:8484/callback',
}

url_parts = list(urllib.parse.urlparse(auth_endpoint))
query = dict(urllib.parse.parse_qsl(url_parts[4]))
query.update(query_params)
url_parts[4] = urllib.parse.urlencode(query)
print(urllib.parse.urlunparse(url_parts))

Step 2: exchange the code for an access token

Paste the authorization code into CODE below, then C-c C-c in the block.

import subprocess

import requests


def op_read(ref):
    return subprocess.check_output(['op', 'read', ref], text=True).strip()


client_id = op_read('op://Private/Whoop API/client_id')
client_secret = op_read('op://Private/Whoop API/client_secret')
code = op_read('op://Private/Whoop API/code')

response = requests.post(
    'https://api.prod.whoop.com/oauth/oauth2/token',
    headers={'Content-Type': 'application/x-www-form-urlencoded'},
    data={
        'grant_type': 'authorization_code',
        'code': code,
        'redirect_uri': 'http://localhost:8484/callback',
        'client_id': client_id,
        'client_secret': client_secret,
    },
)
print(response.json())

Step 3: Pull data

import json
import subprocess
import requests

def op_read(ref):
    return subprocess.check_output(['op', 'read', ref], text=True).strip()

access_token = op_read('op://Private/Whoop API/access_token')

response = requests.get(
    'https://api.prod.whoop.com/developer/v2/recovery',
    headers={'Authorization': f'Bearer {access_token}'},
    params={'limit': 5},
)

data = response.json()
data.pop('next_token', None)

print(json.dumps(data, indent=2))

FitIQ

https://fitiq.io/

Cool add-on analytics company site. $5/mo. I should build something like this. Ran the free trial, the coolest part was the longer-timeframe charts.

Whoomp

Reverse engineering of the whoop band bluetooth interface. Likely illegal.

Repository: https://github.com/jogolden/whoomp Try it out at https://jogolden.github.io/whoomp/

Purchase history

Date Invoice/Order ID Description Amount
10/17/2020 B3083415 Strap 3.0 + 18-Month Subscription 321.37
10/21/2020 E7B92FA0-0001 Trial period for Whoop Membership 3.0 0.00
11/02/2020 W170297 Core Bicep Band (Thunder L/XL) 32.99
12/20/2021 W389941 Superknit Bicep Band 4.0 61.37
08/14/2022 E7B92FA0-0002 Whoop Membership (monthly) 31.88
09/14/2022 E7B92FA0-0003 Whoop Membership (monthly) 31.88
10/14/2022 E7B92FA0-0004 Whoop Membership (monthly) 31.88
11/14/2022 E7B92FA0-0005 Whoop Membership (monthly) 31.88
12/14/2022 E7B92FA0-0006 Whoop Membership (monthly) 31.88
01/14/2023 E7B92FA0-0007 Whoop Membership (monthly) 31.88
02/14/2023 E7B92FA0-0008 Whoop Membership (monthly) 31.88
03/14/2023 E7B92FA0-0009 Whoop Membership (monthly) 31.88
04/14/2023 E7B92FA0-0010 Whoop Membership (monthly) 31.88
05/14/2023 E7B92FA0-0011 Whoop Membership (monthly) 31.88
06/14/2023 E7B92FA0-0012 Whoop Membership (monthly) 31.88
07/14/2023 E7B92FA0-0013 Whoop Membership (monthly) 31.88
08/07/2023 W767432 Battery Pack 4.0 61.05
08/07/2023 W767434 Moss Superknit Bicep Band 61.37
08/07/2023 E7B92FA0-0014 Trial period for Whoop Membership 0.00
08/14/2023 E7B92FA0-0015 Whoop Membership (annual) 253.94
08/14/2024 E7B92FA0-0016 Whoop Membership (annual) 253.94
09/28/2024 W1114938 Replacement WHOOP 4.0 79.69
05/15/2025 B44810861 MG Device + Life 12mo + CoreKnit Band 442.49
01/08/2026 B58015616 MG SuperKnit Band Glade (free) 0.00
11/14/2025 Advanced Labs Subscription 349.00
    Total 2540.65

Whoop band 4

I haven't noticed a big difference from the 3.0, though the health metrics have picked up sickness. Waterproof charger is a plus. I couldn't passed on getting new bands ($$).

I lost the band at one point, and given the remaining time on the plan, it was worth buying a new band. I also think I had to buy one new battery.

Django app to connect with Whoop

Whoop 5/MG

  • Note taken on [2025-05-22 Thu 20:43]
    Arrived today. Got the MG, excited to try it out. The mixed reaction to needing a full year prepaid to get the device has been interesting. I do think they botched it, but at the same time it's not unreasonable.

Last modified: May 04, 2026