ExOura (ex_oura v2.0.2)

View Source

Documentation for Oura API

Configuration

ExOura can be configured in your config files:

config :ex_oura,
  timeout: 10_000,
  rate_limiting: [
    enabled: true,          # Enable/disable rate limiting (default: true)
    daily_limit: 5000,      # Daily request limit (default: 5000)
    per_minute_limit: 300   # Per-minute request limit (default: 300)
  ],
  retry: [
    max_retries: 3          # Maximum retry attempts (default: 3)
  ],
  oauth2: [
    client_id: "your_client_id",
    client_secret: "your_client_secret",
    redirect_uri: "your_redirect_uri"
  ]

ExOura supports OAuth2 authentication, which is the recommended approach since Personal Access Tokens are being deprecated by Oura by the end of 2025.

OAuth2 Setup

  1. Register your application at https://cloud.ouraring.com/oauth/applications
  2. Configure your OAuth2 credentials (see Configuration above)
  3. Implement the OAuth2 flow in your application

OAuth2 Flow Example

# Step 1: Generate authorization URL
authorization_url = ExOura.OAuth2.authorization_url([
  scopes: ["daily", "heartrate", "personal"],
  state: "your_state_parameter"
])

# Step 2: After user authorizes, exchange code for tokens
{:ok, tokens} = ExOura.OAuth2.get_token("authorization_code_from_callback")

# Step 3: Use tokens with the client
{:ok, client} = ExOura.Client.start_link([
  access_token: tokens.access_token,
  refresh_token: tokens.refresh_token
])

# Step 4: Make API calls
{:ok, activity_data} = ExOura.multiple_daily_activity(~D[2025-01-01], ~D[2025-01-31])

# Step 5: Refresh tokens when needed
if ExOura.OAuth2.token_expired?(tokens) do
  {:ok, new_tokens} = ExOura.OAuth2.refresh_token(tokens.refresh_token)
  # Update your stored tokens...
end

Rate Limiting

Rate limiting is enabled by default and follows the Oura API limits:

  • 5000 requests per day
  • 300 requests per minute

You can disable rate limiting entirely by setting enabled: false in the configuration, or adjust the limits if needed. When disabled, the client will not track or enforce any rate limits, but will still handle rate limit responses from the API server in the retry logic.

Retry Logic

ExOura uses Req's built-in retry mechanism with intelligent error handling:

  • Automatically retries on server errors (5xx), timeouts (408), and rate limits (429)
  • Exponential backoff with jitter to avoid thundering herd problems
  • Configurable maximum retry attempts
  • Does not retry on client errors (4xx except 408 and 429)

Summary

Functions

Fetches all daily activity data across all pages for the given date range.

Fetches all daily readiness data across all pages for the given date range.

Fetches all daily sleep data across all pages for the given date range.

Fetches all sleep data across all pages for the given date range.

Fetches all workout data across all pages for the given date range.

Generates the OAuth2 authorization URL to redirect users to.

Returns all available OAuth2 scopes.

Returns the default OAuth2 scopes used when none are specified.

Exchanges an authorization code for access and refresh tokens.

List Webhook Subscription

Retrieves multiple daily activity records for a specified date range.

Retrieves multiple daily sleep records for a specified date range.

Retrieves multiple workout records for a specified date range.

Refreshes an access token using a refresh token.

Sets the OAuth2 authentication configuration for the client in situations where we want to reconfigure the client at runtime.

Retrieves a single daily activity record by its document ID.

Retrieves a single daily sleep record by its document ID.

Retrieves personal information for the authenticated user.

Single Tag (Deprecated)

Retrieves a single workout record by its document ID.

Returns a stream of all daily activity data for the given date range.

Returns a stream of all workout data for the given date range.

Checks if an access token is expired or will expire soon.

Types

document_id()

@type document_id() :: String.t()

end_date()

@type end_date() :: Date.t()

error()

@type error() :: {:error, ExOura.Client.HTTPValidationError.t()}

next_token()

@type next_token() :: String.t() | nil

opts()

@type opts() :: Keyword.t()

start_date()

@type start_date() :: Date.t()

webhook()

@type webhook() ::
  ExOura.Client.CreateWebhookSubscriptionRequest.t()
  | ExOura.Client.UpdateWebhookSubscriptionRequest.t()

webhook_id()

@type webhook_id() :: String.t()

Functions

all_daily_activity(start_date, end_date, opts \\ [])

@spec all_daily_activity(start_date(), end_date(), opts()) :: {:ok, list()} | error()

Fetches all daily activity data across all pages for the given date range.

This is a convenience function that automatically handles pagination using the Pagination module.

Examples

iex> ExOura.all_daily_activity(~D[2025-01-01], ~D[2025-01-31])
{:ok, [%{id: "activity_1", ...}, %{id: "activity_2", ...}]}

