Developer API documentation

Contents


Preliminary remarks

URLs

All API endpoints are relative to https://www.cyclinganalytics.com/api. Therefore, the URL to use for /me/rides is https://www.cyclinganalytics.com/api/me/rides.

Development status

There are not yet API endpoints for everything. Please get in contact if you want something that doesn’t exist yet.

Authentication

Authentication for this API is based on the OAuth 2.0 framework. Once an authentication token has been acquired, request must include the token in the Authorization HTTP header:

Authorization: Bearer qr4hI35VeCmOfPAQhXSHGNX5JZcsJyIP

More information about authentication can be found in the authentication documentation.

HTTP methods

The HTTP methods POST, GET, PUT and DELETE are used to indicate the type of action to perform for the call to any API endpoint, with the following meanings:

  • POST — Create.
  • GET — Read.
  • PUT — Create/update.
  • DELETE — Delete.

The difference between POST and PUT is that doing the same PUT twice in a row will be exactly the same as doing it once, whereas the same POST twice in a row will result in two objects being created (or an error on the second POST if the request contains something that prevents it from being added twice).

Request parameters

For GET requests, any parameters must be encoded in the URL:

/me?power=true&heartrate=true

For POST and PUT requests, unless otherwise specified, parameters must JSON encoded as the body of the request. A Content-Type: application/json header must also be included.

Response

When the response isn’t very short, it is compressed with gzip. There is a Content-Encoding: gzip header when this is the case. Most tools handle this transparently, so this can generally be ignored.

The easiest way to see exactly what is being sent and returned is to use the API console and use the Chrome developer tools (network tab) or the Firefox developer tools (web console) to inspect the requests and responses.

Data format

Values are returned in the following format:

  • All dates are of the format YYYY-MM-DD.
  • All datetimes are of the format YYYY-MM-DDThh:mm:ss.
  • All units are metric where applicable.
  • Power curves are arrays where, to save space, the time gap between elements increases:
    • The first 300 elements (5 minutes) are at a one second interval.
    • The next 300 elements (25 minutes) are at five second intervals.
    • The next 300 elements (150 minutes) are at thirty second intervals.
    • Subsequent elements (after the three hour mark) are at five minute intervals.

This JavaScript function turns an array index into a time:

function indexToTime(i) {
    return i < 300 ? i + 1 : (i < 600 ? 300 + (i - 299) * 5 : (i < 900 ? 1800 + (i - 599) * 30 : 10800 + (i - 899) * 300));
}

This JavaScript function turns a time into an array index:

function timeToIndex(t) {
    return t <= 300 ? t - 1 : (t <= 1800 ? (t - 300) / 5 + 299 : (t <= 10800 ? (t - 1800) / 30 + 599 : (t - 10800) / 300 + 899));
}

Errors

If an error occurs, the returned JSON will include an error and the HTTP status code will be a 4xx or 5xx code. It is currently possible that no JSON will be returned in some cases, so the status code should be checked first.

/me

/me is an alias for /user/<id> for the authenticated user. /me is not available when using the API with a team authorisation token.

API endpoints

/user/<id>
/me

GET

Gets user information.

Request

All are optional.

  • weight — If true, also returns the user’s weight history.
  • power — If true, also returns the user’s FTP history and power zones.
  • heartrate — If true, also returns the user’s resting and maximum heart rate history, and heart rate zones.

Response

  • id
  • name
  • email
  • timezone — A timezone name in the pytz common_timezones_set.
  • units — Currently either metric or us.
  • sex — Either male or female.
  • weight — An array of {date: <date>, value: <weight>} objects.
  • power — An object containing:
    • ftp — An array of {date: <date>, value: <ftp>} objects.
    • zones — An array of {value: <minimum power FTP%>, name: <name>} objects. The maximum power for a zone is based on the minimum power for the next zone. All values are specified as a percentage of FTP.
  • heartrate — An object containing:
    • resting — An array of {date: <date>, value: <resting heart rate>} objects.
    • maximum — An array of {date: <date>, value: <maximum heart rate>} objects.
    • zones — An array of {value: <minimum heart rate>, name: <name>} objects. The maximum heart rate for a zone is the minimum heart rate for the next zone minus one.

