Skip to content
AF
Health Connect API

HealthKit to Health Connect: the API equivalents, call by call

Last verified October 4, 2026 · 7 min read

Most HealthKit calls have a direct Health Connect counterpart: HKSampleQuery maps to readRecords, HKStatisticsQuery to aggregate, HKStatisticsCollectionQuery to aggregateGroupByDuration or aggregateGroupByPeriod, HKAnchoredObjectQuery to getChangesToken and getChanges, requestAuthorization to PermissionController's request contract, and save and delete to insertRecords and deleteRecords. HKObserverQuery with background delivery has no equivalent: Google says apps cannot be notified of new data, so Android apps poll, in the background through WorkManager with READ_HEALTH_DATA_IN_BACKGROUND. Paging, token expiry and permission visibility also differ.

Covered here:HealthKitHealth Connect

Porting a HealthKit integration to Android starts with a translation table: which Health Connect call does the job of each HealthKit class or method. Most rows have a close match. A few do not, and those are where ported apps break. This page maps the calls one by one, with each side described from its own platform's documentation, and links to the detailed page for both halves. For the product-level comparison, see HealthKit vs Health Connect; for the project plan, see adding Android to a HealthKit app. For the data types rather than the calls, the identifier translator looks up the Health Connect record for a HealthKit identifier, and the reverse.

The mapping#

JobHealthKitHealth Connect (Jetpack)
Read matching samples onceHKSampleQueryreadRecords(ReadRecordsRequest)
One total or average over a windowHKStatisticsQueryaggregate(AggregateRequest)
One value per hour, day or monthHKStatisticsCollectionQueryaggregateGroupByDuration / aggregateGroupByPeriod
Only what changed, including deletionsHKAnchoredObjectQuerygetChangesToken + getChanges
Be told when data changes, even in the backgroundHKObserverQuery + enableBackgroundDeliveryNo callback; a periodic WorkManager job with READ_HEALTH_DATA_IN_BACKGROUND
Ask the user for accessrequestAuthorization(toShare:read:completion:)PermissionController.createRequestPermissionResultContract()
SaveHKHealthStore.save(_:withCompletion:)insertRecords
DeleteHKHealthStore.delete(_:withCompletion:), deleteObjects(of:predicate:withCompletion:)deleteRecords
Filter by date and sourceHealthKit query predicatesTimeRangeFilter and dataOriginFilter on each request
Workout GPS routeHKWorkoutRouteQueryExerciseSessionRecord.exerciseRouteResult
async/awaitHealthKit async query descriptorsEvery read and write on HealthConnectClient is a Kotlin suspend function

Reading samples#

Apple describes HKSampleQuery as "A general query that returns a snapshot of all the matching samples currently saved in the HealthKit store." Google's readRecords "Retrieves a collection of Records." Both are one-shot reads.

The difference is paging. HealthKit lets you pass HKObjectQueryNoLimit, "A value indicating that the query returns all the matching samples in the HealthKit store." Health Connect has no such value: ReadRecordsRequest has a pageSize that defaults to 1000, and Google says that if more records match "you need to iterate over all pages of the response to retrieve all records by using pageToken." A port that reads once and stops loses everything after the first page.

Totals and buckets#

Apple's HKStatisticsQuery "performs statistical calculations over a set of matching quantity samples"; Google's aggregate "Reads AggregateMetrics according to requested read criteria". Google's guide gives the reason to use it over adding up raw records: "The aggregation API also contains logic to handle duplicate records, and lessens the chances of rate limiting."

For charts, Apple's HKStatisticsCollectionQuery "performs multiple statistics queries over a series of fixed-length time intervals", aligned to an anchorDate. Health Connect splits this into two methods, one bucketing by Duration and one by Period, each returning a list sorted "by time in ascending order". Google notes that a row "is returned only if there are Record to aggregate within start and end time of the row", so empty buckets are missing rather than zero. Apple says its collection query can also be long-running and receive updates "when the HealthKit store's content changes"; Google's grouped aggregates return a list once and stop.

Incremental sync#

Apple: HKAnchoredObjectQuery "returns an anchor value that corresponds to the last sample or deleted object received by that query", and later queries use it "to restrict their results to only newer saved or deleted objects." Health Connect's equivalent is a changes token: getChangesToken returns one, and getChanges returns the changes since it plus nextChangesToken.

Two differences matter. Google: "Changes-tokens are only valid for 30 days after they're generated", and an expired one sets changesTokenExpired; the Apple pages fetched for this article describe no expiry for HKQueryAnchor. And Google says a DeletionChange "only contains the id of the deleted record, and not the record type, due to privacy", so you must have stored Health Connect IDs to act on deletions. Recovery from expiry is on changes token expired.

Background delivery has no counterpart#

HealthKit can wake your app. Apple's observer query is "A long-running query that monitors the HealthKit store and updates your app when the HealthKit store saves or deletes a matching sample", and with enableBackgroundDelivery(for:frequency:withCompletion:) "HealthKit wakes your app whenever a process saves or deletes samples of the specified type", at most once per period of the frequency you choose.

