The Google Health API allows your application to receive real-time notifications when a user's health data changes. Instead of polling for changes, your server receives an HTTPS POST request (webhook){:target="_blank" class="external"} as soon as data is available in the Google Health API.
Supported data types
Webhook notifications are supported for the following data types:
- Active Zone Minutes
- Activity Level
- Altitude
- Blood Glucose
- Body Fat
- Calories In Heart Rate Zone
- Daily Heart Rate Variability
- Daily Heart Rate Zones
- Daily Oxygen Saturation
- Daily Respiratory Rate
- Daily Resting Heart Rate
- Daily Sleep Temperature Derivations
- Distance
- Exercise
- Floors
- Heart Rate
- Heart Rate Variability
- Height
- Hydration Log
- Nutrition Log
- Respiratory Rate Sleep Summary
- Run Vo2 Max
- Sedentary Period
- Sleep
- Steps
- Time In Heart Rate Zone
- Total Calories
- Weight
Notifications are sent for these data types only when a user has granted consent for one of the corresponding scopes:
- Activity, which covers steps, altitude, distance, and floors data types:
https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonlyhttps://www.googleapis.com/auth/googlehealth.activity_and_fitness.writeonly
- Health Metrics, which covers the weight data type:
https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonlyhttps://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.writeonly
- Sleep, which covers the sleep data type:
https://www.googleapis.com/auth/googlehealth.sleep.readonlyhttps://www.googleapis.com/auth/googlehealth.sleep.writeonly
IAM service accounts
While not required, we recommend using an IAM Service Account when configuring subscribers for the Google Health API. Service accounts provide better security for application workloads compared to standard user accounts through the following features:
- Automated short-lived credentials: When attached to a Google Cloud execution environment (such as Compute Engine, Cloud Run, or Google Kubernetes Engine), service accounts automatically obtain and rotate secure, short-lived credentials. This removes the risks of managing and storing persistent static keys.
- Principle of least privilege: Service accounts provide dedicated identities for workloads. You can grant them only the specific permissions needed to manage subscriber endpoints, avoiding broader access to your Google Cloud resources.
- Lifecycle independence: Service accounts operate independently of any individual user's account, ensuring that personnel changes don't impact long-term authentication stability.
Set up a service account
To configure your subscriber application to authenticate using a service account:
- Create a service account: In the Google Cloud console, navigate to your project's IAM & Admin page to create a new user-managed service account.
- Grant necessary IAM roles: Assign the service account the appropriate roles required to manage subscribers on your Google Cloud project.
- Attach the service account to your workload: Configure the environment hosting your subscriber logic to run as the new service account.
This allows your application code (such as Google API client libraries) to
automatically detect and use the service account's short-lived credentials
when calling the
projects.subscribersREST API.
CPE roles
To manage Google Health API Subscribers or Subscriptions, you must grant the appropriate role to the impersonated Service Account making the API calls. Depending on the level of access needed, assign one of the following roles:
- Google Health API Read
- Google Health API Editor
- Google Health API Admin
Learn more about Google Health API IAM roles and permissions.
Manage subscribers
Before you can receive notifications, you must register a Subscriber, which
represents your application's notification endpoint. You can manage subscribers
using the REST API available at
projects.subscribers.
Your subscriber endpoint must use HTTPS (TLSv1.2+) and be publicly accessible.
During subscriber creation and updates, the Google Health API performs a
verification challenge to ensure you own the endpoint URI. If verification
fails, subscriber creation and update operations fail with a
FailedPreconditionException.
Create a subscriber
To register a new subscriber for your project, use the
create
endpoint. You need to provide:
project-id: The project number where the webhook service account was created.subscriberId: A unique identifier that you provide for the subscriber. This ID must be between 4 and 36 characters, and match the regular expression ([a-z]([a-z0-9-]{2,34}[a-z0-9])).endpointUri: The destination URL for webhook notifications.subscriberConfigs: The data types you want to receive notifications for, and the subscription policy for each.endpointAuthorization: The authorization mechanism for your endpoint. This must contain asecretthat you provide. The value ofsecretis sent in theAuthorizationheader with each notification message. You can use this token to verify that incoming requests are from the Google Health API. For example, you can setsecrettoBearer R4nd0m5tr1ng123for Bearer authentication, orBasic dXNlcjpwYXNzd29yZA==for Basic authentication.
In subscriberConfigs you must set subscriptionCreatePolicy for each data
type. Set it to AUTOMATIC to use automatic subscriptions, or MANUAL if you
intend to manage user subscriptions yourself. See
automatic subscriptions and
manual subscriptions for more details on each option.
Request
POST https://health.googleapis.com/v4/projects/project-id/subscribers?subscriberId=subscriber-id
{
"endpointUri": "https://myapp.com/webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
],
"endpointAuthorization": {
"secret": "Bearer example-secret-token"
}
}Response
{
"name": "projects/project-id/subscribers/subscriber-id",
"endpointUri": "https://myapp.com/webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
]
}List subscribers
Use the list endpoint
to retrieve all subscribers registered for your project.
Request
GET https://health.googleapis.com/v4/projects/project-id/subscribers
Response
{
"subscribers": [
{
"name": "projects/project-id/subscribers/subscriber-id",
"endpointUri": "https://myapp.com/webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
],
"endpointAuthorization": {
"authorizationTokenSet": true
}
}
],
"totalSize": 1
}Update a subscriber
Use the patch endpoint
to update a subscriber in your project. The fields that can be updated are
endpointUri, subscriberConfigs, and endpointAuthorization.
You update fields by providing an updateMask query parameter and a request
body. The updateMask must contain a comma-separated list of field names that
you want to update, using camel case for field names
(for example, endpointUri). The request body must contain a partial Subscriber
object with the new values for fields you want to update. Only fields specified
in updateMask are updated. If you provide fields in the request body that are
not in updateMask, they are ignored.
If you update endpointUri or endpointAuthorization, endpoint verification is
performed. See Endpoint verification for details.
When updating subscriberConfigs, note that it's a full replacement, not a
merge. If subscriberConfigs is included in updateMask, all stored
configurations for that subscriber are overwritten with list provided in request
body. To add or remove a configuration, you must provide the complete set of
configurations. If you are updating other fields and want to keep your current
configurations, omit subscriberConfigs from updateMask.
Request
PATCH https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id?updateMask=endpointUri
{
"endpointUri": "https://myapp.com/new-webhooks/health"
}Response
{
"name": "projects/project-id/subscribers/subscriber-id",
"endpointUri": "https://myapp.com/new-webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
]
}Delete a subscriber
Use the delete
endpoint to remove a subscriber from your project. Once deleted, the subscriber
will no longer receive notifications.
Request
DELETE https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id
Response
An empty response body with HTTP status `200 OK` is returned if deletion is successful.{}Endpoint verification
To ensure the security and reliability of your notification delivery, the Google
Health API performs a mandatory two-step verification handshake whenever you
create a subscriber or update its endpoint configuration (endpointUri or
endpointAuthorization). This process is performed synchronously during the
API call. The service sends two automated POST requests to your endpoint URI,
using the User-Agent Google-Health-API-Webhooks-Verifier, with the JSON body
{"type": "verification"}.
- Authorized Handshake: The first request is sent with your configured
Authorizationheader. Your server must respond with a200 OKor201 Createdstatus. - Unauthorized Challenge: The second request is sent without credentials.
Your server must respond with a
401 Unauthorizedor403 Forbiddenstatus.
This handshake confirms that your endpoint is active and correctly enforcing
security. If either step fails, the API request fails with a
FAILED_PRECONDITION error. Only after this handshake succeeds is your
subscriber saved and activated to receive health data notifications.
Key rotation
If you need to rotate keys for endpointAuthorization, follow these steps:
- Configure your endpoint to accept both old and new
endpointAuthorizationvalues. - Update the subscriber configuration with new
endpointAuthorizationvalue using apatchrequest with?updateMask=endpointAuthorization. - Configure your endpoint to accept only new
endpointAuthorizationvalue after confirming step 2 was successful.
User subscriptions
The Google Health API helps you manage user subscriptions efficiently, reducing the need for manual registration during user onboarding.
Automatic subscriptions
We recommend using automatic subscriptions. To enable this feature, set
subscriptionCreatePolicy to AUTOMATIC in your subscriberConfigs for the
specific data types. The dataTypes you specify with an AUTOMATIC policy are
the same data types for which the Google Health API sends notifications,
provided user consent is also granted for those data types.
When a user grants application consent for scopes that correspond to data
types with an AUTOMATIC policy, the Google Health API automatically tracks and
sends out notifications for the data types resulting from the intersection
between user consented data types and the automatic subscriber config data types
for that user. Notifications are then sent to your endpoint
whenever that user generates new data for those types. This works for users who
grant consent either before or after you create the subscriber. Notifications
are not backfilled for data generated before the subscriber was created.
If a user revokes consent, notifications for the corresponding data types will stop. Automatic subscriptions are managed by Google and cannot be listed or deleted individually; they are removed only when the parent subscriber is deleted.
Manual subscriptions
If your subscriber is configured with a MANUAL subscription_create_policy
for specific data types, you must explicitly create and manage subscriptions
for each user. A Subscription links a specific user to your subscriber
endpoint for a defined set of data types. Developers can use specific APIs to:
- Create (manual) subscriptions per
healthUserId- Creates a new subscription for a specific user. This method requires the subscriber to have aSubscriptionCreatePolicyset toMANUALfor the requested data types. - Update (manual) subscription - Updates the data types for an existing user subscription.
- Delete (manual) subscription - Deletes a specific user subscription. Once deleted, your subscriber endpoint will no longer receive notifications for this user for the associated data types.
- List (manual) subscriptions - Lists all active subscriptions for a given subscriber. You can filter the results by user or data type.
Notifications
When a user's data changes for a subscribed data type, the Google Health API sends an HTTPS POST request to the subscriber endpoint URL.
Notification format
The notification payload is a JSON object containing details about the data change. This includes the user ID, data type, and time intervals, which you can use to query the updated data.
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "subscription-name",
"healthUserId": "health-user-id",
"operation": "UPSERT",
"dataType": "steps",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-03-08T01:29:00Z",
"endTime": "2026-03-08T01:34:00Z"
},
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 17,
"minutes": 29
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 17,
"minutes": 34
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-03-07T17:29:00",
"endTime": "2026-03-07T17:34:00"
}
}
]
}
}
The operation field indicates the type of change that triggered the
notification:
UPSERT: Sent for any data addition or modification.DELETE: Sent when a user deletes data.
We recommend making your notification handling logic idempotent, especially for
UPSERT operations, as retries can cause duplicate notifications to be sent.
The clientProvidedSubscriptionName field is a unique identifier. For
subscriptions with a MANUAL policy, this field contains the persistent,
developer-provided subscription name specified when the subscription is created.
This provides a stable ID for managing manual subscriptions. For subscriptions
created with an AUTOMATIC policy, the Google Health API automatically
generates and assigns a unique identifier (a random UUID) to this field for each
notification. Including clientProvidedSubscriptionName for both manual and
automatic policies ensures a consistent notification payload format across all
subscription types.
The healthUserId is a Google Health API identifier for the user whose
data has changed. If your application supports multiple users, you could receive
notifications for any user who has granted your application consent. When
you receive a notification, use healthUserId to identify which user's
data has changed, so you can use their OAuth credentials to query their data.
To map a user's OAuth credentials to their healthUserId, use the
getIdentity endpoint. Call
this endpoint with a user's credentials during user onboarding to retrieve
their healthUserId, and store this mapping. This mapping doesn't change over
time, so it can be cached indefinitely. For an example, see
Get user ID. This lets you select the correct
user credentials when querying data based on the healthUserId in a
notification.
Respond to a notification
Your server must respond to notifications with an HTTP 204 No Content status
code immediately. To avoid timeouts, process the notification payload
asynchronously after sending the response. If the Google Health API receives any
other status code or the request times out, it retries sending the notification
later.
Node.js (Express) Example:
app.post('/webhook-receiver', (req, res) => {
// 1. Immediately acknowledge the notification
res.status(204).send();
// 2. Process the data asynchronously in the background
const notification = req.body;
setImmediate(() => {
console.log(`Update for user ${notification.data.healthUserId} of type ${notification.data.dataType}`);
// Trigger your data retrieval logic here
});
});
Signature verification
To ensure the authenticity of Webhooks notifications, the raw JSON payload of
every outgoing webhook notification is signed with a private key using Tink's
PublicKeySign,
providing the Base64-encoded signature in the GOOGLE-HEALTH-API-SIGNATURE HTTP
header in the request. These signing keys are automatically rotated every 30
days, and the corresponding official public keyset is distributed as a JSON
file at the permanent URL
https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
How to Verify the Signature
Using Tink (Recommended): Developers can verify the signature using Tink's
PublicKeyVerify
primitive. Fetch the public keyset from the permanent URL, instantiate the
PublicKeyVerify primitive with the keyset, and verify the decoded
GOOGLE-HEALTH-API-SIGNATURE header against the raw webhook JSON payload.
Manual Verification (Without Tink): If developers choose not to use Tink, they can manually verify the signature by following these steps:
- Base64-decodes the
GOOGLE-HEALTH-API-SIGNATUREheader to separate the 5-byte Tink prefix (which contains a 1-byte version prefix and a 4-byte keyId) from the actual DER-encoded signature. - Fetch the JSON keyset from https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
- Locate the key matching the parsed keyId and Base64-decode its value field, which contains a serialized EcdsaPublicKey Protocol Buffer.
- Extract the big-endian x and y coordinates (Protobuf tags 3 and 4) from this binary payload.
- Instantiate a standard ECDSA P-256 public key in a built-in cryptography library using the extracted x and y coordinates.
- Verify the raw webhook JSON payload against the extracted DER signature using the SHA-256 algorithm.
Subscriber status and recovery
If your subscriber endpoint becomes unavailable or returns an error status code
(anything other than 204), the Google Health API stores pending
notifications for up to 7 days and retries delivery with exponential backoff.
Once your endpoint is back online and responds with 204, the API
automatically delivers the backlog of stored messages. Notifications older than
7 days are discarded and cannot be recovered.
Common errors
| Error code | Message | Description | Recommendation |
|---|---|---|---|
| 400 Bad Request | Invalid project number in resource name | When deleting or updating a subscriber using your Google Cloud project ID in the request URL instead of project number. This applies to webhook subscriptions using the projects.subscribers endpoint. |
Use your Google Cloud project number in the request URL, not the project ID. |
| 403 Forbidden | The caller does not have permission | When creating or listing subscribers using your Google Cloud project ID in the request URL instead of project number. This applies to webhook subscriptions using the projects.subscribers endpoint. |
Use your Google Cloud project number in the request URL, not the project ID. |