/user/<id>/heartrate/resting/<date>
/user/<id>/heartrate/maximum/<date>
/user/<id>/power/ftp/<date>
/user/<id>/weight/<date>
/me/heartrate/maximum/<date>
/me/heartrate/resting/<date>
/me/power/ftp/<date>
/me/weight/<date>

The endpoints for resting heart rate, maximum heart rate, FTP and weight behave in the same way. For each there is an array of {value: <value>, date: <date>} objects (which is what is returned by /me), and these endpoints provide easy ways to view and modify these arrays.

GET

Get the resting heart rate, maximum heart rate, FTP or weight for a day.

The value returned is normally the most recent value on or before the requested date. If the date is before all entries, the value of the earliest entry is returned. If there are no entries, null is returned.

Response

  • value — a number or null.

PUT

Adds an entry on the given day (or changes the entry if it already exists).

Request

  • value

Response

  • success

DELETE

Deletes the entry for the given day. This has no effect if there is no entry on the given day, but it will return the same success message.

Response

  • success

/user/<id>/rides
/me/rides

GET

Get the list of rides of a user.

Request

  • minimal — If true, only include id and local_datetime for each ride in the response.
  • curves — If true, then power_curve (highest average power curve) and epower_curve (highest effective power curve) for each ride will be included in the response. Alternatively, use power_curve=true or epower_curve=true to get only one of them. This significantly increases the size of the reponse.

Response

  • rides — An array of rides in the format that /ride/<ride_id> returns, but not including streams or user_id. If minimal=true was included in the request, the elements in rides only contain id and local_datetime.

/user/<id>/upload
/me/upload

POST

Uploads a new ride.

This method returns immediately and the ride file is then processed asynchronously. This returns an upload_id (amongst other things), which can then be used to query the upload status.

A common workflow would be to upload rides with this method, then check /me/upload/<upload_id> every few seconds until processing has finished, and then get the ride summary data from /ride/<ride_id>.

This method supports directly uploading the ride data, or being given a URL for the ride data file that is then downloaded from the server.

Request

data and either filename or format are required when uploading a file. When url is provided, the filename will be read if available, or else filename or format is required.

It is preferable to send these requests with data as multipart/form-data rather than application/json, as the data must be Base64 encoded when sent as JSON, which results in a 33% size increase.

  • data — The raw file data.
  • url — A reachable URL for the ride data file.
  • format — The file format. One of fit, tcx, gpx, pwx, srm, bin or csv.
  • filename
  • title
  • notes
  • virtual — For rides ridden on a trainer that have artificial GPS data (e.g., Zwift rides), but isn’t necessary if the ride file itself specifies it is virtual.

Response

This returns the same as GET /me/upload/<upload_id>.

Example

>>> POST /user/3910838/upload {data: '...', filename: '2013-02-02-10-17-40.fit'}
{
    "status": "processing", 
    "ride_id": null, 
    "user_id": 1000000, 
    "format": "fit", 
    "datetime": "2013-10-07T20:11:09", 
    "upload_id": 3739085130, 
    "filename": "2013-02-02-10-17-40.fit",
    "size": 45374
  }

/user/<id>/upload/<upload_id>
/me/upload/<upload_id>

GET

Get the status of a newly uploaded ride.

status will be processing until it is finished processing, when it will be either done or error. If done, it was successful, and ride_id will contain the ID of the ride. If error, there will also be an error in the response.

This method is only available within two weeks of the ride being uploaded — the ride is still there, but its upload status cannot be queried.

Response

  • upload_id
  • user_id
  • filename
  • format
  • size — The size of the file, in bytes.
  • datetime
  • statusprocessing, done or error.
  • ride_idnull until status is done.
  • error — Only included if the value of status is error.
  • error_code — Only included if the value of status is error.

