Skip to content

Telemetry

Telemetry is a high-volume time series associated with an athlete. Every sample is encoded as a compact numeric array instead of a JSON object. The first value is always a Unix timestamp in milliseconds, expressed in UTC.

Prefer the aggregate fields in a normal ClubTrainingDTO unless the integration needs sample-level analysis, charting, or route reconstruction.

The Club API exposes five raw telemetry series:

Series Operation Tuple
Heart rate GET /Athletes/{userId}/Telemetry/Heartbeats [timestampUnixMs, beatsPerMinute]
Geolocation GET /Athletes/{userId}/Telemetry/GeoPositions [timestampUnixMs, distanceMeters, latitudeDegrees, longitudeDegrees, altitudeMeters]
Power GET /Athletes/{userId}/Telemetry/Power [timestampUnixMs, watts]
Cadence GET /Athletes/{userId}/Telemetry/Cadence [timestampUnixMs, revolutionsPerMinute]
HIT GET /Athletes/{userId}/Telemetry/Hits 19-value tuple described under HIT tuples

Both from and to must be supplied as ISO 8601 timestamps. The boundaries are inclusive and one request may cover at most five hours:

Terminal window
curl --request GET \
--url "https://api-gw.uptivo.fit/club/Athletes/300001/Telemetry/Heartbeats?from=2026-09-04T08%3A00%3A00Z&to=2026-09-04T09%3A00%3A00Z" \
--header "X-Sub-Key: <subscription-key>" \
--header "Authorization: Bearer <club-api-key>"

Raw telemetry responses are not paginated. Split a longer period into adjacent windows of no more than five hours, remembering that both ends are inclusive. Deduplicate samples on the boundary timestamp if two windows share it.

The athlete must currently belong to the club selected by the Club API Key. The endpoint returns 404 Not Found when the athlete is outside that scope. When the athlete is accessible but the requested interval has no samples, the API returns 200 OK with an empty array.

Array positions are part of the public contract; do not infer them from the numeric type alone.

[
[1603317955000, 92],
[1603317960000, 91]
]

The first sample means 2020-10-21T22:05:55.000Z and 92 beats per minute.

[
[1602916604000, 1750.98, 45.7227933, 9.1307737, 401.05]
]

This sample means:

Position Meaning Unit
0 Timestamp Unix epoch milliseconds, UTC
1 Distance from the beginning of the track meters
2 Latitude decimal degrees
3 Longitude decimal degrees
4 Altitude meters

The example timestamp is 2020-10-17T06:36:44.000Z.

{
"Powers": [[1603350935000, 203]],
"Cadences": [[1596470835000, 82]]
}

The power sample is 203 watts at 2020-10-22T07:15:35.000Z. The cadence sample is 82 revolutions per minute at 2020-08-03T16:07:15.000Z.

A decoder should convert tuples into named application objects immediately:

function decodeHeartRate([timestampUnixMs, beatsPerMinute]) {
return { timestamp: new Date(timestampUnixMs), beatsPerMinute };
}
function decodeGeoPosition([
timestampUnixMs,
distanceMeters,
latitudeDegrees,
longitudeDegrees,
altitudeMeters,
]) {
return {
timestamp: new Date(timestampUnixMs),
distanceMeters,
latitudeDegrees,
longitudeDegrees,
altitudeMeters,
};
}

Reject or quarantine tuples with an unexpected length. This prevents a future contract mismatch from silently shifting one measurement into another field.

Set withTelemetry=true on any Training operation when telemetry must be aligned to the exact Begin and End of each training:

An abbreviated response has this shape:

{
"TrainingId": 400001,
"UserId": 300001,
"Begin": "2026-09-04T08:00:00Z",
"BeginDateLocal": "2026-09-04",
"End": "2026-09-04T09:00:00Z",
"Telemetry": {
"HeartBeats": [[1788508805000, 92]],
"GeoPositions": [],
"Powers": [[1788508810000, 203]],
"Cadences": [[1788508815000, 82]],
"Hits": []
}
}

The response remains a flat ClubTrainingDTO. Telemetry is omitted when withTelemetry is false; when requested, an empty series means that no qualifying samples were found. Do not treat an empty series as an error for the whole training.

The independent withUser=true flag adds the current ClubAthleteDTO as User. It can be combined with withTelemetry=true. User is omitted when it was not requested and when the owner of a historical club training is no longer an Athlete of the selected club.

Lists with withTelemetry=true default to 10 trainings and cap count at 30. They retrieve several time series for every item, so use the normal training list for bulk synchronization and fetch telemetry only for the records that need it. Both enrichment flags leave selection and the ID or (Begin, TrainingId) cursor position unchanged, so withTelemetry and withUser may be changed between pages. Keep the endpoint, range, and selection filters unchanged, then pass NextCursor or PrevCursor without modification. See Pagination and synchronization.

HIT samples are available from the raw Athlete telemetry operation and inside Training responses requested with withTelemetry=true. Each tuple contains 19 values:

Positions Meaning Unit
0 UTC Unix timestamp milliseconds since the Unix epoch
1, 10 Right and left hit count count
2, 11 Right and left average force g
3, 12 Right and left maximum force g
4, 13 Right and left average speed km/h
5, 14 Right and left maximum speed km/h
6, 15 Right and left out time seconds
7, 16 Right and left minimum out time seconds
8, 17 Right and left in time seconds
9, 18 Right and left minimum in time seconds
  • Request the narrowest time interval that satisfies the use case.
  • Do not download raw telemetry during every reconciliation if aggregate training values are enough.
  • Stream or process large arrays incrementally where your HTTP client permits it, and avoid copying the payload repeatedly in memory.
  • Apply retention, access-control, and privacy rules appropriate to athlete health and location data.
  • Log identifiers and range boundaries, not telemetry payloads or credentials.

The Club API allows 50 calls per minute and 25,000 calls per 30 days. Raw telemetry can produce large payloads, so clients should use bounded concurrency and avoid repeated downloads of the same interval. See Trainings for synchronization and Errors for quota headers and 429 Too Many Requests handling.