---
title: "Cycling Power API: Power, Cadence and Speed Data Across Platforms"
canonical: "https://aifitnessapi.com/data/cycling-power-api"
cluster: "Health Data"
primary_query: "cycling power api"
last_reviewed: "2026-10-18"
description: "Cycling power, cadence and speed in HealthKit, Health Connect, Wear OS and Bluetooth: samples or series, counts per minute or RPM, and revolutions."
publisher: "AIFitnessAPI — independent, not sponsored"
cite_as: "\"Cycling Power API: Power, Cadence and Speed Data Across Platforms\", AIFitnessAPI, https://aifitnessapi.com/data/cycling-power-api"
---

# Cycling Power API: Power, Cadence and Speed Data Across Platforms

> Cycling power and cadence reach an app either as samples another app wrote into a platform store or straight from a Bluetooth sensor, and the two have different shapes. Apple HealthKit has had cyclingPower, cyclingCadence, cyclingSpeed and cyclingFunctionalThresholdPower since iOS 17.0 as discrete samples, and Apple describes both power types as estimated; Health Connect has PowerRecord, CyclingPedalingCadenceRecord and SpeedRecord as series records, with cadence read under the general exercise permission. The Bluetooth Cycling Power and CSC characteristics send instantaneous watts plus cumulative wheel and crank revolution counts with event times, not a cadence or speed value, so your code derives those. The Google Health API has no power or cadence type, and Wear OS Health Services offers only a general SPEED.

