Endpoints

This page provides an overview of REST API conventions, along with an index of common Google Health API tasks and examples of each.

REST API conventions

The Google Health API follows the Google API Improvement Proposals (AIP) standards, specifically AIP-127 (HTTP and gRPC Transcoding) and AIP-131 through AIP-135 (Standard Methods). These standards define how data is mapped from a proto message to an HTTP request.

Query parameters

Query parameters are used when the data is part of the URL. This is primarily for GET requests (fetching a resource) or LIST requests (filtering/pagination), but is also used for DELETE operations.

  • Placement: Appended to the URL after a ?.
  • Syntax: Key-value pairs separated by &.
  • Mapping: Every field in the request message that is not part of the URL path template is mapped to a query parameter.
  • Best For: Simple types (strings, ints, enums) and repeated fields.

For a detailed list of filter formats, field references, validation rules, and examples, see the Filter data guide.

Example syntax:

GET https://health.googleapis.com/v4/users/me/dataTypes/data-type/dataPoints?page_size=10&filter=data_type.interval.start_time >= "2025-10-01T00:00:00Z"

Request body

The request body is used when the data modifies the state of a resource or is too large for a URL. The body is usually a JSON representation of the resource itself. Typically used for POST, PATCH, and PUT operations.

  • Placement: Inside the HTTP payload (not visible in the URL).
  • Syntax: Formatted as a JSON object.
  • Mapping: Defined in the google.api.http annotation.
    • body: "*" means the entire message is the body.
    • body: "resource_name" means only a specific field in the proto is the body.
  • Best For: Complex objects, nested messages, and sensitive data.

For detailed guidelines on rollup window sizes, bucketing rules, and aggregation examples, see the Aggregate data guide.

Example syntax:

POST https://health.googleapis.com/v4/users/me/dataTypes/data-type/dataPoints:rollUp
Content-Type: application/json

{
  "range": {
    "startTime": "2025-11-05T00:00:00Z",
    "endTime": "2025-11-13T00:00:00Z"
  },
  "windowSize": "3600s"
}

The hybrid case

In an AIP-134 compliant Update method, or a PATCH operation, both are used. The URL contains the resource name, the body contains the updated resource data, and a query parameter (usually update_mask) specifies which fields to change.

PATCH https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id
Content-Type: application/json

{
  "endpointUri": "https://myapp.com/new-webhooks/health"
}

Key differences at a glance

Feature Query Parameters Request Body
AIP Guidance Used for searching, filtering, and read operations. Used for write operations.
Visibility Visible in browser history and server logs. Hidden from the URL.
Complexity Limited to flat or repeated structures. Supports deeply nested JSON objects.
Encoding Must be URL-encoded (for example, spaces become %20). Standard JSON encoding.

Dates

All dates in the Google Health API are displayed in the format YYYY-MM-DD. The Nutrition API supports the ISO-8601 standard for date values with the following conditions:

  • A 4-digit year YYYY
  • Year values within the range of 0000-9999
  • No enforcement of start date restrictions implied by the ISO-8601 standard or other epoch

Headers

Executing the Google Health API endpoints requires using the appropriate headers and access token. The following header is recommended for both GET and POST requests:

Authorization: Bearer access-token
Accept: application/json

API task index

This section provides an index of common Google Health API tasks and examples of each.

Get the Fitbit or Google user ID

After a user consents through Google OAuth 2.0, the token response does not contain the Fitbit or Google user ID. Call the getIdentity endpoint immediately after exchanging the authorization code to retrieve both legacyUserId and healthUserId and verify that the user's Google Account is linked to Google Health. If the account is not yet linked, getIdentity returns 400 Bad Request with reason ACCOUNT_NOT_LINKED (see Handle unlinked Google Accounts).

For example:

Request

GET https://health.googleapis.com/v4/users/me/identity
Authorization: Bearer access-token
Accept: application/json

Response (200 OK)

