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:
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())
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 -X 'GET' \
'https://research-api.nowatch.com/v1/user/YOUR_USER_ID' \
-H 'X-API-KEY: YOUR_API_KEY'
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())
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()}")
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
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 -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'
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()}")
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 -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'
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()}")