all_daily_readiness(start_date, end_date, opts \\ [])

@spec all_daily_readiness(start_date(), end_date(), opts()) :: {:ok, list()} | error()

Fetches all daily readiness data across all pages for the given date range.

all_daily_sleep(start_date, end_date, opts \\ [])

@spec all_daily_sleep(start_date(), end_date(), opts()) :: {:ok, list()} | error()

Fetches all daily sleep data across all pages for the given date range.

all_sleep(start_date, end_date, opts \\ [])

@spec all_sleep(start_date(), end_date(), opts()) :: {:ok, list()} | error()

Fetches all sleep data across all pages for the given date range.

all_workouts(start_date, end_date, opts \\ [])

@spec all_workouts(start_date(), end_date(), opts()) :: {:ok, list()} | error()

Fetches all workout data across all pages for the given date range.

authorization_url(opts \\ [])

Generates the OAuth2 authorization URL to redirect users to.

Examples

authorization_url = ExOura.authorization_url()
authorization_url = ExOura.authorization_url(scopes: ["daily", "heartrate"], state: "random_state")

available_scopes()

Returns all available OAuth2 scopes.

Examples

ExOura.available_scopes()
# => ["email", "personal", "daily", "heartrate", "workout", "tag", "session", "spo2"]

create_webhook_subscription(webhook, opts \\ [])

@spec create_webhook_subscription(webhook(), opts()) ::
  {:ok, ExOura.Client.WebhookSubscriptionModel.t()} | error()

Create Webhook Subscription

default_scopes()

Returns the default OAuth2 scopes used when none are specified.

Examples

ExOura.default_scopes()
# => ["personal", "daily"]

delete_webhook_subscription(webhook_id, opts \\ [])

@spec delete_webhook_subscription(webhook_id(), opts()) :: :ok | error()

Delete Webhook Subscription

get_token(code, opts \\ [])

Exchanges an authorization code for access and refresh tokens.

Examples

{:ok, tokens} = ExOura.get_token("authorization_code_from_callback")

get_webhook_subscription(webhook_id, opts \\ [])

@spec get_webhook_subscription(webhook_id(), opts()) ::
  {:ok, ExOura.Client.WebhookSubscriptionModel.t()} | error()

Get Webhook Subscription

list_webhook_subscriptions(opts \\ [])

@spec list_webhook_subscriptions(opts()) ::
  {:ok, [ExOura.Client.WebhookSubscriptionModel.t()]} | error()

List Webhook Subscription

