Skip to content

Quick start guide

All API endpoints require authentication using an API key. The key must be included in the X-API-KEY header of your requests. See examples/authentication for more details.

All API endpoints support the Accept-Encoding: gzip header which will compress the JSON output to a zip format. Please see encoding/examples for more details.

API Key

You obtain your API key from the NOWATCH dashboard. A key is scoped to one or more groups rather than to individual users directly, and can only read data that a group member has actively shared with you. See Data sharing & access for how groups, sharing, and keys fit together.

Finding which user_ids you can access

A key doesn't come with a fixed list of users: it can reach whoever is currently a member of its linked group(s). To find those user_ids, call /v1/keys/users:

cURL
curl -X 'GET' \
  'https://research-api.nowatch.com/v1/keys/users' \
  -H 'X-API-KEY: YOUR_API_KEY'
Python
import requests

headers = {
    "X-API-KEY": "YOUR_API_KEY",
}

response = requests.get("https://research-api.nowatch.com/v1/keys/users", headers=headers)
response.raise_for_status()
print(response.json())
Python Output
response.json()

Out[1]:
{
    "user_ids": [
        "uid_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6...",
        "uid_f6e5d4c3b2a1098f7e6d5c4b3a291807..."
    ]
}

Use these user_id values directly in the endpoints below. Being reachable this way doesn't guarantee data comes back for every request: each user still controls what they've shared with you, for how long, and whether export is permitted (see Data sharing & access).

To get the user profile data from the API, you need two things:

  • the user_id (from the call above);
  • your API key;

To get the biometrics data from user from the API, you need four things:

  • the user_id;
  • your API key;
  • the metric name (you can check how to do it for all metrics in API Reference tab);
  • the start and end dates from which you want to collect data (DD-MM-YYYY).

We provide here two examples.

Example 1: obtaining user profile data

cURL
curl -X 'GET' \
  'https://research-api.nowatch.com/v1/user/YOUR_USER_ID' \
  -H 'X-API-KEY: YOUR_API_KEY'
Python
import requests

headers = {
    "X-API-KEY": "YOUR_API_KEY",
}

response = requests.get("https://research-api.nowatch.com/v1/user/YOUR_USER_ID", headers=headers)

if response.status_code==200:
    print(response.json())
else:
    print("403 Code: No access to this user")

print(response.json())
Python Output
response.json()

Out[2]:
{
    "first_name": "Jane",
    "last_name": "Doe",
    "age": 30,
    "sex": "MALE",
    "height": 175,
    "weight": 75,
    "watch_hand": "LEFT",
    "dominant_hand": "RIGHT",
    "platform": "ANDROID"
}

Example 2: obtaining heart rate data

For an easy manipulation, you can then convert the JSON response into a dataframe (Pandas or Polars are suggested). Also, don't forget to also fetch the Timezones and adjust them to the data (check out how in Timezone ).

Timeseries endpoints return a paginated envelope. To walk all pages, follow the next_cursor until it is null.

import requests

headers = {
    "X-API-KEY": "YOUR_API_KEY",
}

response_heart_rate = requests.get(
    "https://research-api.nowatch.com/v1/timeline/timeseries/YOUR_USER_ID/HEART_RATE"
    "?start_date=<start_date>&end_date=<end_date>",
    headers=headers,
)

if response_heart_rate.status_code == 200:
    print(response_heart_rate.json())
else:
    print(f"Error {response_heart_rate.status_code}: {response_heart_rate.json()}")


response_timezones = requests.get(
    "https://research-api.nowatch.com/v1/timeline/events/timezones/YOUR_USER_ID"
    "?start_date=<start_date>&end_date=<end_date>",
    headers=headers,
)

if response_timezones.status_code == 200:
    print(response_timezones.json())
else:
    print(f"Error {response_timezones.status_code}: {response_timezones.json()}")
Output and converting to a DataFrame
import polars as pl

if response_heart_rate.status_code == 200:
    page = response_heart_rate.json()
    print(pl.DataFrame(page["data"]).head(5))