{
  "name": "users/me/identity",
  "legacyUserId": "A1B2C3",
  "healthUserId": "111111256096816351"
}

Response (400 ACCOUNT_NOT_LINKED)

{
  "error": {
    "code": 400,
    "message": "The account is not linked to Google Health.",
    "status": "FAILED_PRECONDITION",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "ACCOUNT_NOT_LINKED",
        "domain": "health.googleapis.com",
        "metadata": {
          "redirect_uri": "https://fitbit.google.com/auth/signup"
        }
      }
    ]
  }
}

Get intraday or detailed data collected throughout a day

Use the list endpoint for a specific data type to get intraday or detailed data collected throughout the day in supported intervals for that data type.

For example:

Request

GET https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints
Authorization: Bearer access-token
Accept: application/json

Response

{
  "dataPoints": [
    {
      "dataSource": {
        "recordingMethod": "PASSIVELY_MEASURED",
        "device": {
          "manufacturer": "",
          "displayName": "Charge 6"
        },
        "platform": "FITBIT"
      },
      "steps": {
        "interval": {
          "startTime": "2026-03-04T07:05:00Z",
          "startUtcOffset": "0s",
          "endTime": "2026-03-04T07:06:00Z",
          "endUtcOffset": "0s",
          "civilStartTime": {
            "date": {
              "year": 2026,
              "month": 3,
              "day": 4
            },
            "time": {
              "hours": 7,
              "minutes": 5
            }
          },
          "civilEndTime": {
            "date": {
              "year": 2026,
              "month": 3,
              "day": 4
            },
            "time": {
              "hours": 7,
              "minutes": 6
            }
          }
        },
        "count": "40"
      }
    },
...
  ],
  "nextPageToken": "Xm5h-6L0viZxIlRuWjx5bmvy98zj85uG34tuMn16mu2pntsnZI32iqhq"
}

Get a reconciled view of interval data

To retrieve interval data without overlapping records or multi-device conflicts, call the reconcile endpoint. The reconcile endpoint automatically deduplicates overlapping intervals across sync batches and multiple recording devices, returning an authoritative, continuous stream suitable for rendering activity timelines and calculating durations.

For background on why connected devices produce overlapping intervals and an operational comparison between list and reconcile, see the Data management guide.

The following example compares the response of list (which returns both overlapping records) versus reconcile (which resolves the conflict by returning the authoritative record) for a user with two overlapping exercise sessions:

Raw list

GET https://health.googleapis.com/v4/users/me/dataTypes/exercise/dataPoints
Authorization: Bearer access-token
Accept: application/json
{
  "dataPoints": [
    {
      "name": "users/111111256096816351/dataTypes/exercise/dataPoints/7797422996486764704",
      "exercise": {
        "interval": {
          "startTime": "2026-09-03T11:20:00Z",
          "endTime": "2026-09-03T11:50:00Z"
        },
        "exerciseType": "RUNNING"
      }
    },
    {
      "name": "users/111111256096816351/dataTypes/exercise/dataPoints/4389052750481144696",
      "exercise": {
        "interval": {
          "startTime": "2026-09-03T11:00:00Z",
          "endTime": "2026-09-03T11:30:00Z"
        },
        "exerciseType": "RUNNING"
      }
    }
  ]
}

Reconciled

GET https://health.googleapis.com/v4/users/me/dataTypes/exercise/dataPoints:reconcile
Authorization: Bearer access-token
Accept: application/json
{
  "dataPoints": [
    {
      "dataPointName": "users/111111256096816351/dataTypes/exercise/dataPoints/7797422996486764704",
      "exercise": {
        "interval": {
          "startTime": "2026-09-03T11:20:00Z",
          "endTime": "2026-09-03T11:50:00Z"
        },
        "exerciseType": "RUNNING"
      }
    }
  ]
}

