API access to Intervals.icu

Thanks!

Yes filtering for the list activities endpoint is probably something I should do. A lot of the other endpoints already support filtering.

You need to use GET /api/v1/athlete/{id}/activity-pace-curves{ext} for your best efforts query.

You can seen it in action using the inspector by adding a “Best pace” custom chart:

GET /api/athlete/2060784/activity-pace-curves?oldest=2023-06-15T00:00:00&newest=2024-06-03T23:59:59&distances=5000&gap=false

Just change /api to /api/v1 to call it yourself. You can specify multiple distances with commas. That endpoint also supports filtering. Add some filters to the tab to see how to do it.

For one single activity you can fetch its pace curve with GET /api/v1/activity/{id}/pace-curve{ext}

1 Like

@david let me know if i’m doing something wrong. I’m trying to improve the workout library sync.

For workouts which I created by myself (either created by hand or dragging existing workouts to the library folder), I have no issues using the API to download the JSON it.

basically sending a GET to
let url = “https://intervals.icu/api/v1/athlete/\(athleteId)/workouts/\(workout.id)

then POST to
https://intervals.icu/api/v1/athlete/\(athleteId)/download-workoutjson

However, when i copy any workouts that is available from the public workout library into my own Library,

eg: Gerries Workout

Doing the same process, it will fail with the error:

