The Google Health API tracks user workout sessions and exercise history using the
`exercise` session data type. A session acts as a container that bundles
activity metadata, pause and resume events, laps or splits, and summary
metrics.

Understand how to read, write, and structure workouts in your application to
deliver the best experience for your users.

## Supported data types

The API supports the following data type for tracking workouts and activity
sessions:

<br />

| Data type | Available operations | Scope |
|---|---|---|
| **[Exercise](https://developers.google.com/health/reference/rest/v4/users.dataTypes.dataPoints.md.txt#exercise)** **dataType:** `exercise` **filter parameter:** `exercise` **Record type:**Session [Webhook support](https://developers.google.com/health/webhooks.md.txt) [Workouts guide](https://developers.google.com/health/data-types/workouts.md.txt) #### Compatible devices - [Fitbit Air](https://store.google.com/product/google_fitbit_air) - Fitbit Alta - Fitbit Alta HR - Fitbit Blaze - Fitbit Charge 2 - Fitbit Charge 3 - Fitbit Charge 4 - Fitbit Charge 5 - [Fitbit Charge 6](https://store.google.com/product/fitbit_charge_6) - [Fitbit Edge](https://store.google.com/product/google_fitbit_edge) - Fitbit Flex 2 - Fitbit Inspire - Fitbit Inspire 2 - [Fitbit Inspire 3](https://store.google.com/product/fitbit_inspire_3) - Fitbit Inspire HR - Fitbit Ionic - Fitbit Luxe - Fitbit MobileTrack - Pixel Watch - Pixel Watch 2 - [Pixel Watch 3](https://store.google.com/product/pixel_watch_3) - [Pixel Watch 4](https://store.google.com/product/pixel_watch_4) - Fitbit Sense - [Fitbit Sense 2](https://store.google.com/product/fitbit_sense_2) - Fitbit Versa - Fitbit Versa 2 - Fitbit Versa 3 - [Fitbit Versa 4](https://store.google.com/product/fitbit_versa_4) - Fitbit Versa Lite | list, get, reconcile, create, update, batchDelete | `.activity_and_fitness.readonly` `.activity_and_fitness.writeonly` |
[*Table: Google Health API Workouts data types*]

<br />

### Related telemetry data types

While workout sessions use the `exercise` data type as a container, wearable
devices also record detailed, high-frequency telemetry during a session. In the
Google Health API, these granular intraday telemetry data types are **read-only**
and cannot be created or modified directly through their own REST endpoints.
When writing a workout session through the REST API, pass aggregate workout
metrics inside the `metricsSummary` object of the `exercise` data point.

The following table maps the summary fields inside `exercise.metricsSummary` to
the corresponding read-only intraday telemetry data types that you can query for
that workout's time window:

| Summary field (`metricsSummary`) | Intraday telemetry data type name | API telemetry data type ID (read-only) | Filter field (`?filter=`) | Required read OAuth scope |
|---|---|---|---|---|
| `caloriesKcal` | Active Energy Burned | `active-energy-burned` | `active_energy_burned.interval.start_time` | `activity_and_fitness.readonly` |
| `distanceMillimeters` | Distance | `distance` | `distance.interval.start_time` | `activity_and_fitness.readonly` |
| `steps` | Steps | `steps` | `steps.interval.start_time` | `activity_and_fitness.readonly` |
| `averageHeartRateBeatsPerMinute` | Heart Rate | `heart-rate` | `heart_rate.sample_time.physical_time` | `health_metrics_and_measurements.readonly` |
| `activeZoneMinutes` | Active Zone Minutes | `active-zone-minutes` | `active_zone_minutes.interval.start_time` | `activity_and_fitness.readonly` |

The following sections provide technical details for the `exercise` data type,
including REST representation examples, GPS route handling, and integration
guidelines.

## Workout sessions

Write daily activities or workouts as `exercise` session data points. Each
data point describes the overall session, details event intervals (such as
pause and resume actions), and supplies summary metrics (like overall distance,
steps, and average heart rate).

### Session attributes

When structuring an exercise data point, verify the following core components:

- **Session Time (`interval`)**: The start time and end time of the overall workout session, along with the timezone offsets active at those points.
- **Activity Type (`exerciseType`)** : The category of activity performed (such as `RUNNING`, `WALKING`, `BIKING`, or `AEROBIC_WORKOUT`). Specify the exact type of physical training.
- **Display Name (`displayName`)** : A user-friendly name for the workout session. When created through the REST API, the server populates the localized display name for the `exerciseType`, such as `"Run"` for `RUNNING`.
- **Active Duration (`activeDuration`)** : The true active time of the workout, excluding paused intervals. Standard formatting uses `Duration` format (for example, `"1800s"`). On the server, `activeDuration` is calculated from the session `interval` minus any `PAUSE`/`RESUME` or `AUTO_PAUSE`/`AUTO_RESUME` intervals in `exerciseEvents`.

### Summary metrics

The `metricsSummary` nested object contains total and average metrics
calculated across the entire duration of the exercise session:

- `caloriesKcal`: Total active calories burned during the workout, measured in kilocalories (`kcal`).
- `distanceMillimeters`: Total distance covered, measured in millimeters to maintain high precision across units.
- `steps`: Total steps taken during the exercise.
- `averageHeartRateBeatsPerMinute`: The user's average heart rate during active minutes of the session.
- `activeZoneMinutes`: Cumulative active zone minutes earned during the workout.
- `averageSpeedMillimetersPerSecond`: Average movement speed in millimeters per second.
- `averagePaceSecondsPerMeter`: Average pace during active minutes of the session, measured in seconds per meter.
- `elevationGainMillimeters`: Total elevation gain during the session.

### Laps and splits

For workouts that involve laps (such as track runs or pool swimming) synced from
supported devices, `splitSummaries` segments the session by lap or split.

Each split contains:

- A specific `startTime` and `endTime`.
- An `activeDuration` representing true lap time.
- A `metricsSummary` scoped only to that segment.
- A `splitType` to define the split boundaries (such as `DISTANCE`, `DURATION`, or `MANUAL`).

### Exercise events

To accurately compute active duration, track state transitions (like manual or
automatic pause events) using `exerciseEvents`.

Each event contains the timestamp (`eventTime`) and type:

- `START` / `STOP`: Indicates boundary timestamps of when the user explicitly started or stopped the record.
- `PAUSE` / `RESUME`: Indicates when the session was manually paused or resumed.
- `AUTO_PAUSE` / `AUTO_RESUME`: Indicates sensor-driven auto-pauses/resumes.

## Write a workout session

To create, update, or import a workout session, write a data point to the
`exercise` data type collection. Use the `create` `dataPoints`
endpoint.

### REST representation example

The following example shows how to write a workout session using a `POST`
method:

### Request

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

{
  "dataSource": {
    "recordingMethod": "ACTIVELY_MEASURED",
    "platform": "GOOGLE_WEB_API"
  },
  "exercise": {
    "interval": {
      "startTime": "2026-04-20T08:00:00Z",
      "startUtcOffset": "0s",
      "endTime": "2026-04-20T08:35:00Z",
      "endUtcOffset": "0s"
    },
    "exerciseType": "RUNNING",
    "activeDuration": "1800s",
    "metricsSummary": {
      "caloriesKcal": 380.0,
      "distanceMillimeters": 5000000.0,
      "steps": "6200",
      "averageHeartRateBeatsPerMinute": "148",
      "averagePaceSecondsPerMeter": 0.36
    },
    "exerciseEvents": [
      {
        "eventTime": "2026-04-20T08:15:00Z",
        "eventUtcOffset": "0s",
        "exerciseEventType": "PAUSE"
      },
      {
        "eventTime": "2026-04-20T08:20:00Z",
        "eventUtcOffset": "0s",
        "exerciseEventType": "RESUME"
      }
    ]
  }
}
```

### Response

```
{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.devicesandservices.health.v4.DataPoint",
    "name": "users/999298779973222220/dataTypes/exercise/dataPoints/3054085373369111216",
    "dataSource": {
      "recordingMethod": "ACTIVELY_MEASURED",
      "application": {
        "googleWebClientId": "438076457939-example.apps.googleusercontent.com"
      },
      "platform": "GOOGLE_WEB_API"
    },
    "exercise": {
      "interval": {
        "startTime": "2026-04-20T08:00:00Z",
        "startUtcOffset": "0s",
        "endTime": "2026-04-20T08:35:00Z",
        "endUtcOffset": "0s"
      },
      "exerciseType": "RUNNING",
      "metricsSummary": {
        "caloriesKcal": 380,
        "distanceMillimeters": 5000000,
        "steps": "6200",
        "averagePaceSecondsPerMeter": 0.36,
        "averageHeartRateBeatsPerMinute": "148"
      },
      "exerciseMetadata": {},
      "displayName": "Run",
      "activeDuration": "1800s",
      "exerciseEvents": [
        {
          "eventTime": "2026-04-20T08:15:00Z",
          "eventUtcOffset": "0s",
          "exerciseEventType": "PAUSE"
        },
        {
          "eventTime": "2026-04-20T08:20:00Z",
          "eventUtcOffset": "0s",
          "exerciseEventType": "RESUME"
        }
      ],
      "updateTime": "2026-04-20T08:35:10.123456Z",
      "createTime": "2026-04-20T08:35:10.123456Z"
    }
  }
}
```

## GPS routes and location tracking

The API saves basic session summaries directly within the `exercise` data
point, but handles detailed location history and GPS route coordinates as a
separate stream.

To download the detailed route data for an outdoor session, call the
`exportExerciseTcx` custom method. This endpoint returns the route in the
industry-standard **Training Center XML (TCX)** format.

### Export GPS route

### Request

```
GET https://health.googleapis.com/v4/users/me/dataTypes/exercise/dataPoints/exercise-data-point-id:exportExerciseTcx?alt=media
Authorization: Bearer access-token
```

### Response

An HTTP payload with `Content-Type: application/tcx+xml` and
headers instructing the browser to save the file.

```
<?xml version="1.0" encoding="UTF-8"?>
<TrainingCenterDatabase xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">
  <Activities>
    <Activity Sport="Running">
      <Id>2026-04-20T08:00:00Z</Id>
      <Lap StartTime="2026-04-20T08:00:00Z">
        <TotalTimeSeconds>1800</TotalTimeSeconds>
        <DistanceMeters>5000</DistanceMeters>
        <Calories>380</Calories>
        <Intensity>Active</Intensity>
        <TriggerMethod>Manual</TriggerMethod>
        <Track>
          <Trackpoint>
            <Time>2026-04-20T08:00:00Z</Time>
            <Position>
              <LatitudeDegrees>37.7749</LatitudeDegrees>
              <LongitudeDegrees>-122.4194</LongitudeDegrees>
            </Position>
            <AltitudeMeters>15.0</AltitudeMeters>
            <DistanceMeters>0.0</DistanceMeters>
          </Trackpoint>
        </Track>
      </Lap>
    </Activity>
  </Activities>
