API access to Intervals.icu

Thanks David. Issue was with the permission.

Yes I was trying to post planned workouts. Its working now.

1 Like

Hi,

Is there some kind full documentation of all available REST API endpoints?

Unfortunately not yet. However many mirror the private API endpoints used by the Intervals.icu web app. So you can use the inspector to see what to send and what comes back. Replace /api/ with /api/v1/ in the path.

I need to get API docs done. Keen to have lots of stuff integrating with Intervals.icu.

2 Likes

Hi David, loving using Intervals.icu. I am co-founder of Her Spirit which is a running, swimming, cycling and triathlon community for women. We are encouraging some of our members to sign up and use it. They are really loving the experience. Is it possible with the api to create an authenticated link between our app and Intervals to automatically log members in or create a new account for a new sign up? Many thanks Holly

Thanks! Yes what you are asking for exists. I will be in touch shortly.

Hi @david
As per the post it supports these 3.

Issue is that they don’t support HR and Pace based workouts.

Can we also use the ICU JSON Workout format to post workouts?

Edit
Tried myself and got the answer. Even though its not documented posting with JSON works. My guess is thats the best way to post HR and Pace based workouts

Cool. Posting Intervals.icu text is the best way if you can because all features are supported.

@Rohan_EnduroCo can you share a sample of JSON wor HR and Pace workouts?

@snoopy easiest way is go to intervals and download any workout. One example below is for power. But you can easily change that.

{
  "athlete_id": null,
  "id": null,
  "icu_training_load": null,
  "name": "CSP15 Cycling Speed Play 79m [Cap] #enduroco.in",
  "description": null,
  "type": "Ride",
  "indoor": null,
  "color": null,
  "moving_time": null,
  "updated": null,
  "joules": null,
  "joules_above_ftp": null,
  "workout_doc": {
    "duration": 4740,
    "steps": [
      { "duration": 600, "power": { "value": 43, "units": "%ftp" } },
      { "duration": 900, "power": { "value": 65, "units": "%ftp" } },
      {
        "reps": 6,
        "text": "6x",
        "steps": [
          { "duration": 120, "power": { "value": 98, "units": "%ftp" } },
          { "duration": 120, "power": { "value": 43, "units": "%ftp" } }
        ],
        "duration": 1440
      },
      { "duration": 900, "power": { "value": 65, "units": "%ftp" } },
      { "duration": 900, "power": { "value": 43, "units": "%ftp" } }
    ],
    "zoneTimes": [
      {
        "id": "Z1",
        "zone": 1,
        "name": "Z1",
        "color": "#009e80",
        "max": 55,
        "minWatts": 1,
        "maxWatts": 129,
        "percentRange": "0% - 55%",
        "secs": 2220
      },
      {
        "id": "Z2",
        "zone": 2,
        "name": "Z2",
        "color": "#009e00",
        "max": 75,
        "minWatts": 130,
        "maxWatts": 176,
        "percentRange": "56% - 75%",
        "secs": 1800
      },
      {
        "id": "Z3",
        "zone": 3,
        "name": "Z3",
        "color": "#ffcb0e",
        "max": 90,
        "minWatts": 177,
        "maxWatts": 211,
        "percentRange": "76% - 90%",
        "secs": 0
      },
      {
        "id": "Z4",
        "zone": 4,
        "name": "Z4",
        "color": "#ff7f0e",
        "max": 105,
        "minWatts": 212,
        "maxWatts": 246,
        "percentRange": "91% - 105%",
        "secs": 720
      },
      {
        "id": "Z5",
        "zone": 5,
        "name": "Z5",
        "color": "#dd0447",
        "max": 120,
        "minWatts": 247,
        "maxWatts": 282,
        "percentRange": "106% - 120%",
        "secs": 0
      },
      {
        "id": "Z6",
        "zone": 6,
        "name": "Z6",
        "color": "#6633cc",
        "max": 150,
        "minWatts": 283,
        "maxWatts": 352,
        "percentRange": "121% - 150%",
        "secs": 0
      },
      {
        "id": "Z7",
        "zone": 7,
        "name": "Z7",
        "color": "#504861",
        "max": 999,
        "minWatts": 353,
        "maxWatts": 9999,
        "percentRange": "151% +",
        "secs": 0
      },
      {
        "id": "SS",
        "zone": 8,
        "name": "Sweet Spot",
        "color": "#ffa00e",
        "gap": true,
        "min": 84,
        "minWatts": 197,
        "max": 97,
        "maxWatts": 227,
        "percentRange": "84% - 97%",
        "secs": 0
      }
    ],
    "ftp": 235
  },
  "folder_id": null,
  "day": null,
  "days": null,
  "plan_applied": null,
  "hide_from_athlete": null,
  "target": null,
  "icu_intensity": null
}

