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 can obtain your API key by contacting Nowatch.
To get the user profile data from the API, you need two things:
- the user_id;
- 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/<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/<user_id>", headers=headers)
if response.status_code==200:
print(response.json())
else:
print("404 Code: User not found")
print(response.json())
response.json()
Out[2]:
{
'id': 'xxxxxxxxxxxxxxxxxxxxxx',
'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/<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/<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)
┌─────────────────────┬───────┬─────────┐
│ timestamp ┆ value ┆ quality │
│ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ i64 │
╞═════════════════════╪═══════╪═════════╡
│ 2024-04-01T00:00:05 ┆ 50 ┆ 4 │
│ 2024-04-01T00:00:15 ┆ 52 ┆ 4 │
│ 2024-04-01T00:00:25 ┆ 51 ┆ 4 │
│ 2024-04-01T00:00:35 ┆ 52 ┆ 4 │
│ 2024-04-01T00:00:45 ┆ 51 ┆ 4 │
└─────────────────────┴───────┴─────────┘
if response_timezones.status_code == 200:
print(pl.DataFrame(response_timezones.json()).head(5))
shape: (5, 2)
┌────────────┬────────────────────┐
│ date ┆ timezone │
│ --- ┆ --- │
│ str ┆ struct[2] │
╞════════════╪════════════════════╡
│ 2025-04-01 ┆ {120,"2025-04-01"} │
│ 2025-04-02 ┆ {120,"2025-04-02"} │
│ 2025-04-03 ┆ {120,"2025-04-03"} │
│ 2025-04-04 ┆ {120,"2025-04-04"} │
│ 2025-04-05 ┆ {120,"2025-04-05"} │
└────────────┴────────────────────┘
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_timestamps, 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_timestamps.extend(data["timestamp"])
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_timestamps)} 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,
"hrv_descriptive": "Your HRV is stable."
}
]
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()}")