Skip to main content
The Analytics API provides coaches and athletes with detailed performance data. The athlete_profile endpoint is the primary data source for the athlete detail page, combining profile, metrics, goals, and active plans into a single request. Separate endpoints expose time-series fitness and running stats, and the coach_athletes_metrics endpoint powers the coach roster view with batch data for every athlete in an organization.

Get athlete profile

Returns a comprehensive snapshot of an athlete’s data: user profile, cached fitness metrics, current training goals, active plans, and AI-generated insights. Results are cached in Redis; pass refresh=true to force a fresh fetch.
GET /api/v1/analytics/athlete_profile
Authentication: Bearer token. Coaches can query any athlete in their organization; athletes can only query themselves.
user_id
string
required
The athlete’s user ID.
refresh
boolean
default:"false"
Bypass cache and fetch fresh data from all sources.
curl "https://api.astral.run/api/v1/analytics/athlete_profile?user_id=usr_athlete1" \
  -H "Authorization: Bearer <token>"
Response 200 OK
{
  "status": "ok",
  "data": {
    "user_id": "usr_athlete1",
    "display_name": "Alex",
    "profile_image_url": "https://cdn.astral.run/images/alex.jpg",
    "metrics": {
      "running_analytics": {
        "weekly_distance_km": 62.4,
        "avg_pace_per_km": "5:12"
      },
      "fitness": {
        "current": {
          "vo2_max": 56.3,
          "hrv_avg": 62,
          "recovery_score": 78
        }
      },
      "training": {
        "current_week_load": 420,
        "four_week_avg_load": 395
      }
    },
    "current_goals": {
      "races": [
        {
          "race_name": "Boston Marathon",
          "race_date": "2025-04-21",
          "goal_time": "3:05:00"
        }
      ]
    },
    "current_plans": [
      {
        "plan_instance_id": "pi_001",
        "name": "Marathon Block A",
        "status": "active",
        "current_week": 6,
        "total_weeks": 16
      }
    ]
  }
}
data.metrics.fitness.current
object
Current fitness values: vo2_max, hrv_avg, recovery_score, and 30-day history arrays.
data.metrics.training
object
Training load for the current week and a rolling 4-week average.
data.current_plans
array
Active or scheduled training plan instances with week progress.

Get fitness stats

Returns overall fitness statistics for the authenticated user over a configurable number of days.
GET /api/v1/analytics/fitness_stats
Authentication: Bearer token (self only)
days
integer
default:"30"
Number of days to analyze. Maximum is 365.
curl "https://api.astral.run/api/v1/analytics/fitness_stats?days=90" \
  -H "Authorization: Bearer <token>"
Response 200 OK
{
  "status": "ok",
  "data": {
    "total_activities": 52,
    "total_distance": 624000,
    "total_duration": 187200,
    "avg_pace": "5:01",
    "avg_heart_rate": 148
  }
}
data.total_distance
number
Total distance in meters.
data.total_duration
number
Total duration in seconds.
data.avg_pace
string
Average pace formatted as M:SS per kilometer.
data.avg_heart_rate
number
Average heart rate in bpm. Present only if HR data is available.

Get running stats

Returns time-series running metrics aggregated by day, week, month, or year. Every metric type (distance, duration, training_load) is included in the response so clients can switch views without a second request.
GET /api/v1/analytics/running_stats
Authentication: Bearer token (self only)
from_timestamp
integer
required
Unix timestamp for the start of the range.
to_timestamp
integer
required
Unix timestamp for the end of the range. Must be within 365 days of from_timestamp and no more than 7 days in the future.
range_type
string
required
Aggregation granularity. One of daily, weekly, monthly, or yearly.
  • daily → one point per day
  • weekly → one point per day over 7 days (7 data points)
  • monthly → one point per ISO week
  • yearly → one point per calendar month
metric_type
string
default:"distance"
Primary metric to sort or highlight. One of distance, duration, or training_load. All metrics are always returned.
timezone
string
IANA timezone string for day bucketing (e.g. "Asia/Tokyo").
timezone_offset_minutes
integer
Legacy UTC offset in minutes. Use timezone instead when possible.
curl "https://api.astral.run/api/v1/analytics/running_stats?from_timestamp=1743465600&to_timestamp=1746057600&range_type=weekly&metric_type=distance&timezone=America/Chicago" \
  -H "Authorization: Bearer <token>"
Response 200 OK
{
  "status": "ok",
  "data": [
    {
      "date": "2025-04-07",
      "distance": 52400,
      "duration": 15600,
      "nexrex_training_load": 380,
      "runs": 4,
      "calories": 2840,
      "pace_z1_time": 3600,
      "pace_z2_time": 5400,
      "pace_z3_time": 2160,
      "pace_z4_time": 1800,
      "pace_z5_time": 600
    }
  ]
}
data[].distance
number
Total distance in meters for the period bucket.
data[].duration
number
Total duration in seconds.
data[].nexrex_training_load
number
Astral training load score (NR ETE+) — always included regardless of metric_type.
data[].runs
integer
Number of activities in the bucket.
data[].pace_z1_time — pace_z5_time
number
Seconds spent in each pace zone (Z1 = easy through Z5 = max).