multiple_daily_activity(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_daily_activity(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseDailyActivityModel.t()} | error()

Retrieves multiple daily activity records for a specified date range.

Returns paginated daily activity data including steps, calories, activity score, and more. This is one of the most commonly used endpoints for tracking daily movement and activity patterns.

Parameters

  • start_date - Start date for the data range (Date struct, inclusive)
  • end_date - End date for the data range (Date struct, inclusive)
  • next_token - Optional pagination token from previous response
  • opts - Additional options (e.g., timeout)

Examples

{:ok, activities} = ExOura.multiple_daily_activity(~D[2025-01-01], ~D[2025-01-31])
activities.data |> Enum.each(&IO.inspect/1)

multiple_daily_cardiovascular_age(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_daily_cardiovascular_age(
  start_date(),
  end_date(),
  next_token(),
  opts()
) ::
  {:ok, ExOura.Client.MultiDocumentResponseDailyCardiovascularAgeModel.t()}
  | error()

Multiple Daily Cardiovascular Age

multiple_daily_readiness(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_daily_readiness(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseDailyReadinessModel.t()} | error()

Multiple Daily Readiness

multiple_daily_resilience(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_daily_resilience(
  start_date(),
  end_date(),
  next_token(),
  opts()
) ::
  {:ok, ExOura.Client.MultiDocumentResponseDailyResilienceModel.t()} | error()

Multiple Daily Resilience

multiple_daily_sleep(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_daily_sleep(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseDailySleepModel.t()} | error()

Retrieves multiple daily sleep records for a specified date range.

Returns comprehensive sleep data including sleep score, duration, sleep stages, and sleep quality metrics. Essential for sleep tracking and analysis applications.

Parameters

  • start_date - Start date for the data range (Date struct, inclusive)
  • end_date - End date for the data range (Date struct, inclusive)
  • next_token - Optional pagination token from previous response
  • opts - Additional options (e.g., timeout)

Examples

{:ok, sleep_data} = ExOura.multiple_daily_sleep(~D[2025-01-01], ~D[2025-01-31])
sleep_data.data |> Enum.each(fn sleep ->
  IO.puts("Sleep score: #{sleep.score}, Duration: #{sleep.total_sleep_duration}min")
end)

multiple_daily_sp02(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_daily_sp02(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseDailySpO2Model.t()} | error()

Multiple Daily Sp02

multiple_daily_stress(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_daily_stress(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseDailyStressModel.t()} | error()

Multiple Daily Stress

multiple_enhanced_tag(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_enhanced_tag(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseEnhancedTagModel.t()} | error()

Multiple Enhanced Tag

multiple_heart_rate(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_heart_rate(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.TimeSeriesResponseHeartRateModel.t()} | error()

Multiple Heart Rate

multiple_rest_mode_period(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_rest_mode_period(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseRestModePeriodModel.t()} | error()

Multiple Rest Mode Period

multiple_ring_configuration(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_ring_configuration(
  start_date(),
  end_date(),
  next_token(),
  opts()
) ::
  {:ok, ExOura.Client.MultiDocumentResponseRingConfigurationModel.t()} | error()

Multiple Ring Configuration

multiple_session(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_session(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseSessionModel.t()} | error()

Multiple Session

multiple_sleep(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_sleep(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseSleepModel.t()} | error()

Multiple Sleep

multiple_sleep_time(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_sleep_time(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseSleepTimeModel.t()} | error()

Multiple Sleep Time

multiple_tag(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_tag(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseTagModel.t()} | error()

Multiple Tag (Deprecated)

Note: This endpoint is deprecated. Use Enhanced Tags instead.

multiple_vo2_max(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_vo2_max(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseVo2MaxModel.t()} | error()

Multiple Vo2 Max

multiple_workout(start_date, end_date, next_token \\ nil, opts \\ [])

@spec multiple_workout(
  start_date(),
  end_date(),
  next_token(),
  opts()
) :: {:ok, ExOura.Client.MultiDocumentResponseWorkoutModel.t()} | error()

Retrieves multiple workout records for a specified date range.

Returns both auto-detected and manually logged workout sessions with detailed metrics including duration, intensity, calories, and heart rate data.

Parameters

  • start_date - Start date for the data range (Date struct, inclusive)
  • end_date - End date for the data range (Date struct, inclusive)
  • next_token - Optional pagination token from previous response
  • opts - Additional options (e.g., timeout)

Examples

{:ok, workouts} = ExOura.multiple_workout(~D[2025-01-01], ~D[2025-01-31])
workouts.data |> Enum.each(fn workout ->
  IO.puts("#{workout.activity}: #{workout.duration}s, #{workout.calories} cal")
end)

refresh_token(refresh_token, opts \\ [])

Refreshes an access token using a refresh token.

Examples

{:ok, new_tokens} = ExOura.refresh_token(old_tokens.refresh_token)

renew_webhook_subscription(webhook_id, opts \\ [])

@spec renew_webhook_subscription(webhook_id(), opts()) ::
  {:ok, ExOura.Client.WebhookSubscriptionModel.t()} | error()

Renew Webhook Subscription

set_auth_config(auth_config)

@spec set_auth_config(ExOura.OAuth2.oauth2_config()) :: :ok | {:error, term()}

Sets the OAuth2 authentication configuration for the client in situations where we want to reconfigure the client at runtime.

single_daily_activity(document_id, opts \\ [])

@spec single_daily_activity(document_id(), opts()) ::
  {:ok, ExOura.Client.DailyActivityModel.t()} | error()

Retrieves a single daily activity record by its document ID.

Use this for fetching detailed activity information for a specific day when you have the document ID.

Parameters

  • document_id - Unique identifier for the daily activity document
  • opts - Additional options (e.g., timeout)

Examples

{:ok, activity} = ExOura.single_daily_activity("daily_activity_2025-01-15")
IO.puts("Steps: #{activity.steps}")

single_daily_cardiovascular_age(document_id, opts \\ [])

@spec single_daily_cardiovascular_age(document_id(), opts()) ::
  {:ok, ExOura.Client.DailyCardiovascularAgeModel.t()} | error()

Single Daily Cardiovascular Age

single_daily_readiness(document_id, opts \\ [])

@spec single_daily_readiness(document_id(), opts()) ::
  {:ok, ExOura.Client.DailyReadinessModel.t()} | error()

Single Daily Readiness

single_daily_resilience(document_id, opts \\ [])

@spec single_daily_resilience(document_id(), opts()) ::
  {:ok, ExOura.Client.DailyResilienceModel.t()} | error()

Single Daily Resilience

single_daily_sleep(document_id, opts \\ [])

@spec single_daily_sleep(document_id(), opts()) ::
  {:ok, ExOura.Client.DailySleepModel.t()} | error()

Retrieves a single daily sleep record by its document ID.

Use this for detailed sleep analysis of a specific night's sleep session.

Parameters

  • document_id - Unique identifier for the daily sleep document
  • opts - Additional options (e.g., timeout)

Examples

{:ok, sleep} = ExOura.single_daily_sleep("daily_sleep_2025-01-15")
IO.puts("Deep sleep: #{sleep.deep_sleep_duration} minutes")

single_daily_sp02(document_id, opts \\ [])

@spec single_daily_sp02(document_id(), opts()) ::
  {:ok, ExOura.Client.DailySpO2Model.t()} | error()

Single Daily Sp02

single_daily_stress(document_id, opts \\ [])

@spec single_daily_stress(document_id(), opts()) ::
  {:ok, ExOura.Client.DailyStressModel.t()} | error()

Single Daily Stress

single_enhanced_tag(document_id, opts \\ [])

@spec single_enhanced_tag(document_id(), opts()) ::
  {:ok, ExOura.Client.EnhancedTagModel.t()} | error()

Single Enhanced Tag

single_personal_info(opts \\ [])

@spec single_personal_info(opts()) ::
  {:ok, ExOura.Client.PersonalInfoResponse.t()} | error()

Retrieves personal information for the authenticated user.

Returns demographic and physical information such as age, weight, height, and biological sex. Requires appropriate OAuth2 scopes ('personal' for demographics, 'email' for email).

Parameters

  • opts - Additional options (e.g., timeout)

Examples

{:ok, info} = ExOura.single_personal_info()
IO.puts("Age: #{info.age}, Weight: #{info.weight}kg")

single_rest_mode_period(document_id, opts \\ [])

@spec single_rest_mode_period(document_id(), opts()) ::
  {:ok, ExOura.Client.RestModePeriodModel.t()} | error()

Single Rest Mode Period

single_ring_configuration(document_id, opts \\ [])

@spec single_ring_configuration(document_id(), opts()) ::
  {:ok, ExOura.Client.RingConfigurationModel.t()} | error()

Single Ring Configuration

single_session(document_id, opts \\ [])

@spec single_session(document_id(), opts()) ::
  {:ok, ExOura.Client.SessionModel.t()} | error()

Single Session

single_sleep(document_id, opts \\ [])

@spec single_sleep(document_id(), opts()) ::
  {:ok, ExOura.Client.SleepModel.t()} | error()

Single Sleep

single_sleep_time(document_id, opts \\ [])

@spec single_sleep_time(document_id(), opts()) ::
  {:ok, ExOura.Client.SleepTimeModel.t()} | error()

Single Sleep Time

single_tag(document_id, opts \\ [])

@spec single_tag(document_id(), opts()) :: {:ok, ExOura.Client.TagModel.t()} | error()

Single Tag (Deprecated)

Note: This endpoint is deprecated. Use Enhanced Tags instead.

single_vo2_max(document_id, opts \\ [])

@spec single_vo2_max(document_id(), opts()) ::
  {:ok, ExOura.Client.Vo2MaxModel.t()} | error()

Single Vo2 Max

single_workout(document_id, opts \\ [])

@spec single_workout(document_id(), opts()) ::
  {:ok, ExOura.Client.WorkoutModel.t()} | error()

Retrieves a single workout record by its document ID.

Use this for detailed analysis of a specific workout session including heart rate zones and comprehensive exercise metrics.

Parameters

  • document_id - Unique identifier for the workout document
  • opts - Additional options (e.g., timeout)

Examples

{:ok, workout} = ExOura.single_workout("workout_2025-01-15T14-30-00")
IO.puts("Average HR: #{workout.average_heart_rate} bpm")

stream_daily_activity(start_date, end_date, opts \\ [])

@spec stream_daily_activity(start_date(), end_date(), opts()) :: Enumerable.t()

Returns a stream of all daily activity data for the given date range.

This is memory-efficient for processing large datasets as it streams data page by page rather than loading everything into memory at once.

Examples

iex> ExOura.stream_daily_activity(~D[2025-01-01], ~D[2025-01-31])
...> |> Stream.filter(& &1.score > 80)
...> |> Enum.take(10)

stream_workouts(start_date, end_date, opts \\ [])

@spec stream_workouts(start_date(), end_date(), opts()) :: Enumerable.t()

Returns a stream of all workout data for the given date range.

token_expired?(tokens, buffer_seconds \\ 300)

Checks if an access token is expired or will expire soon.

Examples

ExOura.token_expired?(tokens)
ExOura.token_expired?(tokens, 3600)  # 1 hour buffer

update_webhook_subscription(webhook_id, webhook, opts \\ [])

@spec update_webhook_subscription(webhook_id(), webhook(), opts()) ::
  {:ok, ExOura.Client.WebhookSubscriptionModel.t()} | error()

Update Webhook Subscription