v0.9.1 · MIT licensed · Bun + TypeScript

Own your Oura data. Sleep, readiness, HRV and workouts, straight to your terminal.

Pull your Oura Ring data from the cloud, cache it locally in SQLite, and read it on your own terms: weekly digests, daily scores, seventeen raw V2 collections, pipe-friendly JSON. Without the mobile app, and without telemetry.

Terminal recording of oura-cli running doctor, db today, db week, report, db trends, db rows and describe, on synthetic demo data
$ bun add -g @drakulavich/oura-cli
  • 17 Oura V2 collections
  • 1 SQLite file
  • 0 Native deps
  • 100% Your data, local

Capabilities

Your ring, in a single CLI.

Seventeen collections, one SQLite cache. Read the digests, or drop down to the raw Oura V2 records with fetch and db rows.

Offline-first SQLite cache

One oura-cli sync lands everything in ~/.oura-cli/oura.db. Every report, trend and lookup after that reads local disk: no API call, no mobile app, no internet required. Later syncs resume each collection from its last stored day.

  • bun:sqlite
  • ~/.oura-cli/oura.db
  • incremental sync
  • 17 cached tables

Real terminal reports

oura-cli report prints a weekly or monthly digest: daily scores, averages, deltas vs the previous window, sleep detail, and a short recommendation block. "You slept poorly Tuesday" callouts included.

  • --period week
  • --period month
  • trend deltas
  • recommendations

Pipe-friendly by default

Output auto-switches to stable JSON the moment stdout isn't a terminal. Pipe to jq, plot with gnuplot, or feed it straight into your own scripts and dashboards.

Trends & personal bests

oura-cli db trends 30 walks score history across the last N days. oura-cli db stats surfaces row counts, the full date range, and your personal bests.

  • db trends
  • db stats

Seventeen collections, two ways

fetch <collection> reads the API, db rows <collection> reads the cache, with the same range flags. Sleep, readiness, activity, heart rate, SpO₂, stress, workouts, sleep periods, cardiovascular age, resilience, VO₂ max, bedtime guidance, sessions, rest mode, tags, ring, battery.

Stable JSON envelope

Errors emit a deterministic JSON shape on stderr: code, message, hint. Documented exit codes for success, user errors, auth, API, and storage. Predictable for CI and agents.

A doctor that names the fix

oura-cli doctor checks the token, the database, its integrity (SQLite quick_check) and how fresh the data is, then prints one Next: line. Online it asks Oura whether it holds newer days, so it can tell "run sync" apart from "the ring has not uploaded".

No telemetry, ever

The only outbound network calls are your authenticated requests to Oura Cloud. No analytics. No phone-home. Token files written with 0600 on POSIX so only you can read them.

Bun + TypeScript, no build step

Built on Bun with bun:sqlite, citty for CLI parsing and chalk for output. Runs straight from TypeScript source. Zero native deps.

OpenClaw-ready

oura-cli manifest returns the OpenClaw tool-registry shape. Drop your ring data straight into an LLM agent.

Quick start

Four commands to your first weekly digest.

Requires Bun ≥ 1.0 and an Oura Personal Access Token. Works on macOS, Linux, and Windows (WSL).

01

Install Bun & oura-cli

Skip the first line if you already have Bun.

$ curl -fsSL https://bun.sh/install | bash
$ bun add -g @drakulavich/oura-cli
02

Paste your token, check the setup

One time. The token lands at ~/.oura-token with 0600.

$ oura-cli login
# paste PAT from cloud.ouraring.com
$ oura-cli doctor
03

Sync & report

The first sync fetches 30 days, then print your week.

$ oura-cli sync
$ oura-cli report
# Today, from the local cache
$ oura-cli db today
# A specific day; an empty day says which sync would fill it
$ oura-cli db date 2026-05-10
# Last week at a glance, no API hit
$ oura-cli db week
# Pull the latest days into the cache
$ oura-cli sync
# Token, database, integrity, freshness
$ oura-cli doctor
# Weekly digest (default)
$ oura-cli report
# 30-day window with weekly buckets
$ oura-cli report --period month
# Score trends across the last 30 days
$ oura-cli db trends 30
# Row counts, date range, personal bests
$ oura-cli db stats
# fetch reads the API; db rows reads the cache, same flags
$ oura-cli fetch sleep                    # today
$ oura-cli fetch hr --days 7
$ oura-cli fetch workout --from 2026-05-01 --to 2026-05-31
$ oura-cli db rows tags --days 30
$ oura-cli db rows battery --day 2026-09-01
$ oura-cli db rows hr --day 2026-09-01 --limit 20
# sleep readiness activity hr spo2 stress workout sleep-periods cv-age
# resilience vo2max sleep-time session rest-mode tags ring battery
# Output switches to JSON the moment you pipe
$ oura-cli fetch sleep --days 7 | jq '.[] | {day, score, deep: .contributors.deep_sleep}'
# Dump 90 days of trends to a file
$ oura-cli db trends 90 > trends.json
# Pipe a digest into your favorite LLM
$ oura-cli report | llm "Summarize my week and call out anything concerning"