Get activity stats (v2)

PostgreSQL-backed activity aggregation. Returns distance, duration, training load, calories, and pace zones grouped by day or month in one response. Results are cached for 5 minutes.
GET /api/v1/analytics/activity_stats
Authentication: Bearer token (self only)
from
string
required
Start date in YYYY-MM-DD format.
to
string
required
End date in YYYY-MM-DD format. Maximum range is 400 days.
timezone
string
default:"UTC"
IANA timezone for date bucketing.
granularity
string
default:"day"
Bucket size: day or month.
refresh
boolean
default:"false"
Bypass Redis cache.
curl "https://api.astral.run/api/v1/analytics/activity_stats?from=2025-04-01&to=2025-04-30&granularity=day&timezone=Europe/London" \
  -H "Authorization: Bearer <token>"

Get health metrics (v2)

Returns resting heart rate, HRV, and VO2 Max time series from PostgreSQL. Results are cached for 5 minutes.
GET /api/v1/analytics/health_metrics
Authentication: Bearer token (self only)
from
string
required
Start date in YYYY-MM-DD format.
to
string
required
End date in YYYY-MM-DD format. Maximum range is 400 days.
granularity
string
default:"day"
Bucket size: day, week, or month.
refresh
boolean
default:"false"
Bypass Redis cache.
curl "https://api.astral.run/api/v1/analytics/health_metrics?from=2025-01-01&to=2025-04-30&granularity=week" \
  -H "Authorization: Bearer <token>"

Get coach athletes metrics

Returns batch metrics for every active athlete in a coach’s organization — profile, cached fitness, training load, current plans, and attention signals — in a single API call. Results are cached in Redis for 10 minutes per coach/org combination.
GET /api/v1/analytics/coach_athletes_metrics
Authentication: Bearer token (coach role required)
org_id
string
Organization ID. Falls back to the authenticated coach’s own org_id when omitted.
filter_by_coach
string
Pass "1" to return only athletes assigned to the current coach.
group_id
string
Return athletes from a specific group. Cannot be combined with unassigned_only.
unassigned_only
string
Pass "1" to return athletes not assigned to any group. Cannot be combined with group_id.
Search by athlete username, email, or display name.
page
integer
default:"1"
Page number for server-side pagination.
per_page
integer
default:"200"
Results per page. Maximum 200.
no_cache
string
Pass "1" to bypass Redis cache.
curl "https://api.astral.run/api/v1/analytics/coach_athletes_metrics?org_id=org_xyz789&filter_by_coach=1&page=1&per_page=50" \
  -H "Authorization: Bearer <token>"
Response 200 OK
{
  "status": "ok",
  "data": {
    "athletes": [
      {
        "user_id": "usr_athlete1",
        "username": "alex.runner",
        "display_name": "Alex",
        "profile_image": "https://cdn.astral.run/images/alex.jpg",
        "email": "alex@example.com",
        "assigned_coach_id": "usr_coach1",
        "groups": ["Marathon Block A"],
        "labels": ["5k-focus"],
        "current_plans": [
          {
            "plan_instance_id": "pi_001",
            "name": "Marathon Block A",
            "status": "active",
            "current_week": 6,
            "total_weeks": 16
          }
        ],
        "current_goals": {
          "races": [{ "race_name": "Boston Marathon", "race_date": "2025-04-21" }]
        },
        "has_push_token": true,
        "metrics": {
          "running_analytics": { "weekly_distance_km": 62.4 },
          "fitness": {
            "current": {
              "vo2_max": 56.3,
              "hrv_avg": 62,
              "recovery_score": 78,
              "hrv_avg_prev": 58,
              "recovery_score_prev": 72,
              "vo2_max_prev": 55.1
            }
          },
          "training": { "current_week_load": 420 },
          "attention_signals": { "flags": [] },
          "last_updated": "2025-04-22T23:00:00Z"
        }
      }
    ],
    "total_count": 1,
    "metadata": {
      "total": 1,
      "page": 1,
      "per_page": 50,
      "total_pages": 1,
      "has_next": false,
      "has_prev": false
    },
    "pagination": {
      "total": 1,
      "page": 1,
      "per_page": 50,
      "total_pages": 1,
      "has_next": false,
      "has_prev": false
    }
  }
}
data.athletes
array
Array of athlete objects. Each includes full profile, organization membership, and nested metrics.
data.athletes[].metrics.fitness.current.hrv_avg_prev
number
HRV average from 7 days ago for trend comparison in the attention queue.
data.athletes[].metrics.attention_signals
object
AI-computed attention flags that surface athletes who may need coaching intervention.

Error codes

StatusMeaning
400Validation error — missing or invalid parameters
401Missing or invalid authentication
403Insufficient permissions (e.g. athlete accessing another user’s data)
404No data found for the given user/range
500Internal server error