error_code will be one of:

  • expired_account — If the user’s account has expired.
  • empty_file — When the file is 0 bytes long.
  • unsupported_file — When the format, or variation of format, is unsupported.
  • invalid_file — If there is something obviously wrong with the file (bad headers often result in this).
  • duplicate_ride — When the ride already exists (based on a hash of the ride).
  • too_short — If the ride is less than five seconds long.
  • processing_error — When something else goes wrong parsing or processing the file.

/ride/<id>

GET

Gets a ride.

Request

  • streamsall, or one or more of power, speed, distance, heartrate, cadence, lrbalance, latitude, longitude, elevation, gradient, temperature, torque_effectiveness, pedal_smoothness, platform_center_offset, power_phase, power_direction, thb, smo2, respiration_rate, heart_rate_variability, gears. This causes the raw ride data to be returned. These must be joined together as a comma separated string. If this is not given, no raw data is provided (and the request is a lot faster).
  • curves — If true, then power_curve (highest average power curve) and epower_curve (highest effective power curve) will be included in the response. Alternatively, use power_curve=true or epower_curve=true to get only one of them.

Response

  • id
  • user_id
  • title
  • notes
  • local_datetime
  • utc_datetime
  • typecycling, gym, running, swimming, walking, hiking, rowing, kayaking, sup (stand-up paddleboarding), skiing, snowboarding or other
  • subtyperoad, trainer, virtual, tt, cx, track, mtb or bmx.
  • purposeride, training, race, social or commute. Presently, ride is a generic type for all activity types.
  • trainer — Whether or not the ride was ridden on a trainer.
  • format — The file format the ride was uploaded in.
  • summary
    • total_time — Time from beginning to end of ride.
    • durationtotal_time with all gaps longer than three minutes reduced to three minutes.
    • moving_time — Time when speed was above 3km/h (not 0km/h, to account for GPS errors).
    • distance
    • climbing
    • work
    • epower — Effective power.
    • intensity — Intensity, based on power.
    • variability — Variability, based on power.
    • load — Training load, based on power.
    • trimp — TRIMP, like load, but based on heart rate.
    • pwc150 — Physical Work Capacity; estimated power at 150BPM based on a statistical correlation between power output and HR.
    • pwc170 — As above, but power output at 170BPM.
    • pwc_r2 — The r2 value of the PWC values, indicating the quality of the statistical relationship between HR and power.
    • lrbalance — Left/right power balance. The percentage of power produced by the left leg, or null if the ride has no power balance data.
    • avg_cadence
    • avg_speed
    • avg_heartrate
    • avg_power
    • max_cadence
    • max_speed
    • max_heartrate
    • max_power
    • zones
      • power
      • heartrate
  • has — Whether or not the ride file contains the data stream.
    • power
    • heartrate
    • cadence
    • speed
    • lrbalance
    • gps
    • elevation
    • temperature
    • ps — Pedal smoothness. This and the following are only included when they are true.
    • te — Torque effectiveness.
    • pco — Platform center offset.
    • pp — Power phase.
    • pd — Power direction.
    • thb
    • smo2
    • rr — Respiration rate.
    • hrv — Heart rate variability.
    • gears
  • power_curve — An array with the highest average power for all time intervals. See data format for details about the structure of this array.
  • epower_curve — As above, but for effective power.
  • streams — Not included unless streams is in the request. Streams are only included when data exists for the stream.
    • power
    • speed
    • distance
    • heartrate
    • cadence
    • lrbalance
    • latitude
    • longitude
    • elevation
    • gradient
    • temperature
    • thb
    • smo2
    • respiration_rate
    • heart_rate_variability
    • gears
    • left_te, right_te — Torque effectiveness.
    • left_ps, right_ps — Pedal smoothness.
    • left_pco, right_pco — Platform center offset.
    • left_pp_s, right_pp_s — Power phase start.
    • left_pp_e, right_pp_e — Power phase end.
    • left_ppp_s, right_ppp_s — Peak power phase start.
    • left_ppp_e, right_ppp_e — Peak power phase end.
    • left_tangential, right_tangential — This and the following are power direction metrics (from Pioneer devices).
    • left_radial, right_radial
    • left_torque, right_torque
    • left_efficiency, right_efficiency

