Intervals.icu API
Authentication
Basic auth with username API_KEY and password = your API key.
Use athlete id 0 to auto-resolve from the API key.
# Test authentication
curl -s -u "API_KEY:$ICU_API_KEY" "https://intervals.icu/api/v1/athlete/0/profile" | jq '.name, .id'
Environment variable: ICU_API_KEY (set in shell environment). Never put the key in a skill file, shell history, prompt, or output.
ICU="https://intervals.icu/api/v1"
AUTH="API_KEY:$ICU_API_KEY"
Reusable query script
Use scripts/intervals_data.py for routine training-data collection. It reads ICU_API_KEY, validates API responses, and emits structured JSON for activities, Wellness, and CTL/ATL/TSB snapshots. Do not write temporary curl + heredoc/inline-Python collectors during a query.
# Today in Asia/Shanghai
ICU_API_KEY=*** python3 scripts/intervals_data.py
# Explicit range with Wellness history for trend context
ICU_API_KEY=*** python3 scripts/intervals_data.py \
--start 2026-08-01 --end 2026-08-12 --wellness-lookback-days 14
# Check a newly uploaded activity
ICU_API_KEY=*** python3 scripts/intervals_data.py \
--start 2026-08-12 --end 2026-08-12 --wellness-lookback-days 7
The output includes selected per-activity fields (duration, distance, average/max HR,
Intervals load, HR/pace load, intensity, RPE, HR-zone times, and swim pool metadata),
daily Wellness fields, computed tsb = ctl - atl, and start/end/post-end fitness
snapshots. Extend this script when a reusable field is missing; do not move parsing back
into an ad-hoc prompt-time shell snippet.
OpenAPI Spec
Full spec at: https://intervals.icu/api/v1/docs/ (RapiDoc UI) or https://intervals.icu/api/v1/docs/swagger-ui/index.html (Swagger).
Core Endpoints
Activities
# List activities (date range, desc order)
curl -s -u "$AUTH" "$ICU/athlete/0/activities?oldest=2026-01-01&newest=2026-01-31" | jq '.[].name'
# Get single activity (with intervals)
curl -s -u "$AUTH" "$ICU/activity/{id}?intervals=true"
# Get activity streams (power, HR, cadence, GPS, etc.)
curl -s -u "$AUTH" "$ICU/activity/{id}/streams.json?types=watts,heartrate,cadence,altitude"
# Search activities by name or tag
curl -s -u "$AUTH" "$ICU/athlete/0/activities/search?q=tempo&limit=10"
# Download as CSV (all activities)
curl -s -u "$AUTH" "$ICU/athlete/0/activities.csv" -o activities.csv
# Upload activity file (fit/tcx/gpx)
curl -s -u "$AUTH" -F "file=@activity.fit" "$ICU/athlete/0/activities"
Wellness (Daily Metrics)
Fields: weight, restingHR, hrv, hrvSDNN, sleepSecs, sleepScore, sleepQuality, avgSleepingHR, soreness, fatigue, stress, mood, motivation, injury, spO2, vo2max, steps, kcalConsumed, comments, and more.
The wellness endpoint also returns computed fitness fields: ctl (fitness), atl (fatigue), rampRate, ctlLoad, atlLoad, per-sport sportInfo (eftp, wPrime, pMax).
# Get wellness for date range
curl -s -u "$AUTH" "$ICU/athlete/0/wellness.json?oldest=2026-01-01&newest=2026-01-31"
# Get specific fields only
curl -s -u "$AUTH" "$ICU/athlete/0/wellness.json?oldest=2026-03-01&newest=2026-03-22&fields=id,ctl,atl,rampRate,weight,restingHR,hrv,sleepSecs"
# Update wellness (PUT to date id)
curl -s -u "$AUTH" -X PUT "$ICU/athlete/0/wellness/2026-03-22" \
-H "Content-Type: application/json" \
-d '{"weight": 84.0, "restingHR": 55, "sleepSecs": 28800, "mood": 4, "comments": "Felt good"}'
# CSV format
curl -s -u "$AUTH" "$ICU/athlete/0/wellness.csv?oldest=2026-01-01&newest=2026-03-22&cols=id,ctl,atl,weight"
Power / Pace / HR Curves
# Power curves: last year, 42 days, all time
curl -s -u "$AUTH" "$ICU/athlete/0/power-curves.json?type=Run&curves=1y,42d,all"
# Pace curves (running)
curl -s -u "$AUTH" "$ICU/athlete/0/pace-curves.json?type=Run&curves=42d,1y"
# HR curves
curl -s -u "$AUTH" "$ICU/athlete/0/hr-curves.json?type=Run&curves=42d"
# Single activity power curve
curl -s -u "$AUTH" "$ICU/activity/{id}/power-curve.json"
Curve specifiers: 1y (past year), 2y, 42d (past 42 days), s0 (current season), s1 (previous), all, r.2026-01-01.2026-03-01 (custom range).
Calendar Events (Planned Workouts)
# List upcoming workouts
curl -s -u "$AUTH" "$ICU/athlete/0/events.json?oldest=2026-03-22&newest=2026-03-29"
# Create a planned workout
curl -s -u "$AUTH" -X POST "$ICU/athlete/0/events" \
-H "Content-Type: application/json" \
-d '{
"category": "WORKOUT",
"start_date_local": "2026-03-25",
"name": "Easy Run 45min",
"type": "Run",
"moving_time": 2700,
"description": "45m easy @ Z2 HR"
}'
# Event categories: WORKOUT, RACE_A, RACE_B, RACE_C, NOTE, HOLIDAY, SICK, INJURED, TARGET
Athlete Profile & Sport Settings
# Get profile (includes weight, timezone, thresholds)
curl -s -u "$AUTH" "$ICU/athlete/0/profile"
# List sport settings (zones, FTP, LTHR, etc.)
curl -s -u "$AUTH" "$ICU/athlete/0/sport-settings"
Gear
# List gear
curl -s -u "$AUTH" "$ICU/athlete/0/gear.json"
Training Load Interpretation
Intervals.icu uses the Banister impulse-response model (PMC — Performance Management Chart):
| Metric | Field | What it is | Typical range |
|---|---|---|---|
| Fitness (CTL) | ctl |
42-day EWMA of daily training load | Higher = more fit (sport-specific) |
| Fatigue (ATL) | atl |
7-day EWMA of daily training load | Higher = more tired |
| Form (TSB) | ctl - atl |
Freshness indicator | See zones below |
| Ramp Rate | rampRate |
Weekly CTL change rate | Safe: 3-7 CTL/week |
Form (TSB) Zones
| TSB Range | State | Guidance |
|---|---|---|
| > +25 | Transition | Detraining risk if sustained; fine for planned recovery blocks |
| +15 to +25 | Fresh | Race-ready, taper zone |
| +5 to +15 | Grey zone | Moderate freshness, good for quality sessions |
| -10 to +5 | Optimal training | Productive stress with manageable fatigue |
| -30 to -10 | Overreaching | Functional if planned (≤2 weeks); watch recovery markers |
| < -30 | High risk | Injury/illness risk; reduce load immediately |
Ramp Rate Guidelines
- <3 CTL/week: Maintenance or slight detraining
- 3-5 CTL/week: Conservative build (recommended for most)
- 5-7 CTL/week: Aggressive build (experienced athletes, monitor closely)
- >7 CTL/week: High injury risk; typically only in training camps
Key Nuances (Expert Knowledge)
- CTL is not fitness — it's a proxy for training stress absorbed. Two athletes with CTL=80 may have very different actual fitness.
- Training load source matters — power-based TSS (cycling) is more reliable than HR-based TRIMP (running/hiking). Intervals.icu estimates load from HR when no power meter is available; these estimates vary in accuracy.
- EWMA vs rolling averages — Intervals.icu uses EWMA (exponentially weighted moving average), which weights recent sessions more heavily. This is more physiologically realistic than simple rolling averages.
- Sport-specific loads — The wellness
sportInfoarray breaks down eFTP, W', and Pmax per sport. Cross-training loads are combined for overall CTL but interpreted separately. - Configurable time constants — Default is 42d (CTL) / 7d (ATL), adjustable per sport via
icu_type_settings[].ctlFactorandatlFactor. Non-default constants make cross-athlete comparisons meaningless. - The ACWR debate — Acute:Chronic Workload Ratio (ATL/CTL ≈ ACWR) was popularized as injury predictor ("sweet spot" 0.8-1.3). Recent meta-analyses show weak predictive validity for individuals. Use as a rough guide, not a hard rule. EWMA-based ACWR is more sensitive than rolling-average ACWR.
- Recovery indicators beyond TSB — Combine TSB with wellness data: HRV trend (declining = poor recovery), resting HR (elevated = stress), sleep quality, and subjective scores (mood, soreness, fatigue). A positive TSB with declining HRV still warrants caution.
Practical Decision Framework
When advising on training load:
- Pull current data: wellness (last 7-14 days), recent activities
- Check trends, not snapshots: Is CTL rising/stable/falling? Is ramp rate sustainable?
- Cross-reference wellness: HRV trend, sleep, subjective scores
- Consider context: Training phase (base/build/peak/recovery), upcoming events, life stress
- Give specific recommendations: Not just "you're overreaching" but "reduce volume by ~20% for 3-5 days, prioritize sleep, recheck HRV"
Rate Limits
No published rate limits, but be respectful. Batch date-range queries instead of per-day requests.