Workouts

List normalized activity sessions and retrieve detailed workout metrics and heart-rate data.

6 min read Updated Sep 16, 2026

List Workouts

GET /v1/users/{id}/workouts

Returns workout sessions newest first with an opaque cursor.

Query parameterDefaultRules
from_date29 days before to_dateInclusive YYYY-MM-DD
to_datecurrent date in the user’s timezoneInclusive YYYY-MM-DD
limit50Integer from 1 to 100
cursorOpaque next_cursor from the prior page

The maximum inclusive range is 366 days.

The date range and every start_time or heart-rate sample timestamp use the timezone saved on the user’s profile. Clock values have no offset suffix; do not interpret them as UTC. If the profile has no timezone, Sonar uses UTC.

json
{
  "from_date": "2026-09-01",
  "to_date": "2026-09-03",
  "data": [
    {
      "id": "b203a7ff-9ace-5825-9414-f5c81dcc1a93",
      "type": "running",
      "start_time": "2026-09-03T07:35:00",
      "duration_minutes": 52,
      "calories": 612,
      "distance_km": 10.2,
      "source": "garmin",
      "avg_heart_rate": 151,
      "max_heart_rate": 174,
      "strain": 63
    }
  ],
  "next_cursor": null
}

Copy a non-null next_cursor unchanged into the next request’s cursor. Keep from_date, to_date, and limit unchanged between pages. Measurements not supplied by the source are null.

Retrieve a Workout

GET /v1/users/{id}/workouts/{workout_id}

Returns the session summary plus detail metrics and heart-rate data.

json
{
  "id": "b203a7ff-9ace-5825-9414-f5c81dcc1a93",
  "type": "running",
  "start_time": "2026-09-03T07:35:00",
  "duration_minutes": 52,
  "calories": 612,
  "distance_km": 10.2,
  "source": "garmin",
  "avg_heart_rate": 151,
  "max_heart_rate": 174,
  "strain": 63,
  "name": "Morning run",
  "metrics": {
    "elevation_meters": 104,
    "steps": 8921,
    "avg_speed_kmh": 11.8,
    "avg_pace_seconds_per_km": 306,
    "avg_mets": null,
    "weather_temperature_c": 18,
    "weather_humidity_pct": 71,
    "swimming_stroke_count": null,
    "swimming_lap_length_m": null
  },
  "heart_rate": {
    "avg_bpm": 151,
    "max_bpm": 174,
    "zones": [],
    "samples": [
      { "timestamp": "2026-09-03T07:35:00", "bpm": 112 },
      { "timestamp": "2026-09-03T07:35:15", "bpm": 118 }
    ]
  }
}

heart_rate.samples is the workout trace averaged into 15, 30, or 60 second buckets depending on workout length. heart_rate is null when the provider supplied no trace. A missing workout, or one belonging to another user, returns 404 workout_not_found.