</TrainingCenterDatabase>
```
**Important:** You must append \`?alt=media\` to the request URL to download the raw TCX file. Otherwise, the server responds with a standard JSON envelope.

### Required scopes and location

<br />

To use the **GPS routes and location tracking** feature, your app must request the following OAuth scopes:

- **Read** : `https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonly`
- **Write** : `https://www.googleapis.com/auth/googlehealth.activity_and_fitness.writeonly`
- **Read** : `https://www.googleapis.com/auth/googlehealth.location.readonly`

<br />

## Guidelines

When integrating workout tracking into your app, follow these design and
implementation guidelines.

### Active versus total duration

To calculate speed or pace metrics, always use `activeDuration` rather than the
difference between `startTime` and `endTime`. This prevents paused intervals
from skewing your metrics.

For example, if a user starts a workout at 08:00 and finishes at 08:35, the
workout has a total elapsed duration of 2,100 seconds. If the user paused the
workout for 5 minutes (300 seconds) and you include `PAUSE` and `RESUME` events
in `exerciseEvents`, the active duration is `"1800s"` (2,100 - 300). The API
uses active duration to calculate averages, dividing total distance by 1,800
seconds instead of 2,100.

### Speed and pace calculation

The Google Health API uses standard formulas to calculate speed and pace:

- Speed = `distance / time(hour)`
- Pace = `time(seconds) / distance`

The `Accept-Language` header specified in the request determines the unit of
distance.

### Request location early

If your app maps workout routes, request location permissions and the Google
Health `location` scope in addition to the activity and fitness scope. Explain
to users why your app requires the location scope when reviewing GPS exercises.

When your app requests the location scope
(`https://www.googleapis.com/auth/googlehealth.location.readonly`), Google
OAuth displays a consent prompt to the user. Explain to your users that this
permission is necessary to render route overlays and export GPS track files
(TCX). If a user grants the activity scope but denies location permission,
`exportExerciseTcx` returns an authorization error, though you can still
access session aggregates in `metricsSummary`.

### Sync using webhooks

Subscribe to the `exercise` data type to notify your backend using webhooks
when new workout data becomes available. This lets you trigger post-workout
experiences automatically.

When your server receives a webhook notification, it contains the
`healthUserId` and the civil time interval (`civilIso8601TimeInterval`) of the
updated workout. Your server should acknowledge the notification immediately
with `204 No Content`, process the payload asynchronously, and query the
reconciled `exercise` endpoint using an `exercise.interval.civil_start_time`
filter (note that `exercise` only supports filtering by `civil_start_time`):

```
GET https://health.googleapis.com/v4/users/healthUserId/dataTypes/exercise/dataPoints:reconcile?filter=exercise.interval.civil_start_time >= "2026-04-20T08:00:00" AND exercise.interval.civil_start_time < "2026-04-20T10:00:00"
```