Examples

>>> GET /ride/928389327473?streams=power,heartrate
{id: 928389327473, title: "74km around Richmond", summary: {duration: 5134, ...}, ..., streams: {power: [86, 98, 98, ...], heartrate: [...]}}

PUT

Updates a ride.

This currently only supports updating the title and notes. Get in contact if you want this expanded.

Request

  • title
  • notes

Response

The same as GET if successful, or a list of errors.

DELETE

Deletes a ride.

Response

  • success — a short message if the operation was successful.

/ride/<id>/raw

GET

Gets the raw ride file that was originally uploaded.

The returned data is bitwise identical to the file that was originally uploaded. No processing or editing is applied. The file format is the same as it was when originally uploaded.

Response

This method returns the file as raw bytes. A filename, which includes the start date/time, and the format as the file extension, is included in the Content-Disposition header.

/user/<id>/training-plan
/me/training-plan

POST

Creates a new plan.

Request

All fields are required. (Provide an empty string for unused fields.)

  • title
  • date — Must be in the format YYYY-MM-DD.
  • duration
  • cadence
  • load
  • description
  • notes

Response

This returns the same as GET /plan/<id>.

GET

Get the list of training plans of a user.

Response

/plan/<id>

GET

Gets an individual training plan.

Response

  • id
  • user_id
  • title
  • date
  • order — Multiple plans can exist on the same day. This is normally 1.
  • duration
  • cadence
  • load
  • description
  • notes
  • ride_id — Only exists when the plan has been linked to a ride.

DELETE

Deletes a training plan.

Response

The same as GET.

PUT

Updates a training plan.

Request

All are optional.

  • title
  • date — Must be in the format YYYY-MM-DD.
  • order
  • duration
  • cadence
  • load
  • description
  • notes

Response

The same as GET.

/user/<id>/power-curve/<period>
/me/power-curve/<period>

GET

Gets the maximum power achieved for all time durations for a period.

Request

period can be either all, a year (i.e., 2018), or a year and month (i.e., 2018-9).

Response

  • curve — An array of elements that form the power curve, each in a three element array of:
    • Value in watts.
    • ID of ride in which the maximum power was achieved.
    • Date of the ride.
  • name

The duration that each element of the array represents is described in data format.

/tokens

POST

Create a new token for authenticating with the API.

Request

  • permissions — Must be all and provided.
  • team_id — If the user is the administrator of a team, they can create a token for the team instead of for themself.

Response

  • token — The token.
  • timestamp — When the token was created.
  • type — Either user or team.
  • user_id — The user ID of the token owner. Included if type is user.
  • team_id — The team ID of the token owner. Included if type is team.
  • permissions — A list of permissions this token allows the bearer to do. Currently always only includes all.

Examples

>>> POST /tokens {permissions: 'all'}
{ "timestamp": 1371671316, "user_id": 1000000, "token": "tgReLjMnGlW9RdPuuyQz4NXc4nQX2QT0", "type": "user", "permissions": [ "all" ] }

>>> POST /tokens {permissions: 'all', team_id: 308150}
{ "timestamp": 1374137008, "token": "ZmaQBjCEGMYwAdZeaxTKGLLgTxQnss4n", "type": "team", "team_id": 308150, "permissions": [ "all" ] }

GET

Gets the list of tokens that are owned by the user/team.

Response

  • tokens — An array of tokens in the same format as the response of the POST request.

Examples

>>> GET /tokens
{ "tokens" : [ { "timestamp": 1371671323, "user_id": 1000000, "token": "tgReLjMnGlW9RdPuuyQz4NXc4nQX2QT0", "type": "user", "permissions": [ "all" ] } ] }