Reconciliation resolves conflicting sessions by deduplicating and selecting the authoritative record rather than synthesizing an artificial time union (such as 11:00:00Z to 11:50:00Z). The reconciled response returns the winning data point (7797422996486764704) with its original recorded interval (11:20:00Z to 11:50:00Z), preserving the integrity of that session's measured telemetry and metrics.

Filter data

To retrieve specific subsets of data point records matching criteria such as a time interval, date, or observation time, use the list or reconcile endpoint with a filter parameter.

For detailed guidelines, formatting rules, validation errors, and query examples, see the Filter data guide.

Filter by data source family

To isolate or aggregate data from specific types of sources (for example, physical wearable devices versus manual entries), use the dataSourceFamily parameter.

For detailed guidelines, supported families, and request and response examples for reconcile, rollUp, and dailyRollUp, see Filter by data source family in the Filter data guide.

Filter data by an interval civil start time

Use the list endpoint with a filter parameter to filter data by civil time or an interval.

For example:

Request

GET https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints?filter=steps.interval.civil_start_time >= "2026-03-04T00:00:00"
Authorization: Bearer access-token
Accept: application/json

Response

{
  "dataPoints": [
    {
      "dataSource": {
        "recordingMethod": "PASSIVELY_MEASURED",
        "device": {
          "manufacturer": "",
          "displayName": "Charge 6"
        },
        "platform": "FITBIT"
      },
      "steps": {
        "interval": {
          "startTime": "2026-03-04T07:05:00Z",
          "startUtcOffset": "0s",
          "endTime": "2026-03-04T07:06:00Z",
          "endUtcOffset": "0s",
          "civilStartTime": {
            "date": {
              "year": 2026,
              "month": 3,
              "day": 4
            },
            "time": {
              "hours": 7,
              "minutes": 5
            }
          },
          "civilEndTime": {
            "date": {
              "year": 2026,
              "month": 3,
              "day": 4
            },
            "time": {
              "hours": 7,
              "minutes": 6
            }
          }
        },
        "count": "40"
      }
...
  ],
  "nextPageToken": "Xm5h-6L0viZxIlRuQjp5bml1bZ4ve2dhNmZvMnt4Yn7qIGQhbHN3YQ"
}

Filter data by a sample observation physical time

Use the list endpoint with a filter parameter to filter data by sample observation physical time.

For example:

Request

GET https://health.googleapis.com/v4/users/me/dataTypes/body-fat/dataPoints?filter=body_fat.sample_time.physical_time >= "2026-03-01T00:00:00Z"
Authorization: Bearer access-token
Accept: application/json

Response

{
  "dataPoints": [
    {
      "name": "users/123456789/dataTypes/body-fat/dataPoints/1234567890",
      "dataSource": {
        "recordingMethod": "UNKNOWN",
        "application": {
          "packageName": "",
          "webClientId": "",
          "googleWebClientId": "google-web-client-id"
        },
        "platform": "GOOGLE_WEB_API"
      },
      "bodyFat": {
        "sampleTime": {
          "physicalTime": "2026-03-10T10:00:00Z",
          "utcOffset": "0s",
          "civilTime": {
            "date": {
              "year": 2026,
              "month": 3,
              "day": 10
            },
            "time": {
              "hours": 10
            }
          }
        },
        "percentage": 20
      }
    }
  "nextPageToken": ""
}

Filter and aggregate by data source family

A data source family is a logical grouping of data sources (such as smartwatches, mobile apps, or manual entries). It allows you isolate or aggregate data from specific types of sources (for example, physical wearable devices versus. manual entries).

The reconcile, rollUp, and dailyRollUp endpoints all support the dataSourceFamily parameter. The passing mechanism depends on the endpoint:

Endpoint (HTTP method) Mechanism
reconcile (GET) Pass dataSourceFamily as a URL query parameter.
rollUp (POST) Pass dataSourceFamily as a field in the JSON request body.
dailyRollUp (POST) Pass dataSourceFamily as a field in the JSON request body.

Supported Data Source Families

The following table describes the supported dataSourceFamily values:

Option Description
users/me/dataSourceFamilies/all-sources Default value. Returns data points reconciled across all registered first-party (1P) and third-party (3P) data sources. Third-party app data will be returned with this option (such as smartwatch steps + third-party app steps + mobile phone steps + manual steps).
users/me/dataSourceFamilies/google-wearables Includes data recorded by Google and Fitbit tracker devices (such as Fitbit wearable trackers and Pixel Watch). Excludes manually logged data and phone-estimated data. Use this option when your integration requires raw sensor telemetry recorded directly by wearable hardware.
users/me/dataSourceFamilies/google-sources Includes first-party Google and Fitbit sources. This includes physical tracker device records, data from Health Connect, and any manual entries logged through first-party apps (such as the Fitbit app or Google Fit).

To get a reconciled data stream from a specific data source family, call the reconcile endpoint with the dataSourceFamily query parameter.

For example, the following GET request fetches tracker-recorded sleep for the day after 2026-03-03:

Request

GET https://health.googleapis.com/v4/users/me/dataTypes/sleep/dataPoints:reconcile?dataSourceFamily=users/me/dataSourceFamilies/google-wearables&filter=sleep.interval.civil_end_time >= "2026-03-03"
Authorization: Bearer access-token
Accept: application/json

Response

{
  "dataPoints": [
    {
      "name": "users/2515055256096816351/dataTypes/sleep/dataPoints/2724123844716220216",
      "dataSource": {
        "recordingMethod": "DERIVED",
        "device": {
          "displayName": "Charge 6"
        },
        "platform": "FITBIT"
      },
      "sleep": {
        "interval": {
          "startTime": "2026-03-03T20:57:30Z",
          "startUtcOffset": "0s",
          "endTime": "2026-03-04T04:41:30Z",
          "endUtcOffset": "0s"
        },
        "type": "STAGES",
        "stages": [
          {
            "startTime": "2026-03-03T20:57:30Z",
            "startUtcOffset": "0s",
            "endTime": "2026-03-03T20:59:30Z",
            "endUtcOffset": "0s",
            "type": "AWAKE",
            "createTime": "2026-03-04T04:43:40.937183Z",
            "updateTime": "2026-03-04T04:43:40.937183Z"
          },
          {
            "startTime": "2026-03-04T04:07:30Z",
            "startUtcOffset": "0s",
            "endTime": "2026-03-04T04:41:30Z",
            "endUtcOffset": "0s",
            "type": "AWAKE",
            "createTime": "2026-03-04T04:43:40.937183Z",
            "updateTime": "2026-03-04T04:43:40.937183Z"
          }
        ],
        "metadata": {
          "stagesStatus": "SUCCEEDED",
          "processed": true,
          "main": true
        },
        "summary": {
          "minutesInSleepPeriod": "464",
          "minutesAfterWakeUp": "0",
          "minutesToFallAsleep": "0",
          "minutesAsleep": "407",
          "minutesAwake": "57",
          "stagesSummary": [
            {
              "type": "AWAKE",
              "minutes": "56",
              "count": "12"
            },
            {
              "type": "LIGHT",
              "minutes": "198",
              "count": "19"
            },
            {
              "type": "DEEP",
              "minutes": "114",
              "count": "10"
            },
            {
              "type": "REM",
              "minutes": "94",
              "count": "4"
            }
          ]
        },
        "createTime": "2026-03-04T04:43:40.337983Z",
        "updateTime": "2026-03-04T04:43:40.937183Z"
      }
    }
  ],
  "nextPageToken": ""
}

To aggregate data points over a specific window size restricted to a particular data source family, call the rollUp endpoint and pass the dataSourceFamily field in the JSON request body.

The following POST request queries intraday walking step counts in hourly intervals (3600s), aggregated exclusively from wearable devices:

Request

POST https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints:rollUp
Authorization: Bearer access-token
Accept: application/json

{
  "range": {
    "startTime": "2026-07-29T00:00:00Z",
    "endTime": "2026-07-29T23:59:59Z"
  },
  "windowSize": "3600s",
  "dataSourceFamily": "users/me/dataSourceFamilies/google-wearables"
}

Response

{
  "rollupDataPoints": [
    {
      "startTime": "2026-07-29T08:00:00Z",
      "endTime": "2026-07-29T09:00:00Z",
      "steps": {
        "countSum": "1200"
      }
    },
    {
      "startTime": "2026-07-29T09:00:00Z",
      "endTime": "2026-07-29T10:00:00Z",
      "steps": {
        "countSum": "3450"
      }
    }
  ]
}

To aggregate daily data points for a specific source family, call the dailyRollUp endpoint and pass the dataSourceFamily field in the request body.

For example, the following request calculates the daily rollups for the user's steps, including all first-party Google and Fitbit sources (wearables + manual entries):

Request

POST https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints:dailyRollUp
Authorization: Bearer access-token
Accept: application/json

{
  "range": {
    "start": {
      "date": {
        "year": 2026,
        "month": 7,
        "day": 28
      },
      "time": {
        "hours": 0,
        "minutes": 0,
        "seconds": 0,
        "nanos": 0
      }
    },
    "end": {
      "date": {
        "year": 2026,
        "month": 7,
        "day": 30
      },
      "time": {
        "hours": 0,
        "minutes": 0,
        "seconds": 0,
        "nanos": 0
      }
    }
  },
  "windowSizeDays": 1,
  "dataSourceFamily": "users/me/dataSourceFamilies/google-sources"
}

Response

{
  "rollupDataPoints": [
    {
      "civilStartTime": {
        "date": {
          "year": 2026,
          "month": 7,
          "day": 28
        },
        "time": {}
      },
      "civilEndTime": {
        "date": {
          "year": 2026,
          "month": 7,
          "day": 28
        },
        "time": {
          "hours": 23,
          "minutes": 59,
          "seconds": 59
        }
      },
      "steps": {
        "countSum": "8430"
      }
    },
    {
      "civilStartTime": {
        "date": {
          "year": 2026,
          "month": 7,
          "day": 29
        },
        "time": {}
      },
      "civilEndTime": {
        "date": {
          "year": 2026,
          "month": 7,
          "day": 29
        },
        "time": {
          "hours": 23,
          "minutes": 59,
          "seconds": 59
        }
      },
      "steps": {
        "countSum": "11245"
      }
    }
  ]
}

Aggregate data points over a range of time

Use the rollUp endpoint to return the aggregate of data points based on a window in seconds (windowSize), over a datetime range in the user's physical time (in UTC).

When calling the rollUp endpoint, provide the request body representing the required closed-open time range and windowSize (at least "1s"). For detailed guidelines on windowSize requirements, storage resolution alignment, bucketing rules, and response ordering, see Aggregate over physical time intervals in the Aggregate data guide.

For example, to roll up step counts in 1-minute intervals (60s):

Request

POST https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints:rollUp
Authorization: Bearer access-token
Accept: application/json

{
  "range": {
    "startTime": "2026-02-17T17:00:00Z",
    "endTime": "2026-02-17T17:59:59Z"
  },
  "windowSize": "60s"
}

Response

{
  "rollupDataPoints": [
    {
      "startTime": "2026-02-17T17:55:00Z",
      "endTime": "2026-02-17T17:56:00Z",
      "steps": {
        "countSum": "72"
      }
    },
    {
      "startTime": "2026-02-17T17:54:00Z",
      "endTime": "2026-02-17T17:55:00Z",
      "steps": {
        "countSum": "85"
      }
    },
...
  ]
}

Aggregate data across a single day or multiple days

Use the dailyRollUp endpoint to aggregate data across a single day or multiple days (windowSizeDays). Provide the closed-open civil time range for the required interval in the request body. Depending on the data type, the endpoint returns either the sum or the average over the interval.

For details on daily aggregation behavior, civil time handling across time zones, and bucketing rules, see Aggregate across calendar days in the Aggregate data guide.

For example:

Request

POST https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints:dailyRollUp
Authorization: Bearer access-token
Accept: application/json

{
  "range": {
    "start": {
      "date": {
        "year": 2026,
        "month": 2,
        "day": 26
      },
      "time": {
        "hours": 0,
        "minutes": 0,
        "seconds": 0,
        "nanos": 0
      }
    },
    "end": {
      "date": {
        "year": 2026,
        "month": 2,
        "day": 26
      },
      "time": {
        "hours": 23,
        "minutes": 59,
        "seconds": 59,
        "nanos": 0
      }
    }
  },
  "windowSizeDays": 1
}

Response

{
  "rollupDataPoints": [
    {
      "civilStartTime": {
        "date": {
          "year": 2026,
          "month": 2,
          "day": 26
        },
        "time": {}
      },
      "civilEndTime": {
        "date": {
          "year": 2026,
          "month": 2,
          "day": 26
        },
        "time": {
          "hours": 23,
          "minutes": 59,
          "seconds": 59
        }
      },
      "steps": {
        "countSum": "3822"
      }
    }
  ]
}

Bucketing when range is not a multiple of window size

If the requested range is not an exact multiple of windowSize (or windowSizeDays), the API uses ceiling division to compute the number of windows and clamps the final chronological bucket at the upper endpoint of the range. Because rollup responses are returned in reverse-chronological order, this truncated bucket appears as the first element (index 0) in the returned list.

For the bucketing formula, non-divisible range truncation examples, and guidance on normalizing additive metrics, see Bucketing when range is not a multiple of window size in the Aggregate data guide.

Rollup window and storage resolution

When aggregating interval data types, the rollUp endpoint places each recorded data point into the bucket containing the data point's startTime without slicing or interpolating across sub-interval buckets. To obtain evenly distributed aggregates, set windowSize to a duration equal to or greater than the underlying storage resolution of the target data type (such as "60s" for 1-minute step intervals).

For underlying storage resolutions and sub-interval behavior examples, see Rollup window and storage resolution in the Aggregate data guide.

Update a user's health data

Use the patch endpoint to update a user's health data.

The patch endpoint updates an existing record based on the identifier specified in the request URL. Provide the identifier of a previously inserted data point. The API overwrites the existing record.

A data point's interval timestamps (startTime and endTime) can also be updated by the record's owner or propagated from upstream platforms like Health Connect. For details on timestamp mutability, see the Data management guide. For an example of updating interval timestamps, see Update interval timestamps for existing data.

When to use the data point identifier

The data point identifier is essential in the following scenarios:

  • Targeted updates: To update a specific measurement, provide its identifier in the patch request.
  • Deletions: Retaining the identifier allows your application to delete the record later using the batchDelete endpoint.

Here's an example where a user updates their body fat reading on a scale called "HumanScale" from the company "Scales R Us". The user's new body fat reading is 20% for the date of 2026-03-10:

Request

PATCH https://health.googleapis.com/v4/users/me/dataTypes/body-fat/dataPoints/1234567890
Authorization: Bearer access-token
Content-Type: application/json

{
  "name": "users/me/dataTypes/body-fat/dataPoints/1234567890",
  "dataSource": {
    "recordingMethod": "ACTIVELY_MEASURED",
    "device": {
      "formFactor": "SCALE",
      "manufacturer": "Scales R Us",
      "displayName": "HumanScale"
    }
  },
  "bodyFat": {
    "sampleTime": {
      "physicalTime": "2026-03-10T10:00:00Z"
    },
    "percentage": 20
  }
}

Response

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.devicesandservices.health.v4main.DataPoint",
    "name": "users/123456789/dataTypes/body-fat/dataPoints/1234567890",
    "dataSource": {
      "recordingMethod": "ACTIVELY_MEASURED",
      "device": {
        "formFactor": "SCALE",
        "manufacturer": "Scales R Us",
        "displayName": "HumanScale"
      },
      "application": {
        "googleWebClientId": "618308034039.apps.googleusercontent.com"
      },
      "platform": "GOOGLE_WEB_API"
    },
    "bodyFat": {
      "sampleTime": {
        "physicalTime": "2026-03-10T10:00:00Z"
      },
      "percentage": 20
    }
  }
}