else:
    print(f"Error {response_heart_rate.status_code}")


shape: (5, 3)
┌──────────────────────────┬───────┬───────────────────┐
 datetime                  value  quality_indicator 
 ---                       ---    ---               
 str                       i64    i64               
╞══════════════════════════╪═══════╪═══════════════════╡
 2024-04-01T00:00:05.000Z  50     4                 
 2024-04-01T00:00:15.000Z  52     4                 
 2024-04-01T00:00:25.000Z  51     4                 
 2024-04-01T00:00:35.000Z  52     4                 
 2024-04-01T00:00:45.000Z  51     4                 
└──────────────────────────┴───────┴───────────────────┘


if response_timezones.status_code == 200:
    print(pl.DataFrame(response_timezones.json()).head(5))

shape: (5, 2)
┌────────────┬───────┐
 date        value 
 ---         ---   
 str         i64   
╞════════════╪═══════╡
 2025-04-01  120   
 2025-04-02  120   
 2025-04-03  120   
 2025-04-04  120   
 2025-04-05  120   
└────────────┴───────┘

Example 2b: walking all pages for a multi-day range

Heart rate cursor walk
import requests

BASE_URL = "https://research-api.nowatch.com"
headers = {"X-API-KEY": "YOUR_API_KEY"}

all_datetimes, all_values = [], []
url = f"{BASE_URL}/v1/timeline/timeseries/YOUR_USER_ID/HEART_RATE"
params = {"start_date": "2024-04-01", "end_date": "2024-04-07", "days": 1}

while True:
    resp = requests.get(url, headers=headers, params=params)
    resp.raise_for_status()
    page = resp.json()

    data = page["data"]
    all_datetimes.extend(data["datetime"])
    all_values.extend(data["value"])

    next_cursor = page.get("next_cursor")
    if not next_cursor:
        break
    params = {"cursor": next_cursor}

print(f"Fetched {len(all_datetimes)} rows")

Example 3: obtaining Overview HRV data

cURL
curl -X 'GET' \
  'https://research-api.nowatch.com/v1/overview/YOUR_USER_ID/HRV?start_date=2023-01-01&end_date=2023-01-31' \
  -H "accept: application/json" \
  -H 'X-API-KEY: YOUR_API_KEY'
Python
import requests

headers = {
    "accept": "application/json",
    "X-API-KEY": "YOUR_API_KEY",
}

response = requests.get(
    "https://research-api.nowatch.com/v1/overview/YOUR_USER_ID/HRV"
    "?start_date=2023-01-01&end_date=2023-01-31",
    headers=headers,
)

if response.status_code == 200:
    print(response.json())
else:
    print(f"Error {response.status_code}: {response.json()}")
Python Output
response.json()

Out[3]:
[
  {
    "date": "2023-01-01",
    "hrv_daily": 40.5,
    "hrv_typical": 42.0,
    "hrv_upper_deviation": 5.0,
    "hrv_lower_deviation": 3.0
  }
]

Example 4: obtaining Feelings DAY data

Feelings endpoints return a paginated envelope (data + next_cursor).

cURL
curl -X 'GET' \
  'https://research-api.nowatch.com/v1/feelings/YOUR_USER_ID/DAY?start_date=2023-01-01&end_date=2023-01-31' \
  -H "accept: application/json" \
  -H 'X-API-KEY: YOUR_API_KEY'
Python
import requests

headers = {
    "accept": "application/json",
    "X-API-KEY": "YOUR_API_KEY",
}

response = requests.get(
    "https://research-api.nowatch.com/v1/feelings/YOUR_USER_ID/DAY"
    "?start_date=2023-01-01&end_date=2023-01-31",
    headers=headers,
)

if response.status_code == 200:
    page = response.json()
    print(page["data"])
else:
    print(f"Error {response.status_code}: {response.json()}")
Python Output
page["data"]

Out[4]:
[
  {
    "date": "2023-01-01",
    "value": "GOOD"
  }
]