For details on how to set up subscriptions, see
[Webhook subscriptions](https://developers.google.com/health/webhooks.md.txt).

### Maintain consistent metrics

To provide a complete workout experience, include summary metrics when writing
`exercise` sessions and query the read-only telemetry endpoints using time-range
filters when rendering detail charts.

#### Write workout summary metrics (write path)

When importing or writing a completed workout to the Google Health API, include the
workout's aggregate telemetry metrics (`steps`, `caloriesKcal`,
`distanceMillimeters`, `averageHeartRateBeatsPerMinute`, and
`averagePaceSecondsPerMeter`) inside the `metricsSummary` object of the
`exercise` data point:

- `POST https://health.googleapis.com/v4/users/me/dataTypes/exercise/dataPoints`

> [!NOTE]
> **Note:** Intraday telemetry data types (\`steps\`, \`active-energy-burned\`, \`distance\`, \`heart-rate\`, and \`active-zone-minutes\`) are read-only and cannot be created or modified directly through their own REST endpoints. Pass workout summary metrics through the \`metricsSummary\` field on the \`exercise\` data point instead.

#### Query detailed data for charts (read path)

To build workout dashboards or charts for a specific session, query the
reconciled telemetry endpoints for that workout's time window:

1. Fetch the workout session from the `exercise` endpoint using a
   `civil_start_time` filter to get its `interval` and `metricsSummary`:

   ```
   GET https://health.googleapis.com/v4/users/me/dataTypes/exercise/dataPoints:reconcile?filter=exercise.interval.civil_start_time >= "2026-04-20T00:00:00" AND exercise.interval.civil_start_time < "2026-04-21T00:00:00"
   ```
2. Use the session's `interval.startTime` and `interval.endTime` to query the
   telemetry endpoints for that physical time window.

3. If `exerciseMetadata.hasGps` is `true`, call `exportExerciseTcx` to
   download the route coordinates.