workout 1

HI There, is it possible to get the detailed metrics of an activitie trough the api? i think of detecting anomalies in the power or heart rate data… thanks

Yes you can:

GET /api/v1/activity/{id}/streams?types=raw_watts,watts

You can find out the types by looking at the calls made by the web app in the inspector. The watts and heartrate streams include info on anomalies detected and fixed by Intervals.icu.

You can also tack on .csv to get CSV data instead of JSON.

2 Likes

Trying to use the endpoints from an SPA (which does have a back-end I could use, but prefer not to since using Firebase heavily, including caching stuff like this) and running into CORS issues. Is there any specific reason for this restriction?

CORS isn’t supported because I didn’t need it for the Intervals.icu front-end and it is safer to keep things turned off. I could turn it on but need to be careful about security. Maybe only for /api/v1/ endpoints with Access-Control-Allow-Headers: Authorization? Would that work for your SPA?

2 Likes

@david This post will consist of 2 items:

  1. Possible Bug
    Within Intervals.icu, one can have 2 different FTP settings (outdoor and indoors).
    Hence for workout creation there is an option to mark it as “indoors”. What I found out was that this setting does not trigger the FTP to be updated in the ERG and JSON file downloads. (ERG & JSON file download will still be using Outdoors FTP with FTP = 200 on the ERG download)

  2. Additional Data Request for API
    Recently saw that you enabled JSON based workout export/downloads (download.json) and I’m playing around with this as it may afford some better flexibility vs MRC/ERG files. For Power Based structured workouts downloaded as JSON, there is the inclusion of the user’s FTP to be able to change the workout structure from %FTP and Absolute Watt if necessary.

For HR Based workouts (I didn’t test pace), the user’s MaxHR or LTHR is not included into the workout JSON. Would it be possible to also include either of these depending on the user’s pref into the JSON same as FTP pref? (indoor/outdoor - but just named as FTP)? Right now, HR based workouts editing can only support %HR (and not absolute HR) so Its gonna take another API call to get the MaxHR/LTHR data and even then I do not know if there’s a flag to indicate the user is creating the workout using MaxHR or LTHR.

TQ for your consideration.

I did some testing and this was broken from the Intervals.icu web app (the indoor flag not passed through). I have fixed that. So if you call /api/v1/athlete/{id}/download-workout{ext} you need to include "indoor" true in the payload.

This endpoint works: /api/v1/athlete/{id}/events/{eventId}/download{ext}.

I have added lthr, max_hr, threshold_pace and pace_units. Note that threshold_pace is always in mps and the pace_units field is just so it can be correctly displayed to the user:

SECS_100M(false),
SECS_100Y( true),
MINS_KM(false),
MINS_MILE(true),
SECS_500M(false);
2 Likes

TQ So Much…

intervals.icu web-app front end downloads the ERG w/ the correct FTP (based on the flag)

I’m using this end point “https://intervals.icu/api/v1/athlete/(athleteId)/events/(workoutId)/download.json” and I can also see the maxHR / LTHR and all the ones you added (for pace - i think this would come in handy later)

TQ So much for this…

1 Like

@david yes please!

I just deployed CORS support for /api/v1/ endpoints. Supposedly you should be able to supply an Authorization header. I was a bit lazy and haven’t actually tested it but was simple cut and paste from another project so it should work.

Thanks a lot! All smooth now.
Tho I was banging my head not getting it working, because I was going off info at top of thread to use my strava athlete ID. Might want to put a note about the change in early posts/examples?

Edit: aaaand this is where I realize with no access control to enforce read-only I guess this will leave me open to having everything nuked by anyone haha. Guess I’ll move it to back-end after all… well, eventually. #live4danger

1 Like

would there be a possibility to have the gear data in the api results? Thanks