A comprehensive Python client library for the Oura Ring API v2 with full type annotations, automatic retry logic, and extensive endpoint coverage.
- âś… Complete API Coverage: All 20+ Oura API v2 endpoints implemented
- đź”’ Type Safety: Full type annotations with Pydantic models
- 🔄 Automatic Retry Logic: Built-in exponential backoff for transient failures
- ⚡ Modern Python: Support for Python 3.7+
- 🎯 Intuitive Interface: Simple, consistent API across all endpoints
- 📊 Comprehensive Error Handling: Detailed error messages and proper exception hierarchy
- đź”§ Flexible Configuration: Customizable retry behavior and timeout settings
pip install oura-api-clientgit clone https://github.com/godely/ourapy.git
cd ourapy
pip install -e .You'll need an API token to use the Oura API. You can get one from the Oura Cloud Personal Access Tokens page.
from oura_api_client import OuraClient
# Initialize the client with your access token
client = OuraClient("your_access_token_here")
# Get sleep data for the past week
sleep_data = client.daily_sleep.get_daily_sleep_documents(
start_date="2024-12-15",
end_date="2024-12-22"
)
for sleep in sleep_data.data:
print(f"Date: {sleep.day}")
print(f"Sleep Score: {sleep.score}")
print(f"Total Sleep: {sleep.total_sleep_duration / 3600:.1f} hours")
print(f"REM Sleep: {sleep.rem_sleep_duration / 3600:.1f} hours")
print("---")
# Get activity data with automatic retry on failure
activity = client.daily_activity.get_daily_activity_documents(
start_date="2024-12-01"
)
for day in activity.data:
print(f"Steps: {day.steps}, Calories: {day.active_calories}")The client includes comprehensive error handling with automatic retries:
from oura_api_client import OuraClient, OuraAPIError
from oura_api_client.utils import RetryConfig
# Configure custom retry behavior
retry_config = RetryConfig(
max_retries=5,
base_delay=2.0,
max_delay=60.0
)
client = OuraClient("your_token", retry_config=retry_config)
try:
readiness = client.daily_readiness.get_daily_readiness_document("invalid_id")
except OuraAPIError as e:
print(f"API Error: {e}")
print(f"Status Code: {e.status_code}")
print(f"Endpoint: {e.endpoint}")The client automatically retries on:
- 429 (Rate Limited) - respects Retry-After header
- 500, 502, 503, 504 (Server Errors)
- Connection timeouts
- Network errors
- Daily Activity:
client.daily_activity- Steps, calories, activity levels - Daily Readiness:
client.daily_readiness- Recovery and readiness scores - Daily Sleep:
client.daily_sleep- Sleep stages, duration, and quality - Daily Stress:
client.daily_stress- Stress levels and recovery metrics - Daily Resilience:
client.daily_resilience- Long-term stress resilience - Daily SpO2:
client.daily_spo2- Blood oxygen levels - Daily Cardiovascular Age:
client.daily_cardiovascular_age- Cardiovascular health metrics
- Sleep Sessions:
client.sleep- Detailed sleep session data - Activity Sessions:
client.session- Workout and activity sessions - Heart Rate:
client.heartrate- Time-series heart rate data - HRV Data: Available within sleep and session data
- Workouts:
client.workout- Detailed workout metrics - VO2 Max:
client.vo2_max- Cardio fitness metrics
- Personal Info:
client.personal- User profile information - Ring Configuration:
client.ring_configuration- Ring settings and info - Rest Mode Periods:
client.rest_mode_period- Rest mode status - Tags:
client.tag- User-generated tags - Enhanced Tags:
client.enhanced_tag- AI-enhanced tag data - Sleep Time:
client.sleep_time- Recommended bedtime windows
get_xxx_documents()- Get multiple documents with paginationget_xxx_document(document_id)- Get a single document by IDstream()- NEW! Stream all documents automatically handling pagination- Date filtering with
start_dateandend_date(accepts strings or date objects) - Pagination with
next_token
The easiest way to work with paginated data is using the .stream() methods, which automatically handle pagination and yield individual items:
# Stream all sleep data seamlessly - no manual pagination needed!
for sleep_record in client.daily_sleep.stream(start_date="2024-01-01"):
print(f"Date: {sleep_record.day}, Score: {sleep_record.score}")
# Stream heart rate data for analysis
for hr_sample in client.heartrate.stream(start_date="2024-12-20"):
print(f"Heart rate: {hr_sample.bpm} at {hr_sample.timestamp}")
# Stream activity data with date range
for activity in client.daily_activity.stream(
start_date="2024-01-01",
end_date="2024-01-31"
):
print(f"Steps: {activity.steps}, Calories: {activity.active_calories}")
# Stream session data
for session in client.session.stream(start_date="2024-01-01"):
print(f"Session: {session.type} from {session.start_datetime}")For more control over pagination, you can still handle it manually:
# Iterate through all sleep data using manual pagination
next_token = None
all_sleep_data = []
while True:
response = client.daily_sleep.get_daily_sleep_documents(
start_date="2024-01-01",
next_token=next_token
)
all_sleep_data.extend(response.data)
if not response.next_token:
break
next_token = response.next_token
print(f"Total sleep records: {len(all_sleep_data)}")# Get detailed heart rate data
hr_data = client.heartrate.get_heartrate(start_date="2024-12-20")
for hr in hr_data.data:
# hr.bpm is time-series data with interval and items
print(f"Started at: {hr.bpm.timestamp}")
print(f"Interval: {hr.bpm.interval} seconds")
print(f"Heart rates: {hr.bpm.items[:10]}...") # First 10 readingsfrom datetime import date, timedelta
# All of these work:
today = date.today()
yesterday = today - timedelta(days=1)
# Using date objects
data1 = client.daily_activity.get_daily_activity_documents(
start_date=yesterday,
end_date=today
)
# Using ISO format strings
data2 = client.daily_activity.get_daily_activity_documents(
start_date="2024-12-01",
end_date="2024-12-20"
)git clone https://github.com/godely/ourapy.git
cd ourapy
pip install -r requirements-dev.txt
pip install -e .# Run all tests with parallel execution
pytest -n auto
# Run with coverage
pytest --cov=oura_api_client
# Run specific test file
pytest tests/test_client.py -v# Linting
flake8 oura_api_client/ tests/
# Type checking (if mypy is installed)
mypy oura_api_client/This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.