Health Connect has no such mechanism. Google's sync guide: "As your app can't get notified of new data, check for new data at two points": when the app comes to the foreground, and periodically while it stays there. To poll while the app is closed, Google documents a periodic WorkManager worker that requires the extra READ_HEALTH_DATA_IN_BACKGROUND permission and a feature check. Ports that relied on HealthKit waking them need a polling schedule and a changes token instead. The general design is in background sync.

Permissions behave differently#

Apple's requestAuthorization(toShare:read:completion:) "Requests permission to save and read the specified data types", and Apple says "Each data type has two separate permissions, one to read it and one to share it." Health Connect works the same way per type, with HealthPermission.getReadPermission(...) and getWritePermission(...).

What you can learn afterwards differs. Apple: "your app cannot determine whether or not a user has granted permission to read data. If you are not given permission, it simply appears as if there is no data of the requested type in the HealthKit store." Google's getGrantedPermissions() "Returns a set of all health permissions granted by the user to the calling app", read permissions included. A port can, and Google's guides say should, check grants before every operation instead of inferring them from empty results.

Health Connect also has permissions with no HealthKit equivalent in this table: READ_HEALTH_DATA_HISTORY for data older than 30 days before the first grant, the background read permission, and the per-session route consent.

Saving and deleting#

Both platforms write batches as all-or-nothing. Apple: "When saving multiple objects, if any object cannot be saved, none of them are saved." Google: "Insertion of multiple records is executed in a transaction - if one fails, none is inserted."

Re-saving differs. Apple: "Saving an object with the same unique identifier as an object already in the HealthKit store fails with an errorInvalidArgument error." Health Connect lets you upsert through Metadata.clientRecordId: on a repeat insert, "whichever Record with the higher androidx.health.connect.client.records.metadata.Metadata.clientRecordVersion takes precedence." Health Connect also has updateRecords, which "Updates one or more Record of given UIDs to newly specified values."

For deletion, Apple: "Your app can delete only those objects that it has previously saved to the HealthKit store." Google's time-range deleteRecords deletes records "automatically filtered to Record belonging to the calling application", and its ID-based overload runs "in single transaction - if one fails, none is deleted." Apple's batch delete is likewise all-or-nothing.

Routes#

Apple: HKWorkoutRouteQuery is "A query to access the location data stored in a workout route", returning locations in batches because a route "can include a large number of CLLocation objects." Health Connect has no route query: the route comes back on the session as an ExerciseRouteResult, which is Data, ConsentRequired or NoData. Another app's route can require a one-time user consent through ExerciseRouteRequestContract, and Google says your app "can't read exercise route data created by other apps when it runs in the background."

Pitfalls#

  • Assuming one read returns everything. Loop on pageToken; there is no HKObjectQueryNoLimit.
  • Waiting for a callback that never comes. No Health Connect API pushes changes to your app. Poll on foreground and on a schedule.
  • Treating empty results as "no permission". On Android you can and should ask getGrantedPermissions().
  • Expecting zero-filled buckets. Grouped aggregates omit empty rows; fill gaps yourself, as covered in missing data and gaps.
  • Ignoring the 30-day history window. A HealthKit-style full backfill on Android needs READ_HEALTH_DATA_HISTORY, and the feature must be available.
  • Long-lived anchors. A changes token unused for 30 days expires; plan the re-sync path from the start.

Frequently asked questions

What is the Health Connect equivalent of HKObserverQuery?
There is none. Apple's observer query monitors the HealthKit store and, with background delivery, HealthKit wakes the app when matching samples are saved or deleted. Google's sync guide says Health Connect apps cannot be notified of new data and should check at foreground and periodically; for checks while the app is closed, Google documents a periodic WorkManager worker that needs READ_HEALTH_DATA_IN_BACKGROUND.
Which Health Connect method replaces HKStatisticsCollectionQuery?
Two methods: aggregateGroupByDuration for fixed-length buckets such as hours, and aggregateGroupByPeriod for calendar buckets such as days or months. Both return a list of rows sorted by time in ascending order. Unlike HealthKit's collection query they do not keep running for updates, and Google says a row is returned only if there are records to aggregate in it, so empty buckets are missing.
Can a Health Connect app tell whether read permission was granted, unlike HealthKit?
Yes. Apple says a HealthKit app cannot determine whether the user granted read permission, and that denied reads simply look like no data. Health Connect's PermissionController.getGrantedPermissions returns every health permission granted to the calling app, read permissions included, and Google's guides say to check it before each operation.
Is there a Health Connect equivalent of HKObjectQueryNoLimit?
No. ReadRecordsRequest has a pageSize that defaults to 1000, and Google says that when more records match you must iterate over all pages of the response using the pageToken. A port from HealthKit that reads once with no limit and stops will silently miss everything after the first page.

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