Update interval timestamps for existing data

To update the interval timestamps (startTime and endTime in REST JSON payloads, or start_time and end_time in gRPC) of an existing data point, send a PATCH request to the data point's resource URI. Only the original creator or owner of a record can modify its fields. Applications cannot edit data points they did not create.

For background on timestamp mutability, upstream updates from Health Connect, and caching implications, see the Data management guide.

The following example demonstrates an owner application updating the interval timestamps of an existing hydration log using the patch endpoint:

Request

PATCH https://health.googleapis.com/v4/users/me/dataTypes/hydration-log/dataPoints/4093039283164890826
Authorization: Bearer access-token
Content-Type: application/json

{
  "hydrationLog": {
    "interval": {
      "startTime": "2026-09-03T10:05:00Z",
      "endTime": "2026-09-03T10:19:59Z"
    },
    "amountConsumed": {
      "milliliters": 350
    }
  }
}

Response

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.devicesandservices.health.v4.DataPoint",
    "name": "users/111111256096816351/dataTypes/hydration-log/dataPoints/4093039283164890826",
    "hydrationLog": {
      "interval": {
        "startTime": "2026-09-03T10:05:00Z",
        "endTime": "2026-09-03T10:19:59Z",
        "civilStartTime": {
          "date": {
            "year": 2026,
            "month": 9,
            "day": 3
          },
          "time": {
            "hours": 10,
            "minutes": 5
          }
        },
        "civilEndTime": {
          "date": {
            "year": 2026,
            "month": 9,
            "day": 3
          },
          "time": {
            "hours": 10,
            "minutes": 19,
            "seconds": 59
          }
        }
      },
      "amountConsumed": {
        "milliliters": 350
      }
    }
  }
}

