What sleep_duration measures
sleep_duration is the number of minutes a profile was asleep during one sleep day. It is the anchor of the sleep family: sleep debt, efficiency, and the sleep duration factor in three scores are all derived from it, and the source-selection and phone-estimation rules described on this page apply to every other sleep biomarker.
| Field | sleep_duration |
|---|---|
| Unit | Minutes |
| Reported | Daily, one value per night |
| Window | 6pm to 6pm, profile local time. Dated by the morning the window ends. |
| Wearable required | No |
| API | GET /api/v1/profile/biomarker/{externalId}?categories=sleep&types=sleep_duration plus a date range |
| Webhook | BiomarkerCreatedIntegrationEvent. Entries update in place, upsert on id. |
Data sources
Sleep arrives with very different richness depending on the device, and the difference decides which source wins the night.
| Platform | Record | What it usually contains |
|---|---|---|
| iOS, HealthKit | sleepAnalysis | With an Apple Watch: in-bed, awake, and asleep stages (Apple’s Core stage maps to light). Without a Watch: usually only in-bed periods from the iPhone Sleep schedule, if the user set one up. |
| Android, Health Connect | SleepSessionRecord | Sessions with stages from Samsung Health, Fitbit, Pixel Watch, Oura, and other apps that write to Health Connect. |
| Android, phone sleep detection | Android’s Sleep API through the SDK | A sleep window without stages, on phones that support it. |
| Direct integrations | Provider-dependent | Garmin, Oura, and WHOOP supply sleep. WHOOP supplies session totals rather than timestamped stages. |
| Sahha phone estimate | phone_sleep_estimation, channel sahha | An in-bed window derived from the gap in activity. See below. |
| Wearable required | No | Stages, awakenings, latency, and efficiency do require one. |
The phone sleep estimate
When a night has no sleep record from any other source, Sahha estimates one from the phone. The method, in brief:
- Activity and device events from the phone are laid out over the 6pm to 6pm window. Step buckets under 20 steps are ignored as sensor jitter, and a lone event with nothing within 90 minutes on either side is dropped so that one trip to the bathroom does not split the night.
- The longest gap between events is the candidate sleep window. It is accepted if it lasts 5 to 12 hours. If nothing qualifies, a fallback pass using only movement events accepts a 4 to 14 hour gap.
- The front of the window is trimmed by 35 minutes, because the last pre-sleep event tends to land early relative to true sleep onset. The wake end is not trimmed.
- The result is written as a single in-bed period with no stages, under the source
phone_sleep_estimationand channelsahha. It is re-estimated as more of the morning’s data arrives, at most once an hour, and only rewritten if the window moves by more than 5 minutes.
The thresholds were tuned against roughly 4,600 nights where the same profile had both a phone estimate and a wearable record. On that set the estimate found a valid window on about two thirds of nights, with a median absolute error in duration of about 43 minutes.
The estimate is ranked below every real sleep source, so it never overrides a watch. An account that does not want phone-derived sleep at all can ask Sahha to disable it. The setting also suppresses sleep from Android’s phone-based detection, so an opted-out account gets sleep from wearables and Health Connect writers only.
How Sahha calculates sleep_duration
- Pick the sleep day. The window runs 6pm to 6pm in the profile’s local time. A record is assigned to the day the window ends.
- Pick one source for the night. Sources are ranked by richness: a source with stage data (light, deep, REM) first, a source with asleep periods but no stages second, a source with only in-bed periods third, and the phone estimate last. Within a rank, the source with the most recorded sleep wins. Sources are never combined.
- Group records into sessions. If the source asserts sessions, those are used. Otherwise records are grouped by time, and a gap of more than 60 minutes starts a new session. The longest session is the main sleep. Timing biomarkers use only the main session, so naps cannot shift bed and wake times.
- Sum the time asleep. If the provider reports its own session totals, those are used. Otherwise all asleep-classified records in the window are merged into non-overlapping runs and their length summed, so a night recorded partly as “asleep” and partly as stages is counted once. If the source has no asleep classification at all, the in-bed time is used as a last resort.
- Recompute when data arrives. A watch that syncs at 9am replaces the phone estimate published at 7am, under the same
id.
There is no biomarker for a night with no sleep record and no qualifying estimate. Absence means no data, not zero.
Interpreting the value
What counts as a lot. The score goal is 480 minutes. The factor curve peaks just under 8 hours and falls away on both sides, so 10 hours earns roughly the same credit as 6. For a per-user benchmark, the sleep_duration comparison insight places the night against global, demographic, and personal-baseline groups.
The source decides the precision. Stage data from a watch is accurate to a few minutes. The phone estimate is accurate to about three quarters of an hour at the median and can miss a night entirely. Show the source alongside the value where precision matters.
In-bed-only sources overstate sleep. When a source has no asleep classification, duration equals time in bed, which includes falling asleep and lying awake.
Related scores and insights
Score factor. Sleep duration is a factor, under the name sleep_duration, in the Sleep, Wellbeing, and Readiness scores, with unit minute and goal 480. Credit is symmetric around the goal:
| Minutes asleep | Factor score |
|---|---|
| 300 | 0.55 |
| 360 | 0.69 |
| 420 | 0.85 |
| 480 | 0.99 |
| 540 | 0.82 |
| 600 | 0.67 |
Sleep factors are computed from completed nights only and are not projected forward during the day.
Trend. A sleep_duration trend is available on the factor score.
Comparison. A sleep_duration comparison is available on the raw minutes.
Archetypes. sleep_duration (very_short_sleeper to long_sleeper) is derived from it directly. sleep_pattern and sleep_quality use it alongside timing and regularity.
Use cases
A nightly summary that names the source. Show the minutes with the provider next to it. When the source is the phone estimate, label it as an estimate. This one label prevents most “your app says I slept 9 hours” complaints.
GET /api/v1/profile/biomarker/{externalId}?categories=sleep&types=sleep_duration&startDateTime=2026-08-26&endDateTime=2026-09-02A “connect a wearable” prompt. Users whose nights are consistently phone-estimated get a value but no stages, latency, or efficiency. That is the moment to suggest a watch, with a concrete list of what unlocks.
A weekly average with a floor on coverage. Average only weeks with at least 4 nights of data, and say how many nights were used. Phone-only users will miss nights.
Limitations
- The night is dated by its morning. A query for the 2nd returns the sleep that ended on the 2nd.
- Sleep before 6pm belongs to the next day. A night worker sleeping 9am to 4pm is dated the same day; one sleeping 7pm to 3am is dated the following day.
- The phone estimate misses fragmented nights. No gap of at least 5 hours, or 4 in the fallback, means no estimate. Night-time phone use fragments the gap.
- Values are revised. A better source syncing later rewrites the night. Do not treat the first value for a night as final until the following evening.
- Absence is not zero.
Related guides
- Sleep timing for bed and wake times and the circadian alignment factor
- Sleep debt for the 14-day shortfall derived from duration
- Sleep efficiency and time in bed for the asleep-versus-in-bed split
- Sleep stages for what a wearable adds
- Sleep Score explained for how the seven factors combine
- Data dictionary in the developer docs