Skip to content
AF
Health Connect API

aggregate() and AggregateRequest: totals, averages and deduplicated sources

Last verified October 4, 2026 · 5 min read

HealthConnectClient.aggregate takes an AggregateRequest (a set of AggregateMetric values such as StepsRecord.COUNT_TOTAL, a TimeRangeFilter and an optional set of DataOrigin values) and returns one AggregationResult for the whole window. You read each metric with result[metric], which is null when no record contributed to it. Google documents that for Activity and Sleep data the Aggregate API drops duplicate data and keeps only the data from the app the user has given the highest priority, while for other types it combines the data of every app that wrote it.

Covered here:Health Connect

aggregate is how you ask Health Connect for a number instead of a list: a total, a minimum, a maximum or an average over one window. It is also the call Google tells you to use for step totals. This page covers the request, the result and what Google says about duplicate data, from the HealthConnectClient reference, the AggregateRequest reference, the AggregationResult reference and the aggregate data guide, all fetched October 4, 2026.

The method and the request#

The declaration is suspend fun aggregate(request: AggregateRequest): AggregationResult. Google's summary: it "Reads AggregateMetrics according to requested read criteria: Records from AggregateRequest.dataOriginFilter and within AggregateRequest.timeRangeFilter." Like the other read calls it lists RemoteException, SecurityException and IOException.

AggregateRequest(
    metrics: Set<AggregateMetric<*>>,
    timeRangeFilter: TimeRangeFilter,
    dataOriginFilter: Set<DataOrigin> = emptySet()
)
ParameterGoogle's description
metrics"Set of AggregateMetrics to aggregate."
timeRangeFilter"The TimeRangeFilter to read from."
dataOriginFilter"Set of DataOrigins to read from, or empty for no filter."

The metrics are constants on the record classes: StepsRecord.COUNT_TOTAL, DistanceRecord.DISTANCE_TOTAL, HeartRateRecord.BPM_MAX and so on. The guide splits them into cumulative aggregation, which "computes the total value", and statistical aggregation, which "computes the minimum, maximum, or average values of records with samples." The full list of metrics for every record type is on the Health Connect aggregate metrics page.

Availability. The reference labels AggregateRequest and AggregationResult "Added in 1.1.0". The aggregate method carries no label of its own.

Reading the AggregationResult#

MemberWhat Google documents
operator fun <T : Any> get(metric: AggregateMetric<T>): T?"If there are no relevant records contributing to the requested metric, the metric will not be provided." Returns "the value of the metric, or null if not set."
operator fun contains(metric: AggregateMetric<*>): Boolean"Checks whether the aggregation result contains a metric or not."
val dataOrigins: Set<DataOrigin>"Set of DataOrigins that contributed to the aggregation result."

Values are typed by the metric. DISTANCE_TOTAL gives a length, which Google's sample reads with .inMeters; the heart-rate minimum and maximum come back as Long. One request can mix record types: the example on Google's AggregationResult page asks for a steps total and a distance total together and reads both from the same result.

A sketch#

suspend fun stepsAndPeakHeartRate(
    client: HealthConnectClient,
    start: Instant,
    end: Instant
) {
    val result = client.aggregate(
        AggregateRequest(
            metrics = setOf(StepsRecord.COUNT_TOTAL, HeartRateRecord.BPM_MAX),
            timeRangeFilter = TimeRangeFilter.between(start, end)
        )
    )
    val steps: Long? = result[StepsRecord.COUNT_TOTAL]   // null: nothing contributed
    val peakBpm: Long? = result[HeartRateRecord.BPM_MAX]
    val sources = result.dataOrigins.map { it.packageName }
    // Show "no data" when steps is null instead of rendering 0.
}

What Google says about duplicates#

This is the reason aggregate exists for step totals. The aggregate guide:

"End users can set priority for the Sleep and Activity apps that they have integrated with Health Connect. Only end users can alter these priority lists. When you perform an aggregate read, the Aggregate API accounts for any duplicate data and keeps only the data from the app with the highest priority."

And, later on the same page: "Only the Activity and Sleep data types are deduped by Health Connect" and "For other types of data, the aggregated results combine all data of the type in Health Connect from all apps which wrote the data." The read guide adds that the aggregation API "contains logic to handle duplicate records, and lessens the chances of rate limiting."

