/user/<user_id>/me/user/<user_id>/heartrate/resting/<date>/user/<user_id>/heartrate/maximum/<date>/user/<user_id>/power/ftp/<date>/user/<user_id>/weight/<date>/user/<user_id>/rides/user/<user_id>/upload/user/<user_id>/upload/<upload_id>/ride/<ride_id>/ride/<ride_id>/raw/user/<user_id>/training-plan/plan/<plan_id>/user/<user_id>/power-curve/period/tokens
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.
There are not yet API endpoints for everything. Please get in contact if you want something that doesn’t exist yet.
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.
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).
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.
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.
Values are returned in the following format:
YYYY-MM-DD.YYYY-MM-DDThh:mm:ss.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));
}
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.
/user/<id>/meGETGets user information.
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.idnameemailtimezone — 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.
GETGet 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.
value — a number or null.PUTAdds an entry on the given day (or changes the entry if it already exists).
valuesuccessDELETEDeletes 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.
success/user/<id>/rides/me/ridesGETGet the list of rides of a user.
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.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/uploadPOSTUploads 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.
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.filenametitlenotesvirtual — 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.This returns the same as GET /me/upload/<upload_id>.
>>> 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>GETGet 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.
upload_iduser_idfilenameformatsize — The size of the file, in bytes.datetimestatus — processing, done or error.ride_id — null 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>GETGets a ride.
streams — all, 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.iduser_idtitlenoteslocal_datetimeutc_datetimetype — cycling, gym, running, swimming, walking, hiking, rowing, kayaking, sup (stand-up paddleboarding), skiing, snowboarding or othersubtype — road, trainer, virtual, tt, cx, track, mtb or bmx.purpose — ride, 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.duration — total_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).distanceclimbingworkepower — 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_cadenceavg_speedavg_heartrateavg_powermax_cadencemax_speedmax_heartratemax_powerzones
powerheartratehas — Whether or not the ride file contains the data stream.
powerheartratecadencespeedlrbalancegpselevationtemperatureps — 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.thbsmo2rr — Respiration rate.hrv — Heart rate variability.gearspower_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.
powerspeeddistanceheartratecadencelrbalancelatitudelongitudeelevationgradienttemperaturethbsmo2respiration_rateheart_rate_variabilitygearsleft_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_radialleft_torque, right_torqueleft_efficiency, right_efficiency>>> GET /ride/928389327473?streams=power,heartrate
{id: 928389327473, title: "74km around Richmond", summary: {duration: 5134, ...}, ..., streams: {power: [86, 98, 98, ...], heartrate: [...]}}
PUTUpdates a ride.
This currently only supports updating the title and notes. Get in contact if you want this expanded.
titlenotesThe same as GET if successful, or a list of errors.
DELETEDeletes a ride.
success — a short message if the operation was successful./ride/<id>/rawGETGets 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.
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-planPOSTCreates a new plan.
All fields are required. (Provide an empty string for unused fields.)
titledate — Must be in the format YYYY-MM-DD.durationcadenceloaddescriptionnotesThis returns the same as GET /plan/<id>.
GETGet the list of training plans of a user.
plans — An array of plans in the format that /plan/<plan_id> returns./plan/<id>GETGets an individual training plan.
iduser_idtitledateorder — Multiple plans can exist on the same day. This is normally 1.durationcadenceloaddescriptionnotesride_id — Only exists when the plan has been linked to a ride.DELETEDeletes a training plan.
The same as GET.
PUTUpdates a training plan.
All are optional.
titledate — Must be in the format YYYY-MM-DD.orderdurationcadenceloaddescriptionnotesThe same as GET.
/user/<id>/power-curve/<period>/me/power-curve/<period>GETGets the maximum power achieved for all time durations for a period.
period can be either all, a year (i.e., 2018), or a year and month (i.e., 2018-9).
curve — An array of elements that form the power curve, each in a three element array of:
nameThe duration that each element of the array represents is described in data format.
/tokensPOSTCreate a new token for authenticating with the API.
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.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.>>> 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" ] }
GETGets the list of tokens that are owned by the user/team.
tokens — An array of tokens in the same format as the response of the POST request.>>> GET /tokens
{ "tokens" : [ { "timestamp": 1371671323, "user_id": 1000000, "token": "tgReLjMnGlW9RdPuuyQz4NXc4nQX2QT0", "type": "user", "permissions": [ "all" ] } ] }