Log a food item

To log a food item, send a POST request to the nutrition-log dataPoints endpoint. The request body contains a DataPoint with a nutritionLog object. For more information, see the Nutrition guide.

For example:

Request

POST https://health.googleapis.com/v4/users/me/dataTypes/nutrition-log/dataPoints
Authorization: Bearer access-token
Content-Type: application/json

{
  "nutritionLog": {
    "interval": {
      "startTime": "2026-06-16T12:00:00Z",
      "endTime": "2026-06-16T12:30:00Z"
    },
    "foodDisplayName": "Banana",
    "mealType": "LUNCH",
    "energy": {
      "kcal": 105
    },
    "totalCarbohydrate": {
      "grams": 27
    },
    "totalFat": {
      "grams": 0.3
    }
  }
}

Response

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.devicesandservices.health.v4.DataPoint",
    "name": "users/123456789/dataTypes/nutrition-log/dataPoints/567890",
    "dataSource": {
      "recordingMethod": "ACTIVELY_MEASURED",
      "platform": "GOOGLE_WEB_API"
    },
    "nutritionLog": {
      "interval": {
        "startTime": "2026-06-16T12:00:00Z",
        "startUtcOffset": "0s",
        "endTime": "2026-06-16T12:30:00Z",
        "endUtcOffset": "0s"
      },
      "energy": {
        "kcal": 105
      },
      "totalCarbohydrate": {
        "grams": 27
      },
      "totalFat": {
        "grams": 0.3
      },
      "mealType": "LUNCH",
      "foodDisplayName": "Banana"
    }
  }
}