What Google does not spell out: the page gives steps and distance as examples of duplicate Activity data but does not list every record type it counts as Activity, and it does not describe how Health Connect decides that two records overlap. What happens to totals when a user reorders that priority list is covered in deduplicating health data.

Filtering by data origin#

To aggregate one app's data, pass its package name. The guide's example aggregates steps "from a specific app" with dataOriginFilter = setOf(DataOrigin(appPackageName)). Google does not say how the priority-based deduplication above behaves once the filter narrows the read to a single app.

Since the June 2026 Health Connect update, a filter also decides whether you see the steps the phone counts itself. The read guide: "If your app reads aggregated step counts using aggregate and doesn't filter by DataOrigin, on-device steps are automatically included in the total". On-device steps recorded since that update are attributed to a device-specific Synthetic Package Name (SPN) instead of android, so an app that reads them with a filter, or breaks steps down by source, "must query for records where the DataOrigin is android or matches the device's SPN." Google adds that "you must not hardcode SPN values." The guide also notes that the API that returns the SPN, getCurrentDeviceDataSource(), "is not yet available in the Health Connect Jetpack library". The record itself is described on the StepsRecord page.

Pitfalls#

  • ?: 0L turns "no data" into zero. Google's samples default a missing metric to 0L. For a chart or a goal, a day with no records and a day with zero steps are different facts; keep the null until you render.
  • Summing readRecords instead. Google's read guide says to use aggregate() rather than readRecords() for cumulative types "to avoid double counting from multiple sources". A hand-written sum gets none of the deduplication described above.
  • A DataOrigin filter that drops the phone's own steps. Filtering to an app list without android and the device SPN leaves out on-device steps. Without a filter they are included.
  • Expecting deduplication for every type. Only Activity and Sleep data are deduplicated by priority. For other types, Google says the aggregate combines the data of every app that wrote it.
  • The default read window applies. The aggregate guide repeats that apps can read data for up to 30 days before any permission was first granted, and that without PERMISSION_READ_HEALTH_DATA_HISTORY "an attempt to read records older than 30 days results in an error." Health Connect returns no data covers that limit.
  • Malformed requests throw. Google's read guide lists IllegalStateException for a request that "isn't a valid construction", with an example from the bucketed variants covered on the Duration vs Period page.

Frequently asked questions

Does Health Connect's aggregate API remove duplicate steps written by different apps?
For Activity and Sleep data, yes. Google's aggregate guide says users can set a priority order for their Activity and Sleep apps, and that an aggregate read accounts for duplicate data and keeps only the data from the app with the highest priority, giving steps and distance as examples. Google also says only Activity and Sleep types are deduplicated; for other types the aggregate combines the data of every app that wrote it.
Why does an AggregationResult return null for a metric I asked for?
Because no records contributed to it. Google's AggregationResult reference says that if there are no relevant records contributing to the requested metric, the metric will not be provided, and get returns null when the metric is not set. You can test for this with contains(metric). Defaulting the value to zero, as Google's samples do, hides the difference between no data and a real zero.
Can one Health Connect AggregateRequest include metrics from different record types?
Yes. The metrics parameter is a set of AggregateMetric values, and the example on Google's AggregationResult reference requests a steps total and a distance total together, then reads each one from the same result. Each value is typed by its metric, so a distance total comes back as a length and a heart-rate maximum as a Long.
How can I tell which apps contributed to a Health Connect aggregate value?
Read the dataOrigins property of the AggregationResult. Google's reference describes it as the set of DataOrigin values that contributed to the aggregation result, and each DataOrigin carries the package name of the app that inserted the data. To restrict an aggregate to particular apps instead, pass their package names in the request's dataOriginFilter.

All 58 questions in Health Connect API →

Keep reading

Elsewhere on the site

Pages that share this one’s concepts and sources, from other sections.

Next steps

Was this page useful?

Independent comparison, last reviewed October 4, 2026. Pricing, rate limits, and feature availability change often — confirm current details in each provider’s official documentation before you commit. Product and company names are trademarks of their respective owners; AIFitnessAPI is not affiliated with, endorsed by, or sponsored by any product listed here.

← All health connect api · by AIFitnessAPI