Download Error: requestError[Error Domain=OAuthSwiftError Code=400 "Required request body is missing" UserInfo={NSLocalizedDescription=Required request body is missing, Response-Headers={
    "Access-Control-Allow-Origin" = "*";
    "Content-Length" = 57;
    "Content-Type" = "application/json;charset=UTF-8";
    Date = "Fri, 07 Jun 2024 13:03:29 GMT";
    "Strict-Transport-Security" = "max-age=15724800; includeSubDomains";
}, OAuthSwiftError.response=<NSHTTPURLResponse: 0x6000037f99c0> { URL: https://intervals.icu/api/v1/athlete/{athleteId}/download-workoutjson } { Status Code: 400, Headers {
    "Access-Control-Allow-Origin" =     (
        "*"
    );
    "Content-Length" =     (
        57
    );
    "Content-Type" =     (
        "application/json;charset=UTF-8"
    );
    Date =     (
        "Fri, 07 Jun 2024 13:03:29 GMT"
    );
    "Strict-Transport-Security" =     (
        "max-age=15724800; includeSubDomains"
    );
} }, OAuthSwiftError.response.data={length = 57, bytes = 0x7b227374 61747573 223a3430 302c2265 ... 69737369 6e67227d }, NSErrorFailingURLKey=https://intervals.icu/api/v1/athlete/{athleteId}/download-workoutjson, Response-Body={"status":400,"error":"Required request body is missing"}}]

when issuing a GET to one of my own libraries, i will get a response which can then be converted to JSON. But for the shared libraries, copied to my own library, nothing gets returned.

Hmm. I wasn’t able to reproduce this. The Code=400 "Required request body is missing" response says that you didn’t supply the workout doc etc. in the body of the post so maybe the GET failed?

Note that unfortunately library workout IDs are still “per athlete” (something I should have fixed long ago). So if you are fetching a workout from your library that has been shared by someone else you need to use their athlete id.

So I have this one in my library (my athlete ID is 2049151):

        {
            "athlete_id": "5039599",
            "id": 55,
            "icu_training_load": 28,
            "name": "Recovery Ride",

To fetch it I need to do:

GET /api/v1/athlete/5039599/workouts/55

To convert to JSON format for myself:

POST /api/v1/athlete/2049151/download-workout.json
(body returned by the GET above)

Tx for the tip.
Seems to be working now…

1 Like

Hi!,

Long time lurker and first time poster. I am trying to create a sync between a service with no integrations and intervals.icu so It can update my Coros Watch. But I think I am having troubles to get the excersise correct using the api. the workouts sync correctly to cores but The graph of the workout never shows on the calendar. This the json I send to the api

{
  "start_date_local": "2024-06-23T08:00:00",
  "name": "Day 1 - Long Run",
  "description": null,
  "icu_distance_target": 14960.999999999904,
  "icu_training_load": 73.20785881093873,
  "moving_time": 5100,
  "workout_doc": {
    "steps": [
      {
        "reps": 1,
        "text": "1X",
        "steps": [
          {
            "duration": 600,
            "power": {
              "end": 80,
              "start": 65,
              "units": "%ftp"
            }
          }
        ]
      },
      {
        "reps": 1,
        "text": "1X",
        "steps": [
          {
            "duration": 3000,
            "power": {
              "end": 84,
              "start": 77,
              "units": "%ftp"
            }
          }
        ]
      },
      {
        "reps": 6,
        "text": "6X",
        "steps": [
          {
            "duration": 60,
            "power": {
              "end": 107,
              "start": 102,
              "units": "%ftp"
            }
          },
          {
            "duration": 120,
            "power": {
              "end": 80,
              "start": 65,
              "units": "%ftp"
            }
          }
        ]
      },
      {
        "reps": 1,
        "text": "1X",
        "steps": [
          {
            "duration": 420,
            "power": {
              "end": 80,
              "start": 65,
              "units": "%ftp"
            }
          }
        ]
      }
    ],
    "duration": 5100,
    "target": "POWER",
    "zone_times": [
      {
        "secs": 1740,
        "id": "Z1"
      },
      {
        "secs": 3000,
        "id": "Z2"
      },
      {
        "secs": 0,
        "id": "Z3"
      },
      {
        "secs": 360,
        "id": "Z4"
      },
      {
        "secs": 0,
        "id": "Z5"
      }
    ]
  }
}

You need to supply the workout description in Intervals.icu format (as you would enter it in the workout builder) or a workout file (fit, zwo etc.). You can’t supply a workout_doc.

{
  "category": "WORKOUT",
  "start_date_local": "2024-03-30T00:00:00",
  "description": "- 2h Z2 Pace\n- 10m Z1 Pace"
}
{
  "category": "WORKOUT",
  "start_date_local": "2024-03-30T00:00:00",
  "filename": "Woddle_Toddle.fit",
  "file_contents_base64": "DiCNCG8AAAAuRklUKz9AAAEAAAMBAoQAAQAIDgcAAP8FSW50ZXJ2YWxzLmljdQBAAAEAGgMEAQAIDgcGAoQAAVdvZGRsZSBUb2RkbGUAAAFAAAEAGwcBAQACBIYDAQAFBIYGBIYHAQD+AoQAAAA27oABAAAA+AAAAQAEAAAe/A=="
}

Intervals.icu will calculate moving_time and other fields.

2 Likes

Hi David, thank you for the update. I based my json on the following post API access to Intervals.icu - #129 by Rohan_EnduroCo and if i follow this json file the changes are reflected also on the calendar. Is this because of the null values in the icu_ parameters? Thank you for the assist on this topic

In a clear view with a gpx file:

curl -X POST “https://intervals.icu/api/v1/athlete/i11111/activities?name=Prueba&description=Borrar” -H “accept: /” -H “authorization: Basic xxxxxxxxx” -F file=@c:\temp\pru.gpx

I´m having troubles whe loading from my app; I informe type=Ride but always create a type=Run activity.

curl -X POST “https://intervals.icu/api/v1/athlete/i111111/activities?name=Prueba&description=Borrar&type=Ride” -u “API_KEY:cucurrucucu” -H “accept: /” -H “accept: /” -F type=Ride -F file=@/temp/31yLg2vkjHKMSG.gpx

You cannot specify an activity type with that endpoint. The type is derived from the information in the uploaded file. For a gpx:

 <trk>
  <name>Morning Ride</name>
  <type>Ride</type>
1 Like

hey. i was looking for a way to upload a multiple amount of workouts in bulk using the api. i wondered, if there is a type of format we can create using the multiple ai tools that are out there, to do this kind of job. for example, google calendar can use a csv table to upload multiple events at once. is there something similar? tried to use the api but couldnt seem to do it properly. if so, please specify the best way to do so. thanks in advance.

you can upload in bulk using the API endPoint by sending it workout files in the format of ZWO/FIT/etc.

You need to use this endpoint to upload in bulk: https://intervals.icu/api-docs.html#post-/api/v1/athlete/-id-/events/bulk

POST /api/v1/athlete/{id}/events/bulk

It accepts a JSON array of event objects.

[{
  "category": "WORKOUT",
  "start_date_local": "2024-03-30T00:00:00",
  "filename": "Woddle_Toddle.fit",
  "file_contents_base64": "DiCNCG8AAAAuRklUKz9AAAEAAAMBAoQAAQAIDgcAAP8FSW50ZXJ2YWxzLmljdQBAAAEAGgMEAQAIDgcGAoQAAVdvZGRsZSBUb2RkbGUAAAFAAAEAGwcBAQACBIYDAQAFBIYGBIYHAQD+AoQAAAA27oABAAAA+AAAAQAEAAAe/A==",
  "external_id": "1234"
},
{
  "category": "WORKOUT",
  "start_date_local": "2024-04-01T00:00:00",
  "filename": "Woddle_Toddle2.fit",
  "file_contents_base64": "DiCNCG8AAAAuRklUKz9AAAEAAAMBAoQAAQAIDgcAAP8FSW50ZXJ2YWxzLmljdQBAAAEAGgMEAQAIDgcGAoQAAVdvZGRsZSBUb2RkbGUAAAFAAAEAGwcBAQACBIYDAQAFBIYGBIYHAQD+AoQAAAA27oABAAAA+AAAAQAEAAAe/A==",
  "external_id": "1235"
},
{
    "category": "NOTE",
    "start_date_local": "2024-04-01T00:00:00",
    "name": "Build",
    "description": "Start of build phase",
    "color": "green",
    "external_id": "1236"
}]

Here I have supplied fit files. But you can also use the native Intervals.icu format (same as workout builder) in the description field. Then you also need to supply “type” (Ride, Run etc.).

If you pass a query parameter upsert=true and are calling from an OAuth client (not using API key) then events with matching external_id are updated if they already exist.

how can i upload a plan from a csv file?
i have everything calculated in tss points?

thank you!

can anybody support me with the API please?

Please refer to the threads above.
But IIRC, CSV upload for planned workouts are not supported
you need JSON/FIT/MRC/ERG/ZWO and the like

i want to upload a whole seasonplan not one workout.

Should be the same process.

i want to upload a season plan for marathon running and triathlon training to the intervals.icu database - so every one can select this plans and get them displayed in his calender with his threshold levels.

I saw you asked this same question last year

so it LOOKS like you’re not really wanting to upload a bunch of workouts, but to upload a series of Planned TSS?