How to access Google Health Connect data in your backend
Introduction
Google Health Connect has no server-side API. If you want Health Connect data in your backend, there is only one path: build an Android app that reads data from Health Connect on the device and uploads it to your server. This is architecturally different from every other wearable integration covered in this series. There is no OAuth flow you can initiate from your backend, no token to exchange, no webhook to receive. The data lives on the user's Android device and your app must go get it.
This guide covers the complete pipeline: setting up Health Connect in your Android app, requesting permissions correctly, implementing reliable background sync with WorkManager and change tokens, serializing and uploading data to your backend, and handling the production edge cases that trip up most implementations. It also covers how to normalize Health Connect data once it reaches your backend so it fits alongside data from server-side providers.
Android app setup
Add the Health Connect SDK dependency to your app's build.gradle file. Use the androidx.health.connect:connect-client library. Check the current version in Google's Maven repository. Add the required permissions to your AndroidManifest.xml for each Health Connect data type you intend to read. Each data type has a separate read permission. Declare only the permissions you will actually request and use.
Also add a queries element to your AndroidManifest.xml declaring the androidx.health.connect.client package. This is required for Android to recognize that your app uses Health Connect and enables the permission request flow.
Check for Health Connect availability when the app starts. On Android 14+, Health Connect is built into the system. On older versions, the user may need to install the Health Connect app separately. Use HealthConnectClient.getSdkStatus(context) to check availability before attempting to use the SDK. If the status indicates unavailable, direct the user to the Play Store to install Health Connect.
Requesting and managing permissions
Health Connect permissions are requested using a specialized ActivityResultContract called RequestMultiplePermissions paired with Health Connect's PermissionController.createRequestPermissionResultContract(). This is not the same as the standard Android runtime permissions flow, although it looks similar to the user.
Request permissions at the right moment in your onboarding flow. Explain to the user before the permission dialog appears which data you will read and why. Users who understand the value of sharing health data are significantly more likely to grant all requested permissions. A one-screen explanation before the permission dialog showing what the app does with each data type improves grant rates.
After permissions are granted, check which were actually approved using healthConnectClient.permissionController.getGrantedPermissions(). Do not assume all requested permissions were granted. Build your sync logic to work with whatever subset of permissions the user granted and surface clear messaging about which features require which permissions.
Permissions can be revoked at any time. Check granted permissions at the start of each sync job. If a previously granted permission is no longer present, skip the affected data types and update your UI to show the restricted data types.
Reading data and change tokens
Health Connect read operations use the readRecords method on the HealthConnectClient. Each call specifies a record type class, a time range filter, a page size and an optional page token for pagination. For initial sync, use a time range from 30 days ago to now. For incremental sync, use change tokens instead.
Obtain a change token by calling getChangesToken with the record types you want to track. After your initial data read, store this token. On subsequent syncs, call getChanges with the stored token to receive only records that have been added, modified or deleted since the token was issued. This dramatically reduces the data you need to process and upload on each sync cycle.
Handle token expiry: change tokens expire after 30 days. If getChanges returns a ChangesTokenExpiredException, fall back to a full time-range read and obtain a new change token after completing the read.
Reliable background sync with WorkManager
Background work in Android is subject to battery optimization restrictions that can kill background services. WorkManager is the correct tool for reliable background Health Connect sync. It survives process death, handles retry with backoff, and respects Android's battery optimization while ensuring work eventually completes.
class HealthConnectSyncWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val client = HealthConnectClient.getOrCreate(applicationContext)
val prefs = applicationContext.getSharedPreferences("hc_sync", Context.MODE_PRIVATE)
return try {
val recordTypes = listOf(
ExerciseSessionRecord::class,
SleepSessionRecord::class,
HeartRateRecord::class,
StepsRecord::class,
HeartRateVariabilityRmssdRecord::class
)
val payloads = mutableListOf<Any>()
for (recordType in recordTypes) {
val tokenKey = "change_token_${recordType.simpleName}"
val storedToken = prefs.getString(tokenKey, null)
if (storedToken != null) {
val changesResponse = client.getChanges(storedToken)
payloads.addAll(changesResponse.changes)
prefs.edit().putString(tokenKey, changesResponse.nextChangesToken).apply()
} else {
val timeFilter = TimeRangeFilter.between(
Instant.now().minus(30, ChronoUnit.DAYS),
Instant.now()
)
val response = client.readRecords(
ReadRecordsRequest(recordType, timeFilter)
)
payloads.addAll(response.records)
val newToken = client.getChangesToken(
ChangesTokenRequest(setOf(recordType))
)
prefs.edit().putString(tokenKey, newToken).apply()
}
}
uploadToBackend(payloads)
Result.success()
} catch (e: PermissionException) {
// Permissions revoked, do not retry
Result.failure()
} catch (e: Exception) {
Result.retry()
}
}
private suspend fun uploadToBackend(payloads: List<Any>) {
// Serialize and POST to your backend endpoint
}
}
Register this Worker as a PeriodicWorkRequest with a one-hour interval and a network connectivity constraint. Use enqueueUniquePeriodicWork with ExistingPeriodicWorkPolicy.KEEP to ensure only one instance runs at a time.
Serializing and uploading Health Connect data
Health Connect SDK objects are not directly JSON serializable. You need to map each record type to a serializable data class before uploading. Define a data class for each record type you sync and write a conversion function that extracts the fields you need.
Include the dataOrigin source package name from each record's metadata in your serialized payload. This allows your backend to identify which app contributed each record, which is important for deduplication when multiple apps write overlapping data to Health Connect.
Upload batches rather than individual records. Group all records from a sync cycle into a single request payload and POST to your backend. Your backend should process the payload idempotently: uploading the same record twice should not create duplicates. Use the Health Connect record ID (available in each record's metadata) as a stable deduplication key in your backend storage.
Backend normalization
Once Health Connect data reaches your backend, normalize it to your common health data schema. Map Health Connect record types to your canonical entities: ExerciseSessionRecord to a workout event, SleepSessionRecord with SleepStageRecord children to a sleep record, HeartRateRecord to heart rate time series data, StepsRecord to daily activity, and HeartRateVariabilityRmssdRecord to HRV measurements.
Health Connect sport type identifiers are enumerations from the ExerciseSessionRecord class (for example ExerciseSessionRecord.EXERCISE_TYPE_RUNNING, ExerciseSessionRecord.EXERCISE_TYPE_CYCLING). Map these to your internal sport type taxonomy using the same mapping table you use for other providers.
Timestamps in Health Connect are stored as Instant objects (UTC). Ensure your backend preserves UTC timestamps and handles timezone display on the client side based on user preference or the local timezone recorded in the session metadata.
Production edge cases
Large initial syncs can fail due to payload size limits or upload timeouts. Implement chunked uploads for the initial 30-day backfill: process data in daily batches and upload each batch before moving to the next day. Store the last successfully uploaded date so you can resume from where you left off if the initial sync is interrupted.
Device battery optimization can delay or kill WorkManager jobs on some Android manufacturers that apply aggressive background process restrictions (Xiaomi, OnePlus, Huawei and others are known for this). Include device-specific battery optimization guidance in your app's settings or onboarding flow, directing users to exempt your app from battery optimization. Without this, sync jobs may not run reliably on a significant portion of Android devices.
Multiple overlapping change token streams for the same record type can arise if a user has multiple sessions of your app running or reinstalls the app. Manage change token storage carefully: store tokens in a persistent location that survives app updates (SharedPreferences or Room database) and implement a reset mechanism that clears all change tokens and triggers a fresh time-range backfill if you detect inconsistency.
Open Wearables
Open Wearables is an open-source platform that connects your application to wearable and health data providers through a single API. One normalized data model, multi-provider support in a single self-hosted deployment, no per-user fees, MIT licensed, and health intelligence built in: normalized recovery, sleep, activity and biometric data ready for your product layer.
We offer custom deployment and integration support through Momentum. If your team needs help getting to production faster, we can set up and configure Open Wearables as part of a managed engagement. Let's talk.
Book a demo to see how Open Wearables fits your use case.
Google Health Connect integration
View the full Google Health Connect integration documentation on Open Wearables.
See related articles
Google Health Connect Integration: Android Health Data for Developers