whoopy


Namewhoopy JSON
Version 0.3.0 PyPI version JSON
download
home_pageNone
SummaryUnofficial Client for the Whoop API
upload_time2025-07-14 09:28:25
maintainerFelix Geilert
docs_urlNone
authorFelix Geilert
requires_python>=3.10
licenseMIT
keywords api fitness health wearable whoop
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Whoop Python Client

[![Python package](https://github.com/felixnext/whoopy/actions/workflows/python-package.yml/badge.svg)](https://github.com/felixnext/whoopy/actions/workflows/python-package.yml)
[![PyPI version](https://badge.fury.io/py/whoopy.svg)](https://badge.fury.io/py/whoopy)
[![Python Versions](https://img.shields.io/pypi/pyversions/whoopy.svg)](https://pypi.org/project/whoopy/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

An unofficial Python client for the [WHOOP API](https://developer.whoop.com/) with support for both OAuth and Personal API keys. Features async/await support, automatic token refresh, and comprehensive data models.

## Features

- 🚀 **Full API v2 Support** - Access cycles, sleep, recovery, workouts, and user data
- ⚡ **Async/Await Support** - High-performance async client with synchronous wrapper
- 🔄 **Automatic Token Management** - Token refresh and persistence out of the box
- 📊 **Pandas Integration** - Export data directly to DataFrames for analysis
- 🛡️ **Type Safety** - Comprehensive type hints and Pydantic models
- 🔁 **Retry Logic** - Built-in retry with exponential backoff
- 🐍 **Python 3.10+** - Modern Python with the latest features

## Installation

### Using pip

```bash
pip install whoopy
```

### Using uv (recommended)

```bash
uv add whoopy
```

### Development Installation

```bash
# Clone the repository
git clone https://github.com/felixnext/whoopy.git
cd whoopy

# Install with uv (recommended)
uv sync --all-extras

# Or install with pip
pip install -e ".[dev]"
```

## Quick Start

### 1. Get Your API Credentials

1. Go to the [WHOOP Developer Dashboard](https://developer-dashboard.whoop.com/)
2. Create a new application
3. Note your `client_id`, `client_secret`, and set `redirect_uri` to `http://localhost:1234`

### 2. Create Configuration

Create a `config.json` file:

```json
{
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "redirect_uri": "http://localhost:1234"
}
```

> **Note**: The library also supports a nested config structure for backward compatibility:
> ```json
> {
>     "whoop": {
>         "client_id": "YOUR_CLIENT_ID",
>         "client_secret": "YOUR_CLIENT_SECRET",
>         "redirect_uri": "http://localhost:1234"
>     }
> }
> ```

### 3. Run the Example

```bash
# Run the example script
uv run python -m tools.example

# Or if using standard Python
python -m tools.example
```

Note: The redirect uri will not exist. You need to copy the entire url from your browser and
paste it in the console. This will then handle the token exchange.

## Usage Examples

### Synchronous Usage (Recommended for Beginners)

```python
from whoopy import WhoopClient
from datetime import datetime, timedelta

# Initialize client (loads credentials from config.json)
client = WhoopClient.from_config()

# Get user profile
profile = client.user.get_profile()
print(f"Hello, {profile.first_name}!")

# Get recent recovery data
recoveries = client.recovery.get_all(
    start=datetime.now() - timedelta(days=7),
    end=datetime.now()
)

for recovery in recoveries:
    print(f"Recovery: {recovery.score.recovery_score}%")

# Export to pandas DataFrame
df = client.sleep.get_dataframe(
    start=datetime.now() - timedelta(days=30)
)
print(df.describe())
```

### Asynchronous Usage (Better Performance)

```python
import asyncio
from whoopy import WhoopClientV2

async def main():
    # Use async context manager
    async with WhoopClientV2.from_config() as client:
        # Fetch multiple data types concurrently
        profile, cycles, sleep = await asyncio.gather(
            client.user.get_profile(),
            client.cycles.get_all(limit_per_page=10),
            client.sleep.get_all(limit_per_page=10)
        )
        
        print(f"User: {profile.first_name}")
        print(f"Recent cycles: {len(cycles)}")
        print(f"Recent sleep: {len(sleep)}")

# Run the async function
asyncio.run(main())
```

### Authentication Options

```python
# Option 1: Interactive OAuth flow (opens browser)
client = WhoopClient.auth_flow(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    redirect_uri="http://localhost:1234"
)

# Option 2: From existing token
client = WhoopClient.from_token(
    access_token="YOUR_ACCESS_TOKEN",
    refresh_token="YOUR_REFRESH_TOKEN",
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET"
)

# Option 3: From config files (recommended)
client = WhoopClient.from_config()

# Save credentials for later use
client.save_token(".whoop_credentials.json")
```

## Available Data Types

### User Data
- Profile information
- Body measurements

### Physiological Data
- **Cycles** - Daily physiological cycles
- **Recovery** - Recovery metrics including HRV, resting heart rate
- **Sleep** - Sleep stages, efficiency, and performance
- **Workouts** - Exercise activities with strain and heart rate data

## Development

### Setup Development Environment

```bash
# Clone the repository
git clone https://github.com/felixnext/whoopy.git
cd whoopy

# Install with development dependencies
uv sync --all-extras
```

### Running Tests

```bash
# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=whoopy

# Run specific test file
uv run pytest tests/test_client.py
```

### Code Quality

```bash
# Run linting
uv run ruff check .

# Format code
uv run ruff format .

# Type checking
uv run mypy whoopy
```

### Building the Package

```bash
# Build wheel and sdist
uv build

# Install locally to test
uv pip install dist/whoopy-*.whl
```

## Data Explorer Tool

The package includes a Streamlit-based data explorer for visualizing your WHOOP data:

```bash
# Install explorer dependencies
uv sync --extra explorer

# Run the explorer
cd tools/explorer && uv run streamlit run explorer.py
```

### Explorer Features
- Interactive data visualization
- Date range filtering
- Export to CSV/Excel
- Recovery, sleep, and workout analysis

![Dashboard](assets/explorer.jpeg)

## API Reference

### Client Classes

- `WhoopClient` - Synchronous client (v2 API)
- `WhoopClientV2` - Async client (v2 API)
- `WhoopClientV1` - Legacy v1 client

### Main Methods

```python
# User data
profile = client.user.get_profile()
measurements = client.user.get_body_measurements()

# Cycles (daily summaries)
cycle = client.cycles.get_by_id(12345)
cycles = client.cycles.get_all(start="2024-01-01", end="2024-01-31")

# Sleep
sleep = client.sleep.get_by_id("uuid-here")
sleep_df = client.sleep.get_dataframe(start="2024-01-01")

# Recovery
recovery = client.recovery.get_for_cycle(12345)
recoveries = client.recovery.get_all()

# Workouts
workout = client.workouts.get_by_id("uuid-here")
workouts = client.workouts.get_by_sport("running", start="2024-01-01")
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

### Development Workflow

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes and add tests
4. Run tests and linting (`uv run pytest && uv run ruff check`)
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Disclaimer

This is an unofficial client for the WHOOP API. It is not affiliated with, endorsed by, or in any way officially connected to WHOOP. Use at your own risk.

## Support

- 📖 [Documentation](https://github.com/felixnext/whoopy#readme)
- 🐛 [Issue Tracker](https://github.com/felixnext/whoopy/issues)
- 💬 [Discussions](https://github.com/felixnext/whoopy/discussions)

## Acknowledgments

- Thanks to WHOOP for providing the [official API](https://developer.whoop.com/)
- Built with [aiohttp](https://docs.aiohttp.org/) and [pydantic](https://docs.pydantic.dev/)
            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "whoopy",
    "maintainer": "Felix Geilert",
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "api, fitness, health, wearable, whoop",
    "author": "Felix Geilert",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/7f/03/22bfcb796c909234f3fcc19793e4f518c21c305babccacd63f4d6061d302/whoopy-0.3.0.tar.gz",
    "platform": null,
    "description": "# Whoop Python Client\n\n[![Python package](https://github.com/felixnext/whoopy/actions/workflows/python-package.yml/badge.svg)](https://github.com/felixnext/whoopy/actions/workflows/python-package.yml)\n[![PyPI version](https://badge.fury.io/py/whoopy.svg)](https://badge.fury.io/py/whoopy)\n[![Python Versions](https://img.shields.io/pypi/pyversions/whoopy.svg)](https://pypi.org/project/whoopy/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nAn unofficial Python client for the [WHOOP API](https://developer.whoop.com/) with support for both OAuth and Personal API keys. Features async/await support, automatic token refresh, and comprehensive data models.\n\n## Features\n\n- \ud83d\ude80 **Full API v2 Support** - Access cycles, sleep, recovery, workouts, and user data\n- \u26a1 **Async/Await Support** - High-performance async client with synchronous wrapper\n- \ud83d\udd04 **Automatic Token Management** - Token refresh and persistence out of the box\n- \ud83d\udcca **Pandas Integration** - Export data directly to DataFrames for analysis\n- \ud83d\udee1\ufe0f **Type Safety** - Comprehensive type hints and Pydantic models\n- \ud83d\udd01 **Retry Logic** - Built-in retry with exponential backoff\n- \ud83d\udc0d **Python 3.10+** - Modern Python with the latest features\n\n## Installation\n\n### Using pip\n\n```bash\npip install whoopy\n```\n\n### Using uv (recommended)\n\n```bash\nuv add whoopy\n```\n\n### Development Installation\n\n```bash\n# Clone the repository\ngit clone https://github.com/felixnext/whoopy.git\ncd whoopy\n\n# Install with uv (recommended)\nuv sync --all-extras\n\n# Or install with pip\npip install -e \".[dev]\"\n```\n\n## Quick Start\n\n### 1. Get Your API Credentials\n\n1. Go to the [WHOOP Developer Dashboard](https://developer-dashboard.whoop.com/)\n2. Create a new application\n3. Note your `client_id`, `client_secret`, and set `redirect_uri` to `http://localhost:1234`\n\n### 2. Create Configuration\n\nCreate a `config.json` file:\n\n```json\n{\n    \"client_id\": \"YOUR_CLIENT_ID\",\n    \"client_secret\": \"YOUR_CLIENT_SECRET\",\n    \"redirect_uri\": \"http://localhost:1234\"\n}\n```\n\n> **Note**: The library also supports a nested config structure for backward compatibility:\n> ```json\n> {\n>     \"whoop\": {\n>         \"client_id\": \"YOUR_CLIENT_ID\",\n>         \"client_secret\": \"YOUR_CLIENT_SECRET\",\n>         \"redirect_uri\": \"http://localhost:1234\"\n>     }\n> }\n> ```\n\n### 3. Run the Example\n\n```bash\n# Run the example script\nuv run python -m tools.example\n\n# Or if using standard Python\npython -m tools.example\n```\n\nNote: The redirect uri will not exist. You need to copy the entire url from your browser and\npaste it in the console. This will then handle the token exchange.\n\n## Usage Examples\n\n### Synchronous Usage (Recommended for Beginners)\n\n```python\nfrom whoopy import WhoopClient\nfrom datetime import datetime, timedelta\n\n# Initialize client (loads credentials from config.json)\nclient = WhoopClient.from_config()\n\n# Get user profile\nprofile = client.user.get_profile()\nprint(f\"Hello, {profile.first_name}!\")\n\n# Get recent recovery data\nrecoveries = client.recovery.get_all(\n    start=datetime.now() - timedelta(days=7),\n    end=datetime.now()\n)\n\nfor recovery in recoveries:\n    print(f\"Recovery: {recovery.score.recovery_score}%\")\n\n# Export to pandas DataFrame\ndf = client.sleep.get_dataframe(\n    start=datetime.now() - timedelta(days=30)\n)\nprint(df.describe())\n```\n\n### Asynchronous Usage (Better Performance)\n\n```python\nimport asyncio\nfrom whoopy import WhoopClientV2\n\nasync def main():\n    # Use async context manager\n    async with WhoopClientV2.from_config() as client:\n        # Fetch multiple data types concurrently\n        profile, cycles, sleep = await asyncio.gather(\n            client.user.get_profile(),\n            client.cycles.get_all(limit_per_page=10),\n            client.sleep.get_all(limit_per_page=10)\n        )\n        \n        print(f\"User: {profile.first_name}\")\n        print(f\"Recent cycles: {len(cycles)}\")\n        print(f\"Recent sleep: {len(sleep)}\")\n\n# Run the async function\nasyncio.run(main())\n```\n\n### Authentication Options\n\n```python\n# Option 1: Interactive OAuth flow (opens browser)\nclient = WhoopClient.auth_flow(\n    client_id=\"YOUR_CLIENT_ID\",\n    client_secret=\"YOUR_CLIENT_SECRET\",\n    redirect_uri=\"http://localhost:1234\"\n)\n\n# Option 2: From existing token\nclient = WhoopClient.from_token(\n    access_token=\"YOUR_ACCESS_TOKEN\",\n    refresh_token=\"YOUR_REFRESH_TOKEN\",\n    client_id=\"YOUR_CLIENT_ID\",\n    client_secret=\"YOUR_CLIENT_SECRET\"\n)\n\n# Option 3: From config files (recommended)\nclient = WhoopClient.from_config()\n\n# Save credentials for later use\nclient.save_token(\".whoop_credentials.json\")\n```\n\n## Available Data Types\n\n### User Data\n- Profile information\n- Body measurements\n\n### Physiological Data\n- **Cycles** - Daily physiological cycles\n- **Recovery** - Recovery metrics including HRV, resting heart rate\n- **Sleep** - Sleep stages, efficiency, and performance\n- **Workouts** - Exercise activities with strain and heart rate data\n\n## Development\n\n### Setup Development Environment\n\n```bash\n# Clone the repository\ngit clone https://github.com/felixnext/whoopy.git\ncd whoopy\n\n# Install with development dependencies\nuv sync --all-extras\n```\n\n### Running Tests\n\n```bash\n# Run all tests\nuv run pytest\n\n# Run with coverage\nuv run pytest --cov=whoopy\n\n# Run specific test file\nuv run pytest tests/test_client.py\n```\n\n### Code Quality\n\n```bash\n# Run linting\nuv run ruff check .\n\n# Format code\nuv run ruff format .\n\n# Type checking\nuv run mypy whoopy\n```\n\n### Building the Package\n\n```bash\n# Build wheel and sdist\nuv build\n\n# Install locally to test\nuv pip install dist/whoopy-*.whl\n```\n\n## Data Explorer Tool\n\nThe package includes a Streamlit-based data explorer for visualizing your WHOOP data:\n\n```bash\n# Install explorer dependencies\nuv sync --extra explorer\n\n# Run the explorer\ncd tools/explorer && uv run streamlit run explorer.py\n```\n\n### Explorer Features\n- Interactive data visualization\n- Date range filtering\n- Export to CSV/Excel\n- Recovery, sleep, and workout analysis\n\n![Dashboard](assets/explorer.jpeg)\n\n## API Reference\n\n### Client Classes\n\n- `WhoopClient` - Synchronous client (v2 API)\n- `WhoopClientV2` - Async client (v2 API)\n- `WhoopClientV1` - Legacy v1 client\n\n### Main Methods\n\n```python\n# User data\nprofile = client.user.get_profile()\nmeasurements = client.user.get_body_measurements()\n\n# Cycles (daily summaries)\ncycle = client.cycles.get_by_id(12345)\ncycles = client.cycles.get_all(start=\"2024-01-01\", end=\"2024-01-31\")\n\n# Sleep\nsleep = client.sleep.get_by_id(\"uuid-here\")\nsleep_df = client.sleep.get_dataframe(start=\"2024-01-01\")\n\n# Recovery\nrecovery = client.recovery.get_for_cycle(12345)\nrecoveries = client.recovery.get_all()\n\n# Workouts\nworkout = client.workouts.get_by_id(\"uuid-here\")\nworkouts = client.workouts.get_by_sport(\"running\", start=\"2024-01-01\")\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\n### Development Workflow\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Make your changes and add tests\n4. Run tests and linting (`uv run pytest && uv run ruff check`)\n5. Commit your changes (`git commit -m 'Add amazing feature'`)\n6. Push to the branch (`git push origin feature/amazing-feature`)\n7. Open a Pull Request\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Disclaimer\n\nThis is an unofficial client for the WHOOP API. It is not affiliated with, endorsed by, or in any way officially connected to WHOOP. Use at your own risk.\n\n## Support\n\n- \ud83d\udcd6 [Documentation](https://github.com/felixnext/whoopy#readme)\n- \ud83d\udc1b [Issue Tracker](https://github.com/felixnext/whoopy/issues)\n- \ud83d\udcac [Discussions](https://github.com/felixnext/whoopy/discussions)\n\n## Acknowledgments\n\n- Thanks to WHOOP for providing the [official API](https://developer.whoop.com/)\n- Built with [aiohttp](https://docs.aiohttp.org/) and [pydantic](https://docs.pydantic.dev/)",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Unofficial Client for the Whoop API",
    "version": "0.3.0",
    "project_urls": {
        "Changelog": "https://github.com/felixnext/whoopy/releases",
        "Documentation": "https://github.com/felixnext/whoopy#readme",
        "Homepage": "https://github.com/felixnext/whoopy",
        "Issues": "https://github.com/felixnext/whoopy/issues",
        "Repository": "https://github.com/felixnext/whoopy.git"
    },
    "split_keywords": [
        "api",
        " fitness",
        " health",
        " wearable",
        " whoop"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "67ed9f8df00839401495e2dfebbdc0ca8c70cdc174a14dc9330a17798905101a",
                "md5": "3edaae31ed2521fbf073be601ea58279",
                "sha256": "e0422a40f3a7fd217c4cd43c190dd2190a958494d68225ac9563bfd208183773"
            },
            "downloads": -1,
            "filename": "whoopy-0.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "3edaae31ed2521fbf073be601ea58279",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 45883,
            "upload_time": "2025-07-14T09:28:24",
            "upload_time_iso_8601": "2025-07-14T09:28:24.444715Z",
            "url": "https://files.pythonhosted.org/packages/67/ed/9f8df00839401495e2dfebbdc0ca8c70cdc174a14dc9330a17798905101a/whoopy-0.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "7f0322bfcb796c909234f3fcc19793e4f518c21c305babccacd63f4d6061d302",
                "md5": "73e8029f7c6358bdfed8d1071352477c",
                "sha256": "bf9fbb1c71159dc9299081da01263d1abbc0ef5b9cd7312dbc281ef7873e454a"
            },
            "downloads": -1,
            "filename": "whoopy-0.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "73e8029f7c6358bdfed8d1071352477c",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 44369,
            "upload_time": "2025-07-14T09:28:25",
            "upload_time_iso_8601": "2025-07-14T09:28:25.358312Z",
            "url": "https://files.pythonhosted.org/packages/7f/03/22bfcb796c909234f3fcc19793e4f518c21c305babccacd63f4d6061d302/whoopy-0.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-14 09:28:25",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "felixnext",
    "github_project": "whoopy",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "whoopy"
}
        
Elapsed time: 2.31864s