- Canonical: https://aifitnessapi.com/data/cycling-power-api
- Last reviewed: 2026-10-18
- Publisher: AIFitnessAPI (https://aifitnessapi.com) — independent, not sponsored
- Cite as: "Cycling Power API: Power, Cadence and Speed Data Across Platforms", AIFitnessAPI, https://aifitnessapi.com/data/cycling-power-api

---

A rider's power and cadence come from sensors on the bike, not the wrist. The platform stores hold those values after some app has read the sensor and written them, and the Bluetooth profiles carry them on the way in. This page is about the types on both ends. For pairing several sensors, choosing one source of truth and the FTMS overlap, see [reading cycling power, cadence and speed sensors](/devices/cycling-sensors-power-cadence).

## Where each value lives

| Metric | Apple HealthKit | Android Health Connect | Wear OS Health Services | Bluetooth GATT |
|---|---|---|---|---|
| Power | [`cyclingPower`](/healthkit-identifiers/cycling-power): discrete, power units | [`PowerRecord`](/health-connect/power-record): a Series, `POWER_AVG`, `POWER_MAX`, `POWER_MIN` | none | [Cycling Power Measurement 0x2A63](/bluetooth-fitness/cycling-power-measurement): Instantaneous Power, a `sint16` in watts |
| Cadence | [`cyclingCadence`](/healthkit-identifiers/cycling-cadence): discrete, counts per minute | [`CyclingPedalingCadenceRecord`](/health-connect/cycling-pedaling-cadence-record): a Series, `RPM_AVG`, `RPM_MAX`, `RPM_MIN` | none | crank revolution data in Cycling Power Measurement and [CSC Measurement 0x2A5B](/bluetooth-fitness/csc-measurement) |
| Speed | [`cyclingSpeed`](/healthkit-identifiers/cycling-speed): discrete, distance per time | [`SpeedRecord`](/health-connect/speed-record): a Series, `SPEED_AVG`, `SPEED_MAX`, `SPEED_MIN` | [`SPEED`](/wear-os-data-types/speed): meters per second | wheel revolution data in the same two characteristics |
| Threshold power | [`cyclingFunctionalThresholdPower`](/healthkit-identifiers/cycling-functional-threshold-power): discrete, power units | none | none | none |

Read from Apple's documentation on 2026-10-02, Google's Health Connect reference on 2026-10-03, its Health Services reference on 2026-10-04 and the Bluetooth SIG's files on 2026-10-17. All four HealthKit types are available from iOS 17.0 and watchOS 10.0.

The Google Health API is the platform with nothing here. It has no power, cadence or cycling speed data type, and the per-exercise summary its exercise type carries, which this page does not cover, has an average speed and pace but no power or cadence field in the discovery document.

## Estimated, generated, or instantaneous

The descriptions differ in a way that matters for labels. Apple's `cyclingPower` "measures the estimated power being used while riding a bike," and `cyclingFunctionalThresholdPower` "measures the estimated maximum average power sustained while riding a bike for 60 minutes." Google's `PowerRecord` "Captures the power generated by the user, e.g. during cycling or rowing with a power meter." The Bluetooth characteristic reports an Instantaneous Power from the sensor itself.

Apple's word is "estimated," and the identifier data does not say what produces the estimate. As our [cycling app guide](/build/cycling-app) puts it, the HealthKit types are a place to write and read a value, not a source of one. Show the writing app's name beside any power value you read from a store.

## Samples versus series

HealthKit stores each value as a discrete quantity sample, so a statistics query averages or takes the minimum or maximum, and summing power or cadence gives a meaningless number. Health Connect stores a ride's values as a Series record: one record per stretch of time, holding a list of samples, with averages, maxima and minima as aggregates. Wear OS delivers `SPEED` as a `SampleDataPoint` during an exercise, with `SPEED_STATS` for the session.

Cadence has a unit question on top. Health Connect's aggregates are named in revolutions per minute (RPM); Apple's cadence uses counts-per-minute units, and its identifier text does not say what one count is. Check one against the other on a device before you map them.

## Revolutions, not RPM, on the wire

Neither Bluetooth characteristic has a cadence or speed field. Each sends counters and times, and your code turns two consecutive notifications into a rate:

| Field | Cycling Power Measurement | CSC Measurement |
|---|---|---|
| Cumulative Wheel Revolutions | `uint32`, present when Flags bit 4 is set | `uint32`, present when Flags bit 0 is set |
| Last Wheel Event Time | `uint16` in 1/2048 second | `uint16` in 1/1024 second |
| Cumulative Crank Revolutions | `uint16`, present when Flags bit 5 is set | `uint16`, present when Flags bit 1 is set |
| Last Crank Event Time | `uint16` in 1/1024 second | `uint16` in 1/1024 second |

Two details from that table catch people. The wheel event time has twice the resolution in the power characteristic as in the speed and cadence one, so one shared parser is wrong for one of them. And turning wheel revolutions into a speed needs a distance per revolution that neither characteristic carries.

Cycling Power Measurement can carry much more, each behind its own bit of a 16-bit Flags field: Pedal Power Balance in half-percent units, Accumulated Torque in 1/32 newton meter with a bit saying whether it is wheel- or crank-based, extreme force and torque magnitudes, extreme angles, top and bottom dead spot angles, and Accumulated Energy in kilojoules. Because every optional field shifts the ones after it, offsets depend on which bits are set; the [byte-layout page](/bluetooth-fitness/cycling-power-measurement) shows how. [Cycling Power Vector 0x2A64](/bluetooth-fitness/cycling-power-vector) adds arrays of instantaneous force and torque magnitudes, with the crank angle of the first measurement.

## Permissions

Health Connect reads power under `android.permission.health.READ_POWER` and speed under `READ_SPEED`, but cadence under `android.permission.health.READ_EXERCISE`, the same permission as exercise sessions, rather than a cadence permission of its own. Wear OS `SPEED` sits under `ACTIVITY_RECOGNITION` in Google's Health Services permissions table. A cadence feature on Android therefore asks the user for exercise access.

## Traps

- **One parser for two time bases.** 1/2048 second in Cycling Power Measurement, 1/1024 second in CSC Measurement.
- **Counters wrap.** The crank counter is a `uint16`; handle rollover between notifications.
- **Estimated in Apple's words.** Do not relabel a HealthKit power value as measured.
- **Exercise permission for cadence.** On Android, plan for the broader consent screen.
- **Nothing to read in the cloud.** A Fitbit or Pixel Watch user's power and cadence are not in the Google Health API types.

## Before you ship

Re-read the identifier, record and characteristic pages before each release; they regenerate from Apple's, Google's and the SIG's sources. Parse each characteristic with its own time base, derive cadence and speed in one tested place, and store which app or sensor produced every value you read from a store.

## FAQ

### Which Health Connect permission covers cycling cadence?

The exercise permission. Google's data-types table puts CyclingPedalingCadenceRecord under android.permission.health.READ_EXERCISE and WRITE_EXERCISE, the same strings as exercise sessions, rather than a cadence permission of its own. Power and speed have their own: PowerRecord uses READ_POWER and WRITE_POWER, and SpeedRecord uses READ_SPEED and WRITE_SPEED. All three are Series records with average, maximum and minimum aggregates.

[Permalink](https://aifitnessapi.com/data/cycling-power-api#faq-1)

### Does the Bluetooth CSC characteristic send cadence in RPM?

No. CSC Measurement (0x2A5B) sends a cumulative crank revolution count as a uint16 and the time of the last crank event in units of 1/1024 second, present when bit 1 of its Flags field is set, and the same pair for the wheel when bit 0 is set. Your code computes cadence from the change between two notifications, handling the counter's rollover.

[Permalink](https://aifitnessapi.com/data/cycling-power-api#faq-2)

### Why does Apple describe HealthKit cycling power as estimated?

That is Apple's wording: cyclingPower measures the estimated power being used while riding a bike, and cyclingFunctionalThresholdPower the estimated maximum average power sustained for 60 minutes. Apple's identifier documentation does not say what produces the estimate, and the value in the store is whatever an app wrote. Health Connect's PowerRecord, by contrast, describes power generated by the user with a power meter.

[Permalink](https://aifitnessapi.com/data/cycling-power-api#faq-3)

### Can I get cycling power from the Google Health API?

Not from a data type. None of the Google Health API's 44 data types is power or cadence, and the exercise summary in its discovery document has average speed and pace but no power or cadence field. For power on Android, read Health Connect's PowerRecord, which Google describes with cycling and rowing as examples, or read the sensor over Bluetooth.

[Permalink](https://aifitnessapi.com/data/cycling-power-api#faq-4)