Delete user health data

Use the batchDelete method to delete an array of a user's Fitbit app data.

Here's an example where a user previously recorded their body fat on a scale, but they want to delete the record. Using the user-id and data-point-id from the original insert action:

Request

POST https://health.googleapis.com/v4/users/me/dataTypes/body-fat/dataPoints:batchDelete
Authorization: Bearer access-token
Accept: application/json
content-length: 93

{
  "names": [
    "users/123456789/dataTypes/body-fat/dataPoints/1234567890"
  ]
}

Response

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.devicesandservices.health.v4main.BatchDeleteDataPointsResponse"
  }
}

Find device information

Use the list endpoint to retrieve the list of devices paired to a user's account. This includes the device's model information (deviceVersion) and the last time it synchronized with the Google Health mobile app (lastSyncTime).

The list configuration and sync information is useful for troubleshooting syncing issues or fetching historical data since the last sync time.

For example:

Request

GET https://health.googleapis.com/v4/users/me/pairedDevices
Authorization: Bearer access-token
Accept: application/json

Response

{
  "pairedDevices": [
    {
      "name": "users/me/pairedDevices/123456",
      "deviceType": "TRACKER",
      "batteryStatus": "High",
      "batteryLevel": 88,
      "lastSyncTime": "2026-03-04T07:05:00Z",
      "deviceVersion": "Charge 6",
      "macAddress": "00:11:22:33:44:55",
      "features": [
        "STEPS",
        "HEART_RATE"
      ]
    }
  ]
}