What's inside

Seventeen collections, one SQLite file.

Every collection lands in its own cached table. fetch <collection> reads the API, db rows <collection> reads the cache. Inspect the file with the sqlite3 CLI if you want: it's your data.

Endpoint Source Cached table Collection
Sleep Oura V2 daily_sleep daily_sleep sleep
Readiness Oura V2 daily_readiness daily_readiness readiness
Activity Oura V2 daily_activity daily_activity activity
Heart rate Oura V2 heartrate heartrate hr
SpO₂ Oura V2 daily_spo2 daily_spo2 spo2
Stress Oura V2 daily_stress daily_stress stress
Workouts Oura V2 workout workouts workout
Sleep periods Oura V2 sleep sleep_model sleep-periods
Cardiovascular age Oura V2 cardiovascular_age cardiovascular_age cv-age
Resilience Oura V2 daily_resilience daily_resilience resilience
VO₂ max Oura V2 vO2_max vo2max vo2max
Bedtime guidance Oura V2 sleep_time sleep_time sleep-time
Sessions Oura V2 session sessions session
Rest mode Oura V2 rest_mode_period rest_mode_periods rest-mode
Tags Oura V2 enhanced_tag enhanced_tags tags
Ring Oura V2 ring_configuration ring_configuration ring
Battery Oura V2 ring_battery_level ring_battery_level battery

Storage: bun:sqlite. CLI parsing: citty. Output: chalk. No build step: the CLI runs from src/ on Bun.

Configuration

Sensible defaults. Override anything.

Setting Flag Env var Default
Token --token OURA_TOKEN file
Token file path --path (login) OURA_TOKEN_PATH ~/.oura-token
Database path --db OURA_DB_PATH ~/.oura-cli/oura.db
Timezone --tz OURA_TZ system, else UTC
Output format --format   auto (TTY → table)
Colour --no-color NO_COLOR on for a terminal, off when piped

Automation

Built for agents as well as humans.

If you're driving the CLI from a script or LLM harness, the introspection surface is first-class, and the output contracts follow semver.

oura-cli describe

A JSON manifest of every command, argument and output schema. Agents discover capabilities without scraping --help.

oura-cli healthcheck

Returns {ok, version, latencyMs} for liveness probes: the database opens and answers a query. Gate on .ok, not the exit code; doctor does the deeper checks.

Agent skill in the box

skills/oura-cli/SKILL.md ships in the package, in the Agent Skills format OpenClaw, Hermes and ~/.agents/skills read: which command answers which question, the JSON shapes, exit codes, and the rules. A test resolves every command in it against describe, so it cannot drift.

Read the skill →

Stable JSON schemas

Schemas under docs/schemas/ cover fetch <collection>, doctor and the describe manifest. Exit codes are documented: 0 ok, 1 user, 2 auth, 3 API, 4 storage.

Browse schemas →

Security

Your health data.
Handled carefully.

This tool reads personal health data. The CLI is designed to keep your token off disk in obvious places, out of shell history, and out of error logs.

  • 0600 Token file perms on POSIX
  • 200 Char cap on API error bodies
  • 0 Telemetry calls, ever
Read the full security notes
  • file ~/.oura-token written with 0600 by oura-cli login. Only your user can read it.
  • env OURA_TOKEN works for CI & scripts, but shows up in ps auxe and core dumps. Prefer the file.
  • avoid --token <pat> lands in shell history. Throw-away scripts only.
  • redact API errors truncate response bodies to 200 chars and redact Bearer tokens and "token":"…" patterns.
  • revoke Lost device? Revoke at cloud.ouraring.com, not through this CLI.

Ready to own your data?

Open source, MIT licensed, no telemetry. Star the repo, install the package, paste your token: your weekly digest is one command away.

Copied