WhenPeak API
Performance intelligence in one API call. Stateless predictions, no setup.
Quick Start
Get your first prediction in under 2 minutes.
curl -X POST https://api.whenpeak.com/api/v1/predict \
-H "Content-Type: application/json" \
-d '{
"sleep_time": "23:00",
"wake_time": "07:00",
"sleep_quality": "good"
}'{
"dps": 74,
"peak_1": { "time": "10:00" },
"peak_2": { "time": "16:00" },
"dip": { "time": "14:00" },
"chronotype": "Third Bird",
"confidence": "low",
"upgrade_prompt": "Connect Apple Health for 3x more accurate predictions."
}API Reference
https://api.whenpeak.comAuthentication
WhenPeak has two access levels.
No key needed: public prediction: POST /api/v1/predict is open. Stateless, no account required.
API key: authenticated endpoints: All personalised endpoints (/ingest/*, /performance/*, /chronotype) require a key sent in the header:
X-WhenPeak-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxKey registration is handled in the developer dashboard. Create a free account at whenpeak.com/dashboard.html. Your pk_live key is issued once on signup (Free tier: 1,000 calls/month).
Your api_key is shown once. We store only a SHA-256 hash. It cannot be recovered later. Save it now. If you lose it, create another account from the dashboard.
Optional fields: omit, don't null
Send optional request fields by omitting them entirely. Do not send them as null. A standard client that serializes unset fields as null will receive a 422 error that looks like a missing required field. Omit the key instead.
Good: {"sleep_time":"23:00","wake_time":"07:00","sleep_quality":"good"}
Avoid: {"sleep_time":"23:00","wake_time":"07:00","sleep_quality":"good",
"exercise_yesterday":null,"waso_minutes":null}curl https://api.whenpeak.com/api/v1/auth/verify \
-H "X-WhenPeak-API-Key: pk_live_..."Returns {"valid": true, "user_id": "..."}. Costs 1 call against your quota.
If you're building an app where multiple end-users each need their own performance profile, you don't need a separate API key per user. Your developer key authenticates the account; you route each call to the right end-user with a second header.
Step 1: provision your end-users once:
curl -X POST https://api.whenpeak.com/api/v1/users \
-H "X-WhenPeak-API-Key: pk_live_..." \
-H "Content-Type: application/json" \
-d '{"label": "alice@yourapp.com"}'{
"user_id": "a1b2c3...",
"label": "alice@yourapp.com",
"created": 888">true
}Store the user_id in your app. It's the stable identifier for that end-user's sleep and performance history.
Step 2: route every data call to the right user:
curl -X POST https://api.whenpeak.com/api/v1/ingest/sleep \
-H "X-WhenPeak-API-Key: pk_live_..." \
-H "X-WhenPeak-User-ID: a1b2c3..." \
-H "Content-Type: application/json" \
-d '{...}'Each end-user's data is fully isolated. All calls count against your developer quota. Management endpoints (provision / list / delete users) do not count against your quota.
Rate Limits
Each key has a monthly call quota by tier. Resets on the first of each calendar month (UTC).
| Tier | Price | Calls / month |
|---|---|---|
| Free | $0 | 1,000 |
| Growth | $49 | 50,000 |
| Scale | $199 | 500,000 |
The public /api/v1/predict, the usage check endpoint, and all user management endpoints (POST/GET/DELETE /api/v1/users) do not count against your quota.
curl https://api.whenpeak.com/api/v1/auth/usage \
-H "X-WhenPeak-API-Key: pk_live_..."{
"tier": "free",
"used": 412,
"limit": 1000,
"remaining": 588,
"period": "2026-06"
}Authenticated endpoints return 429 Too Many Requests:
{
"detail": "Monthly quota exceeded for the 'free' tier (1,000 calls/month).
Upgrade at https://whenpeak.com/#pricing."
}Upgrade on the Pricing page or email hello@whenpeak.com.
Endpoints
Auth Endpoints
/api/v1/auth/registerRetired / Deprecated
This endpoint is retired and now returns 410 Gone with a message pointing to the dashboard. Create a free account at whenpeak.com/dashboard.html to receive your pk_live key.
/api/v1/auth/verifyConfirm a key is valid. Returns the resolved user_id. Costs 1 call.
/api/v1/auth/usageCurrent month's usage for a key. Does not count against your quota. Returns: tier, used, limit, remaining, period.
User Management Endpoints
These endpoints let you provision and manage end-users under your developer account. Require X-WhenPeak-API-Key. Do not count against your quota.
/api/v1/usersProvision an isolated end-user. Idempotent. Provisioning the same label twice returns the existing user (created: false) rather than erroring.
| Field | Type | Required | Description |
|---|---|---|---|
| label | string | Yes | Your identifier for this user, e.g. their email |
| age | int | No | Sets HRV baseline (default: 35) |
Returns: user_id, label, created (true/false).
/api/v1/usersList every end-user you have provisioned. Returns: count, users[] (user_id, label, created_at).
/api/v1/users/{user_id}Permanently delete an end-user and all of their data. Scoped to your account. You can only delete users you provisioned. Use this to honour GDPR erasure requests.
Prediction Endpoints
/api/v1/predictStateless prediction from self-reported data. No authentication required.
| Field | Type | Required | Description |
|---|---|---|---|
| sleep_time | string | Required | HH:MM previous night, e.g. "23:00" |
| wake_time | string | Required | HH:MM this morning, e.g. "07:00" |
| sleep_quality | string | Required | "good" / "fair" / "poor" |
| exercise_yesterday | boolean | Optional: omit if unknown | Whether user exercised yesterday |
| exercise_timing | string | Optional: omit if unknown | "morning" / "afternoon" / "evening" |
| sleep_latency_minutes | number | Optional: omit if unknown | Minutes to fall asleep |
| waso_minutes | number | Optional: omit if unknown | Minutes awake during the night |
Optional fields must be omitted entirely when not provided: do not send as null.
/api/v1/performance/best-windowFind the optimal time window for a specific task type.
| Param | Type | Required | Options |
|---|---|---|---|
| task_type | string | Yes | analytical / creative / learning / administrative |
| duration | integer | No | Minutes (default: 90) |
/api/v1/performance/nowGet the current moment's performance score.
current_performancewindow_typerecommendationpeak_1dip
/api/v1/performance/forecastMulti-day behavioural forecast (7–30 days). Requires an API key. Projects DPS and a performance curve for each upcoming day from your day-of-week sleep pattern, with sleep debt rolled forward. Days 1–2 high confidence, 3–7 medium, beyond a week a projection of habitual behaviour.
| Param | Type | Required | Description |
|---|---|---|---|
| days | integer | No | Forecast horizon. Range: 7–30, default: 7 |
{
"summary": {
"average_day": "Tuesday",
"best_day": "Wednesday",
"worst_day": "Sunday"
},
"forecast": [
{
"day": "2026-06-11",
"dps": 74,
"curve": [...],
"peak_1": { "time": "10:00" },
"peak_2": { "time": "16:00" },
"dip": { "time": "14:00" },
"projected_sleep_hours": 7.5,
"confidence": "high"
}
]
}/api/v1/predict/weekStateless multi-day projection from a single self-report. No auth. Repeats today's estimate forward with decaying confidence; points users to the authenticated forecast for a behavioural projection.
| Param | Type | Required | Description |
|---|---|---|---|
| days | integer | No | Projection horizon. Range: 7–30, default: 7 |
Same as POST /api/v1/predict.
Suggestion Endpoints
/api/v1/performance/suggestThe model run in reverse. Prediction answers "given my behaviour, what's my curve?"; suggestion answers "given a target, what behaviour gets me there?": it works backward from a future target to tonight's actions.
Requires an API key (X-WhenPeak-API-Key). Uses the caller's stored sleep history, and is multi-user aware via the X-WhenPeak-User-ID header. Counts as 1 call against your quota.
With few or no synced nights it still returns a plan, labelled with a population_baseline confidence that matures to calibrating and then personalized as sleep history accumulates.
The body has a required target plus optional aggressiveness and constraints.
target is one of three shapes, selected by kind:
| Kind | Fields | Use |
|---|---|---|
| point | date "YYYY-MM-DD", time "HH:MM" | Be at peak at a moment (interview, exam, call) |
| window | date "YYYY-MM-DD", start "HH:MM", end "HH:MM" | Hold capacity across a window (multi-hour exam) |
| shift | direction "earlier" | "later", hours (number), by_date "YYYY-MM-DD" (optional), reason "jetlag" | "habit" (optional) | Move your whole circadian phase (jet lag, or waking earlier permanently) |
aggressiveness (string, optional): "minimal" (default: one gentle nudge, favours keeping your current rhythm) or "full" (travellers / hard deadlines; permits larger daily shifts).
constraints (object, optional):
| Field | Type | Description |
|---|---|---|
| earliest_wake | string HH:MM | Won't ask you to wake before this |
| latest_wake | string HH:MM | Won't ask you to wake after this |
| fixed_wake | string HH:MM | Wake is immovable that day (e.g. a flight) |
| min_sleep_hours | number | Your sleep floor; raised to max(this, the 7h health floor), never lowered |
| Field | Description |
|---|---|
| shape | point / window / shift |
| feasibility | { achievable, runway_days, limit_reason, headline }. limit_reason is null, "runway", "constraints", "chronotype_range", or "circadian_floor" |
| confidence | { label, nights_of_data, consistency_sd_min, note }. label is population_baseline / calibrating / personalized |
| current_state | Your estimated chronotype, peak, and typical wake |
| plan | { summary, nights[], event_day }: the night-by-night plan and the projected event-day outcome |
| tonight | A single gentle action for tonight |
| guardrails | Health-floor and framing guarantees (never prescribes under 7h) |
| disclaimer | Plain-language scope note |
curl -X POST https://api.whenpeak.com/api/v1/performance/suggest \
-H "X-WhenPeak-API-Key: pk_live_..." \
-H "Content-Type: application/json" \
-d '{"target": {"kind": "point", "date": "2026-07-16", "time": "14:00"}}'{
"shape": "point",
"target": { "date": "2026-07-16", "window": "14:00", "centre": "14:00" },
"feasibility": {
"achievable": 888">true,
"runway_days": 13,
"limit_reason": 888">null,
"headline": "There's a clear plan to have you sharp across 14:00. Projected capacity ~80.8/100."
},
"confidence": {
"label": "personalized",
"nights_of_data": 14,
"consistency_sd_min": 0.0,
"note": "Personalised to your measured circadian phase and sleep pattern."
},
"current_state": {
"estimated_chronotype": "Third Bird",
"estimated_peak": "10:41",
"typical_wake": "07:00"
},
"plan": {
"summary": "Your rhythm already lines up well with 14:00. The plan is to arrive rested: clear sleep debt over the next 13 nights and protect the night before.",
"nights": [
{ "date": "2026-07-04", "target_wake": "07:00", "target_bed": "23:15", "target_sleep_hours": 7.75, "focus": "clear sleep debt" },
{ "date": "2026-07-16", "target_wake": "07:00", "target_bed": "23:15", "target_sleep_hours": 7.75, "focus": "protect the night before" }
],
"event_day": {
"date": "2026-07-16",
"target_wake": "07:00",
"projected_peak": "10:00",
"projected_capacity_at_target": 80.8,
"projected_window_floor": 888">null,
"framing": "This puts your projected capacity around 80.8/100 across 14:00: odds in your favour, not a guarantee."
}
},
"tonight": {
"action": "one_gentle_nudge",
"detail": "Aim for lights-out around 23:15 tonight to start clearing sleep debt. Just one small step."
},
"guardrails": {
"health_floor_respected": 888">true,
"min_sleep_hours_prescribed": 7.0,
"claims_basis": "internal_dps",
"style": "pull_not_push"
},
"disclaimer": "WhenPeak suggestions raise the odds your best hours land where you need them. They are not medical advice and never ask you to cut sleep."
}Response Format
| Field | Type | Description |
|---|---|---|
| dps | float (0.0–100.0) | Daily Performance Score. Always a float: type as float/double, not int |
| peak_1 | object | First cognitive peak: { "time": "HH:MM", "hour": int, "value": float } |
| peak_2 | object | Second cognitive peak: same shape as peak_1 |
| dip | object | Natural energy dip: same shape as peak_1 |
| chronotype | string | "Lark" / "Third Bird" / "Night Owl" |
| confidence | string | "low" / "medium" / "high" |
| upgrade_prompt | string | Shown when confidence is low: prompt to connect wearable data |
All score values (dps, peak value, dip value) are floats. Typing them as int will cause a validation error on real data, e.g. dps: 87.8, not 87.
Integrations
ChatGPT
Available now as a GPT. Users ask in plain English; WhenPeak predicts their peak.
Try the WhenPeak GPT →Claude (MCP)
MCP server deployed. Remote integration rolling out.
https://mcp.whenpeak.com/sseAny AI Agent
OpenAPI spec available for any tool-calling LLM:
https://api.whenpeak.com/openapi.jsonTool definitions for Claude and OpenAI function calling are available in the GitHub repo.