Query historical data

One of the core benefits of the Google Health API is the ability to track a user's performance and monitor their health vitals over long periods of time. You can query a user's data as far back as it has been recorded; the API imposes no limitations or restrictions on the amount of historical data your application can consume.

However, querying historical data is still governed by standard rate limits. To manage system stability and prevent excessive payloads, the Google Health API uses automatic pagination with endpoint-specific page sizes. Note the following boundaries and behavior:

  • Automatic pagination: If you query a long span of data, the API will only return the first page of results up to the page size cap for that endpoint, along with a nextPageToken. You must use the nextPageToken to request subsequent pages.
  • Variable page sizes: Capping limits depend on the endpoint and data type. For most data types, page sizes are capped at a maximum of 10,000. However, for certain data types like exercise and sleep, the default and maximum page size is capped at 25. For example, if a client requests all sleep data for the past 10 years, the API will still return only 25 sleep sessions on the first page.
  • Rollup date range restrictions: For data rollup and aggregation endpoints (such as rollUp and dailyRollUp), query date ranges are restricted based on the data type:
    • A maximum range of 14 days for calories-in-heart-rate-zone, heart-rate, active-minutes, and total-calories.
    • A maximum range of 90 days for all other rollup data types.

Depending on the volume of historical data your application needs, retrieving the entire dataset will require paginating through the pages sequentially. Keep this in mind when designing your application's data synchronization process.

To ensure optimal performance and avoid API errors, follow these guidelines when querying historical data:

Phased data sync (hot versus cold load)

  • Initial "hot" load: Fetch and render only the most recent 7–14 days of data during the primary load sequence. This ensures that users see data immediately without waiting for long-running queries.
  • Background "cold" load: Delegate older historical data retrieval to an asynchronous, lower-priority queue or background process after the primary UI is rendered.

Query chunking for aggregation

  • Because rollup and daily rollup endpoints enforce a maximum date range limit (14 or 90 days depending on the data type), you must break down large historical aggregation queries into smaller, sequential intervals within these limits.
  • Batch or sequence these sub-queries safely to respect concurrency limits and maintain steady UI progress indicators.

Leverage pre-aggregated roll-ups

Restructure overview dashboards and trend charts to use pre-aggregated, summary endpoints (such as DailyRollUpDataPoints). This will drastically reduce compute overhead on the backend and network transfer time to the client.

Resilient error handling (smart retries)

  • Implement strict exponential backoff handling when encountering rate limits (429 Too Many Requests) and server gateway timeouts (504 Gateway Timeout). Never retry large, failed payloads immediately. Instant retries multiply backend congestion and compound system degradation.