How to Integrate the Oura API (2026)
Last verified October 17, 2026 · 9 min read
Sources checked (4) · October 17, 2026
- cloud.ouraring.com/docs/ — getting started: account, API application, ten-user default limit, review, V1 removal date
- cloud.ouraring.com/docs/authentication — authorization and token URLs, universal link, scopes, PKCE, single-use refresh tokens, client-side flow, revocation
- cloud.ouraring.com/docs/error-handling — error responses, reused refresh tokens, 5,000 requests per 5 minutes
- cloud.ouraring.com/v2/static/json/openapi-1.42.json — API V2 spec 1.42: personal access token status, user limit and approval, consent, 403 meaning, two-layer rate limits, data types, sandbox, webhooks, FAQ
Covered here:Oura
Three things shape an Oura build before any code, all from Oura's own documentation as read on October 17, 2026:
- V2 and OAuth only. Oura's spec describes API V2 as "the only available integration point for Oura data", says the V1 API has been sunset (its getting-started page dates the removal to January 22, 2024), and says personal access tokens "were deprecated in December 2025 and are no longer available for use". If a tutorial tells you to paste a personal token, it is out of date; Oura personal access tokens deprecated covers the switch.
- A 10-user limit until Oura approves the app. API applications are limited to 10 users before requiring approval from Oura, with no limit once approved, and you submit the app for review from its page in the developer portal.
- Consent is per data type, and membership matters. Oura says users must consent to share each data type your app has access to, and its API returns 403 when a request needs more permissions "or the user's Oura subscription has expired".
If you are still choosing a provider, see wearable data APIs; the neighboring recovery-band integration is in the WHOOP API guide. Oura's access requirements, and every page on this site that covers Oura, are collected in the Oura API overview. Scripting in Python? oura-ring vs oura (python-ouraring) compares the two clients and which one documents the OAuth2 flow.
What you'll need#
- An Oura account, created in the Oura iOS or Android app, which you also use to sign in at
https://cloud.ouraring.com. - An API application registered under API Applications, which gives you a client ID and client secret. Oura says the secret should not be shared, so keep it on your server.
- At least one redirect URI on the application; Oura treats the list as a whitelist the
redirect_uriparameter must match exactly. - A backend for the code exchange, token refresh and webhook endpoint.
Auth and scopes#
Oura uses OAuth 2.0. Its authorization URL is https://cloud.ouraring.com/oauth/authorize and its token URL https://api.ouraring.com/oauth/token. Oura also now offers https://developer.ouraring.com/authorize as an alternative authorization URL for new OAuth clients; on mobile devices with the Oura app installed it can open the consent page inside the app.
Oura's authentication page lists eight scopes. Request only what you use, because the user can enable and disable scopes at consent:
| Scope | What Oura says it covers |
|---|---|
email | Email address of the user |
personal | Gender, age, height, weight |
daily | Daily summaries of sleep, activity and readiness |
heartrate | Time series heart rate for Gen 3 users |
workout | Auto-detected and user-entered workouts |
tag | User-entered tags |
session | Guided and unguided sessions in the Oura app |
spo2 | Daily SpO2 average recorded during sleep |
The OpenAPI spec's own scope list adds a ninth, heart_health ("Heart health data"), which the authentication page does not mention yet; test it before you depend on it.
The scope parameter is space-separated, and leaving it blank requests every scope. Oura marks PKCE as recommended: generate a code_verifier of 43 to 128 characters, send its SHA-256 hash base64url-encoded as code_challenge with code_challenge_method=S256, and keep the verifier for the token exchange.
GET https://cloud.ouraring.com/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fyourapp.example.com%2Fcallback
&scope=daily%20heartrate%20workout%20spo2
&state=RANDOM_ANTI_CSRF_STRING
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
On consent Oura redirects with code and your state; a refusal returns error=access_denied. Oura says the scope parameter on that redirect is deprecated and may be omitted, so do not rely on it. Exchange the code from your server; credentials can go in the form body or as HTTP Basic:
curl -s -X POST "https://api.ouraring.com/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=https://yourapp.example.com/callback" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "code_verifier=YOUR_CODE_VERIFIER"
The response holds access_token, expires_in and refresh_token. Read expires_in rather than hard-coding a lifetime: Oura's spec says access tokens typically expire after 24 hours in one troubleshooting section and typically last 30 days in its FAQ. Oura's refresh token is single-use: it "is invalidated after being used", so store the new pair after every refresh, and its error docs say a reused refresh token comes back as "Token already used or revoked". Oura also documents a client-side flow (response_type=token) with no refresh token, where the token currently expires after 30 days and the user must re-authenticate; use it only where you have no backend. To revoke a token, for example when a user disconnects, call https://api.ouraring.com/oauth/revoke?access_token=ACCESS_TOKEN.
curl -s -X POST "https://api.ouraring.com/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=STORED_REFRESH_TOKEN" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
Data: the usercollection endpoints#
All data lives under https://api.ouraring.com/v2/usercollection/. Oura's V2 OpenAPI spec, as published on October 17, 2026, lists these data types, each as a list endpoint and, except heartrate, personal_info and ring_battery_level, a single-document endpoint by document_id:
| Endpoint | What Oura's spec says it holds |
|---|---|
daily_activity, daily_sleep, daily_readiness | Daily activity summaries and activity levels, daily sleep, and readiness |
daily_spo2 | Daily SpO2 average, available only for users with a Gen 3 ring |
daily_stress | Minutes in high stress and high recovery each day |
daily_resilience, daily_cardiovascular_age, vO2_max | Resilience, cardiovascular age estimate, VO2 max |
sleep | Sleep periods; a user can have several per day |
sleep_time | Recommended bedtime window, calculated from sleep data |
heartrate | Time-series heart rate in 5-minute increments |
workout, session | Workouts, and guided or unguided sessions |
enhanced_tag, tag | User-entered tags; tag is deprecated in favor of enhanced_tag |
rest_mode_period, ring_configuration, ring_battery_level, personal_info | Rest mode periods, ring model, size and color, battery level, the user's profile |
curl -s -H "Authorization: Bearer ${OURA_ACCESS_TOKEN}" \
"https://api.ouraring.com/v2/usercollection/daily_activity?start_date=2026-10-01&end_date=2026-10-16"
List responses wrap records in a data array with a next_token; repeat the request with it until it comes back null. The same spec also has a sandbox endpoint for each data type under /v2/sandbox/usercollection/, serving fake data "that you can access without an Oura account", so you can build before you have connected users. Oura notes that sandbox calls share your rate limit with the real data endpoints.
Expect data to arrive on the user's schedule, not yours: Oura notes that sleep data requires users to open the Oura app, while daily activity and stress may sync in the background.
Webhooks#
Oura calls webhooks "the preferred way to consume Oura data" and says customers who implement them properly have not hit rate limits. Its recommended pattern is one request for historical data when a user first connects, then webhooks for everything after.
Create one subscription per combination of event_type (create, update or delete) and data_type by calling POST /v2/webhook/subscription with your app's x-client-id and x-client-secret headers and a JSON body with callback_url, verification_token, event_type and data_type. The data types you can subscribe to are tag, enhanced_tag, workout, session, sleep, daily_sleep, daily_readiness, daily_activity, daily_spo2, sleep_time, rest_mode_period, ring_configuration, daily_stress, daily_cardiovascular_age, daily_resilience and vo2_max.
Oura verifies the endpoint with a GET carrying verification_token and challenge; respond with {"challenge": "..."} to activate the subscription. After that, each event is a POST whose JSON body carries event_type, data_type, object_id and user_id, with x-oura-signature and x-oura-timestamp headers:
// Oura's documented check: HMAC-SHA256 of timestamp + JSON body, keyed by the client secret, hex, upper-case
const crypto = require("crypto");
function verifyOuraSignature(headers, body, clientSecret) {
const hmac = crypto.createHmac("sha256", clientSecret);
hmac.update(headers["x-oura-timestamp"] + JSON.stringify(body));
return hmac.digest("hex").toUpperCase() === headers["x-oura-signature"];
}
Respond with a 2xx in under 10 seconds and fetch the record by object_id afterwards. Oura retries failed deliveries (10 retries each for 4xx responses, 5xx responses and timeouts, for about an hour), and a 410 response cancels the subscription, as does DELETE /v2/webhook/subscription/{id}. Every subscription carries an expiration_time; renew it with PUT /v2/webhook/subscription/renew/{id} before it lapses. Notifications arrive about 30 seconds after data syncs from the app; for longer outages, run a reconciliation job.
Rate limits#
Oura's V2 spec describes two layers: a per-access-token limit and a per-application limit across all of an app's user tokens. Either returns 429 with Retry-After, X-RateLimit-Limit, X-RateLimit-Window, X-RateLimit-Reset and an X-RateLimit-Tier header naming the layer that fired, and Oura says to prefer those headers over fixed-interval backoff. That section names no figure; the FAQ in the same spec and Oura's error-handling page both give one, 5,000 requests per 5-minute period. Oura's FAQ also suggests fetching history in date ranges of about one to three months at a time and paging with next_token. Contact Oura at its API support address if you expect to need more, and use webhooks first.
What changed recently#
| Date | Change (Oura's documentation) |
|---|---|
| January 22, 2024 | V1 API removed |
| December 2025 | Personal access tokens deprecated; the spec says they are no longer available for use |
| Undated, seen October 17, 2026 | A universal-link authorization URL at developer.ouraring.com/authorize that Oura labels new; PKCE marked recommended; two-layer rate limits with an X-RateLimit-Tier header; a heart_health scope in the spec. None of these were in this guide's July 2026 version |
Gotchas and production notes#
- OAuth only, PKCE recommended. Personal tokens are gone; build the authorization-code flow with PKCE from day one.
- The 10-user limit blocks scale, not testing. Submit the app for review from its page well before launch.
- A 403 is usually scope or subscription, not a bad token. Oura's 403 means the resource needs more permissions or the user's Oura subscription has expired; a missing-scope error lists the scopes the token needs.
- Refresh tokens are single-use. Store the new pair every time; reusing one fails as already used or revoked.
- Sleep arrives when the user opens the app. Do not treat a missing night as an error until the user has synced.
- Some data needs newer hardware. Oura's spec says daily SpO2 data is only available for users with a Gen 3 ring, and its authentication page describes the
heartratescope as time series for Gen 3 users. - Dates are the user's local dates. Oura says date parameters are interpreted in the user's local time zone, and timestamps come back in ISO 8601 with time zone information.
For where Oura fits among recovery and sleep sources, see wearable data APIs; for what the membership costs your users, see Oura API pricing.
Frequently asked questions
- Does the Oura API still support personal access tokens?
- No. Oura's API spec says personal access tokens were deprecated in December 2025 and are no longer available for use, and that API V2 is the only available integration point. Build the OAuth 2.0 authorization-code flow instead, with PKCE, which Oura's authentication page marks as recommended.
- Does the Oura API require approval?
- Only to grow past 10 users. Oura's spec says API applications are limited to 10 users before requiring approval from Oura and that there is no limit once an application is approved. When development is done, you submit the app for review from its page in the developer portal.
- What OAuth endpoints and scopes does Oura use?
- Authorize at https://cloud.ouraring.com/oauth/authorize, or the newer universal link https://developer.ouraring.com/authorize, and exchange or refresh tokens at https://api.ouraring.com/oauth/token. The authentication page lists eight scopes: email, personal, daily, heartrate, workout, tag, session and spo2, and the OpenAPI spec also lists heart_health. Users can switch scopes off at consent.
- Why am I getting a 403 from a usercollection endpoint?
- Oura's spec says a 403 means the requested resource requires additional permissions or the user's Oura subscription has expired. Check the scopes on the token first: a missing-scope error lists the scopes you need, and the fix is asking the user to re-authorize. If the scopes are right, the user's membership has probably lapsed.
- Does the Oura API have webhooks and rate limits?
- Yes to both. Oura calls webhooks the preferred way to consume its data: you subscribe per event type and data type, answer a verification challenge, and check each event's HMAC signature. Rate limits apply per access token and per application, and Oura's FAQ and error-handling page give 5,000 requests per 5-minute period.
Keep reading
Elsewhere on the site
Pages that share this one’s concepts and sources, from other sections.
Next steps
Was this page useful?
Independent comparison, last reviewed October 17, 2026. Pricing, rate limits, and feature availability change often — confirm current details in each provider’s official documentation before you commit. Product and company names are trademarks of their respective owners; AIFitnessAPI is not affiliated with, endorsed by, or sponsored by any product listed here.
← All integration guides · by AIFitnessAPI