This page gets you from zero to a working health score query. You'll create a Sonar user in the sandbox, explore pre-loaded health data, and query computed scores — all from your terminal.
Prerequisites
- A Sonar account — sign up here if you don't have one
- A sandbox API key from your dashboard
curlor any HTTP client (Python and TypeScript examples below)
Try It Out
Create a User
A Sonar user is the container for all device connections and health data belonging to one of your end-users. You supply your own reference_id to link them back to your system.
import requests
BASE = "https://api.sonarhealth.co/v1"
HEADERS = {"Authorization": "Bearer sk_sandbox_abc123", "Content-Type": "application/json"}
user = requests.post(f"{BASE}/users", headers=HEADERS, json={
"reference_id": "user_123",
}).json()["data"]
print(user["id"]) # usr_abc123
const BASE = "https://api.sonarhealth.co/v1";
const HEADERS = {
Authorization: "Bearer sk_sandbox_abc123",
"Content-Type": "application/json",
};
const { data: user } = await fetch(`${BASE}/users`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({
reference_id: "user_123",
}),
}).then(r => r.json());
console.log(user.id); // usr_abc123
{
"data": {
"id": "usr_abc123",
"reference_id": "user_123",
"timezone": "UTC",
"status": "active",
"score_status": "ready",
"last_sync_at": null,
"last_score_at": null,
"created_at": "2025-01-15T10:00:00Z"
}
}
In the sandbox, every new user is automatically seeded with 7 days of realistic wearable data and pre-computed health scores — no device connection required. That's why score_status is ready immediately. In production, new users start as calibrating until enough data arrives (typically 3–4 days). Store user.id — you'll use it in every subsequent request.
Query Health Data
Query normalized daily metrics for your sandbox user:
daily = requests.get(f"{BASE}/users/{user['id']}/daily", headers=HEADERS, params={
"metrics": "steps,active_calories,resting_heart_rate,heart_rate_variability",
"start_date": "2025-01-08", # sandbox data covers 7 days before user creation
"end_date": "2025-01-14",
}).json()["data"]
for row in daily:
print(f"{row['date']} {row['metric']:20s} {row['value']}")
const params = new URLSearchParams({
metrics: "steps,active_calories,resting_heart_rate,heart_rate_variability",
start_date: "2025-01-08", // sandbox data covers 7 days before user creation
end_date: "2025-01-14",
});
const { data: daily } = await fetch(
`${BASE}/users/${user.id}/daily?${params}`,
{ headers: HEADERS }
).then(r => r.json());
for (const row of daily) {
console.log(`${row.date} ${row.metric.padEnd(20)} ${row.value}`);
}
{
"data": [
{ "date": "2025-01-14", "metric": "steps", "value": 9842, "unit": "count" },
{ "date": "2025-01-14", "metric": "active_calories", "value": 485, "unit": "kcal" },
{ "date": "2025-01-14", "metric": "resting_heart_rate", "value": 52, "unit": "bpm" },
{ "date": "2025-01-14", "metric": "heart_rate_variability", "value": 58, "unit": "ms" },
{ "date": "2025-01-13", "metric": "steps", "value": 12100, "unit": "count" }
]
}
Data is always in normalized units regardless of the source device. See the Data Catalog for all 70+ metric IDs and the Data Model for how normalization and deduplication work.
Query Health Scores
Health scores are computed automatically as data arrives. Query the latest scores for a user:
scores = requests.get(f"{BASE}/users/{user['id']}/scores", headers=HEADERS).json()["data"]
for score in scores:
print(f"{score['type']:22s} {score['value']:3d}/100")
const { data: scores } = await fetch(
`${BASE}/users/${user.id}/scores`,
{ headers: HEADERS }
).then(r => r.json());
for (const score of scores) {
console.log(`${score.type.padEnd(22)} ${score.value}/100`);
}
{
"data": [
{
"type": "recovery_score",
"value": 78,
"computed_at": "2025-01-15T07:30:00Z",
"factors": [
{ "name": "hrv_trend", "contribution": 0.35, "direction": "positive" },
{ "name": "sleep_quality", "contribution": 0.28, "direction": "positive" },
{ "name": "resting_heart_rate", "contribution": 0.22, "direction": "neutral" }
]
},
{ "type": "strain_score", "value": 42, "computed_at": "2025-01-15T14:00:00Z", "factors": [] },
{ "type": "sleep_score", "value": 85, "computed_at": "2025-01-15T07:30:00Z", "factors": [] },
{ "type": "stress_score", "value": 22, "computed_at": "2025-01-15T14:00:00Z", "factors": [] },
{ "type": "nutrition_score", "value": 65, "computed_at": "2025-01-15T12:00:00Z", "factors": [] },
{ "type": "energy_reserve_score", "value": 71, "computed_at": "2025-01-15T14:00:00Z", "factors": [] }
]
}
With real devices, scores require a baseline period before they become available — check the score_status field on the user object (calibrating, ready, or stale). See Health Scores for calibration timelines per score type.
Sonar