Skip to main content

A Django app for the WHOOP API (v2), backed by django-healthdatamodel.

Project description

django-whoop

CI

A reusable Django app for the official WHOOP API (v2). Handles the WHOOP OAuth 2.0 flow, fetches cycles, sleep, recovery, and workouts from api.prod.whoop.com, and persists them through django-healthdatamodel so the same storage and query layer serves Apple Health, Fitbit, Google Health, Garmin, Oura, and WHOOP side-by-side.

v0.2.0 is a ground-up rewrite. Versions ≤ 0.1.3 used WHOOP's unofficial password-grant API (api-7.whoop.com), which no longer works, and stored data in their own tables. There is no migration path from the old models — uninstall the old app, delete its tables, and start fresh.

Install

pip install django-whoop

Add both this app and django-healthdatamodel to INSTALLED_APPS, then run migrations:

INSTALLED_APPS = [
    ...
    "healthdatamodel",
    "whoop",
]
python manage.py migrate

The model uses settings.AUTH_USER_MODEL so it works with any custom user model.

Include the URLs:

path("whoop/", include("whoop.urls")),

Configuration

WHOOP_CLIENT_ID = "..."        # from the WHOOP developer dashboard
WHOOP_CLIENT_SECRET = "..."
WHOOP_REDIRECT_URI = "https://your-app.example.com/whoop/callback/"

# Optional:
WHOOP_DEFAULT_SCOPES = [...]         # default: all read scopes + offline
WHOOP_CONNECT_SUCCESS_URL = "/"      # default: /admin/

Create the app at the WHOOP developer dashboard (any WHOOP member can). See docs/whoop/oauth.md for the flow details — notably, the offline scope is what makes WHOOP issue a refresh token, and WHOOP rotates the refresh token on every refresh.

Storage

This app does not define Record / Workout tables — those live in django-healthdatamodel. The whoop.ingest module maps WHOOP API responses to healthdatamodel.schemas.RecordInput and WorkoutInput, then persists them via healthdatamodel.ingest. Read the data back with healthdatamodel.query.*.

The only model defined here is WhoopConnection: per-user OAuth tokens, granted scopes, connection status, and last sync timestamp.

What maps where (details + caveats in the whoop/ingest.py docstring):

WHOOP healthdatamodel
Cycle strain Record type WHOOPStrain
Cycle kilojoules (total energy) Record ACTIVE_CALORIES (kcal)
Sleep stage totals Record sleep-analysis entries (synthetic sequential intervals)
Sleep respiratory rate Record HK respiratory rate
Recovery score / RHR / HRV / SpO2 / skin temp Record (WHOOPRecoveryScore + HK types)
Workout (sport, energy, distance, HR zones) Workout + metadata entries
Body measurement (height, weight) Record HK height / body mass

Webhooks

Point the webhook URL in the developer dashboard at /whoop/webhooks/. Every delivery is HMAC-verified against WHOOP_CLIENT_SECRET; authenticated events emit the whoop.signals.event_received signal. Wire a handler to whoop.webhooks.process_event (inline or via a queue) to fetch + ingest the changed resource:

from django.dispatch import receiver
from whoop.signals import event_received
from whoop.webhooks import process_event

@receiver(event_received)
def on_event(sender, payload, **kwargs):
    process_event(payload)

Documentation

The WHOOP API documentation is summarized as Markdown under docs/whoop/ so it's grep-able offline: oauth.md, api.md (endpoints + payload shapes from the OpenAPI spec), webhooks.md.

Try it on your own data

The repo includes a runnable demo Django project at demo/ that takes you through the full OAuth flow and syncs your WHOOP data into healthdatamodel.

1. Set up a WHOOP developer app (one-time)

At https://developer-dashboard.whoop.com/ create a team, then an app:

  • Redirect URL: http://localhost:8000/whoop/callback/ (exact match, trailing slash included).
  • Scopes: enable all the read scopes plus offline (read:recovery, read:cycles, read:sleep, read:workout, read:profile, read:body_measurement, offline).
  • Copy the Client ID and Client Secret.

You'll authenticate as your own WHOOP account — no review process needed for personal use.

2. Run the demo

uv sync
uv run python manage.py migrate
uv run python manage.py createsuperuser
export WHOOP_CLIENT_ID=...
export WHOOP_CLIENT_SECRET=...
uv run python manage.py runserver

Open http://localhost:8000/, sign in with the superuser you just created, then:

  • Click Connect WHOOP → consent at WHOOP → land back on the homepage with a WhoopConnection saved for your user.
  • Pick a window and click Sync now to fetch and persist records.
  • Browse the resulting rows at /admin/healthdatamodel/record/ (and .../workout/).

If you'd rather drive sync from the terminal:

uv run python manage.py sync_whoop --user <your-username> --days 7

Development

uv sync --group dev
uv run pytest tests/ -v
uv run pre-commit run --all-files

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_whoop-0.2.0.tar.gz (64.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_whoop-0.2.0-py3-none-any.whl (28.0 kB view details)

Uploaded Python 3

File details

Details for the file django_whoop-0.2.0.tar.gz.

File metadata

  • Download URL: django_whoop-0.2.0.tar.gz
  • Upload date:
  • Size: 64.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for django_whoop-0.2.0.tar.gz
Algorithm Hash digest
SHA256 696356891c42c0619864ed26602aee98206c1d24a970689ed60575ccbe56e028
MD5 eb0271b822c1175d1d195dee7501fa24
BLAKE2b-256 ce9b1559f72f85a23c2efd2d9053f888b63209a3ad2ffc16666acadfdedd44a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_whoop-0.2.0.tar.gz:

Publisher: ci.yml on django-health/django-whoop

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_whoop-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: django_whoop-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 28.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for django_whoop-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2bf1fc2d1cbfda53dc561ccc6ea0f0eec2b53ae3ad275f919cf7e8337940c5a9
MD5 8208ea35bd61215254bd351ea67cd051
BLAKE2b-256 8d62a04a8744be79ce1a74dc3c72d219682321c5173ba15b6cfd6710f049530e

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_whoop-0.2.0-py3-none-any.whl:

Publisher: ci.yml on django-health/django-whoop

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page