The Google Fit APIs, including the Google Fit REST API, will be deprecated in 2026. As of May 1, 2024, developers cannot sign up to use these APIs.

For instructions on which API or platform to migrate to, visit the Health Connect migration guide. For a comparison of Health Connect with the Google Fit APIs and the Fitbit Web APIs, visit the Health Connect comparison guide.

Learn more about Health Connect and how to integrate with the API.

Work with historical data

  • The History API facilitates bulk operations like reading, inserting, updating, and deleting historical health and wellness data in the fitness store.

  • You can use the History API to read data from other apps, import batch data, update existing data, and delete data your app previously stored.

  • To read historical data, create a DataReadRequest and use the HistoryClient.readData() method.

  • Data can be inserted by creating a DataSet and using the HistoryClient.insertData method, with a warning to limit each request to 1,000 datapoints.

  • Data can be updated using the HistoryClient.updateData method, which replaces existing data points that overlap with the new data.

  • Historical data inserted by your app can be deleted using the HistoryClient.deleteData method.

  • Apps can register to receive updates when certain data types, like weight, are inserted into the Google Fit database using HistoryClient.registerDataUpdateListener.

The History API lets your app perform bulk operations on the fitness store: reading, inserting, updating, and deleting historical health and wellness data. Use the History API to do the following:

  • Read health and wellness data that was inserted or recorded using other apps.
  • Import batch data into Google Fit.
  • Update data in Google Fit.
  • Delete historical data that your app previously stored.

To insert data that contains session metadata, use the Sessions API.

Read data

The following sections cover how to read different kinds of aggregate data.

Read detailed and aggregate data

To read historical data, create a DataReadRequest instance.

Kotlin

// Read the data that's been collected throughout the past week.
valendTime=LocalDateTime.now().atZone(ZoneId.systemDefault())
valstartTime=endTime.minusWeeks(1)
Log.i(TAG,"Range Start: $startTime")
Log.i(TAG,"Range End: $endTime")
valreadRequest=
DataReadRequest.Builder()
// The data request can specify multiple data types to return,
// effectively combining multiple data queries into one call.
// This example demonstrates aggregating only one data type.
.aggregate(DataType.AGGREGATE_STEP_COUNT_DELTA)
// Analogous to a "Group By" in SQL, defines how data should be
// aggregated.
// bucketByTime allows for a time span, whereas bucketBySession allows
// bucketing by <a href="/fit/android/using-sessions">sessions</a>.
.bucketByTime(1,TimeUnit.DAYS)
.setTimeRange(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.build()

Java

// Read the data that's been collected throughout the past week.
ZonedDateTimeendTime=LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTimestartTime=endTime.minusWeeks(1);
Log.i(TAG,"Range Start: $startTime");
Log.i(TAG,"Range End: $endTime");
DataReadRequestreadRequest=newDataReadRequest.Builder()
// The data request can specify multiple data types to return,
// effectively combining multiple data queries into one call.
// This example demonstrates aggregating only one data type.
.aggregate(DataType.AGGREGATE_STEP_COUNT_DELTA)
// Analogous to a "Group By" in SQL, defines how data should be
// aggregated.
// bucketByTime allows for a time span, while bucketBySession allows
// bucketing by sessions.
.bucketByTime(1,TimeUnit.DAYS)
.setTimeRange(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.build();

The previous example uses aggregated data points, where each DataPoint represents the number of steps walked in a day. For this particular use case, aggregated data points have two advantages:

  • Your app and the fitness store exchange smaller amounts of data.
  • Your app doesn't have to aggregate the data manually.

Aggregate data for multiple activity types

Your app can use data requests to retrieve many different types of data. The following example shows how to create a DataReadRequest to get calories burned for each activity performed within the specified time range. The resulting data matches the calories per activity as reported in the Google Fit app, where each activity gets its own bucket of calorie data.

Kotlin

valreadRequest=DataReadRequest.Builder()
.aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
.bucketByActivityType(1,TimeUnit.SECONDS)
.setTimeRange(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.build()

Java

DataReadRequestreadRequest=newDataReadRequest.Builder()
.aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
.bucketByActivityType(1,TimeUnit.SECONDS)
.setTimeRange(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.build();

After you create a DataReadRequest instance, use the HistoryClient.readData() method to asynchronously read historical data.

The following example demonstrates how to obtain the DataPoint instances from a DataSet:

Kotlin

Fitness.getHistoryClient(this,GoogleSignIn.getAccountForExtension(this,fitnessOptions))
.readData(readRequest)
.addOnSuccessListener{response->
// The aggregate query puts datasets into buckets, so flatten into a
// single list of datasets
for(dataSetinresponse.buckets.flatMap{it.dataSets}){
dumpDataSet(dataSet)
}
}
.addOnFailureListener{e->
Log.w(TAG,"There was an error reading data from Google Fit",e)
}
fundumpDataSet(dataSet:DataSet){
Log.i(TAG,"Data returned for Data type: ${dataSet.dataType.name}")
for(dpindataSet.dataPoints){
Log.i(TAG,"Data point:")
Log.i(TAG,"\tType: ${dp.dataType.name}")
Log.i(TAG,"\tStart: ${dp.getStartTimeString()}")
Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}")
for(fieldindp.dataType.fields){
Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
}
}
}
funDataPoint.getStartTimeString()=Instant.ofEpochSecond(this.getStartTime(TimeUnit.SECONDS))
.atZone(ZoneId.systemDefault())
.toLocalDateTime().toString()
funDataPoint.getEndTimeString()=Instant.ofEpochSecond(this.getEndTime(TimeUnit.SECONDS))
.atZone(ZoneId.systemDefault())
.toLocalDateTime().toString()

Java

Fitness.getHistoryClient(this,GoogleSignIn.getAccountForExtension(this,fitnessOptions))
.readData(readRequest)
.addOnSuccessListener(response->{
// The aggregate query puts datasets into buckets, so convert to a
// single list of datasets
for(Bucketbucket:response.getBuckets()){
for(DataSetdataSet:bucket.getDataSets()){
dumpDataSet(dataSet);
}
}
})
.addOnFailureListener(e->
Log.w(TAG,"There was an error reading data from Google Fit",e));
}
privatevoiddumpDataSet(DataSetdataSet){
Log.i(TAG,"Data returned for Data type: ${dataSet.dataType.name}");
for(DataPointdp:dataSet.getDataPoints()){
Log.i(TAG,"Data point:");
Log.i(TAG,"\tType: ${dp.dataType.name}");
Log.i(TAG,"\tStart: ${dp.getStartTimeString()}");
Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}");
for(Fieldfield:dp.getDataType().getFields()){
Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}");
}
}
}
privateStringgetStartTimeString(){
returnInstant.ofEpochSecond(this.getStartTime(TimeUnit.SECONDS))
.atZone(ZoneId.systemDefault())
.toLocalDateTime().toString();
}
privateStringgetEndTimeString(){
returnInstant.ofEpochSecond(this.getEndTime(TimeUnit.SECONDS))
.atZone(ZoneId.systemDefault())
.toLocalDateTime().toString();
}

Read daily total data

Google Fit also provides simple access to the daily total of a specified data type. Use the HistoryClient.readDailyTotal() method to retrieve the data type that you specify as of midnight of the current day in the device's current timezone. For example, pass in the TYPE_STEP_COUNT_DELTA data type to this method to retrieve the daily total steps. You can pass in an instantaneous data type that has an aggregate daily total. For more information on the supported data types, see DataType.getAggregateType.

Google Fit doesn't require authorization to subscribe to TYPE_STEP_COUNT_DELTA updates from the HistoryClient.readDailyTotal() method when this method is called using the default account and no scopes are specified. This can be useful if you require step data for use in areas where you're unable to show the permissions panel, for example on Wear OS watch faces.

Users prefer to see consistent step counts across the Google Fit app, other apps, and Wear OS watch faces, because this provides them with a consistent and reliable experience. To keep step counts consistent, subscribe to steps in the Google Fit platform from your app or watch face, and then update the count in onExitAmbient(). For more information on how to use this data in a watch face, see Watch face complications and the Android WatchFace sample application.

Insert data

To insert historical data, first create a DataSetinstance:

Kotlin

// Declare that the data being inserted was collected during the past hour.
valendTime=LocalDateTime.now().atZone(ZoneId.systemDefault())
valstartTime=endTime.minusHours(1)
// Create a data source
valdataSource=DataSource.Builder()
.setAppPackageName(this)
.setDataType(DataType.TYPE_STEP_COUNT_DELTA)
.setStreamName("$TAG - step count")
.setType(DataSource.TYPE_RAW)
.build()
// For each data point, specify a start time, end time, and the
// data value -- in this case, 950 new steps.
valstepCountDelta=950
valdataPoint=
DataPoint.builder(dataSource)
.setField(Field.FIELD_STEPS,stepCountDelta)
.setTimeInterval(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.build()
valdataSet=DataSet.builder(dataSource)
.add(dataPoint)
.build()

Java

// Declare that the data being inserted was collected during the past hour.
ZonedDateTimeendTime=LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTimestartTime=endTime.minusHours(1);
// Create a data source
DataSourcedataSource=newDataSource.Builder()
.setAppPackageName(this)
.setDataType(DataType.TYPE_STEP_COUNT_DELTA)
.setStreamName("$TAG - step count")
.setType(DataSource.TYPE_RAW)
.build();
// For each data point, specify a start time, end time, and the
// data value -- in this case, 950 new steps.
intstepCountDelta=950;
DataPointdataPoint=DataPoint.builder(dataSource)
.setField(Field.FIELD_STEPS,stepCountDelta)
.setTimeInterval(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.build();
DataSetdataSet=DataSet.builder(dataSource)
.add(dataPoint)
.build();

After you create a DataSet instance, use the HistoryClient.insertData method to asynchronously add this historical data.

Kotlin

Fitness.getHistoryClient(this,GoogleSignIn.getAccountForExtension(this,fitnessOptions))
.insertData(dataSet)
.addOnSuccessListener{
Log.i(TAG,"DataSet added successfully!")
}
.addOnFailureListener{e->
Log.w(TAG,"There was an error adding the DataSet",e)
}

Java

Fitness.getHistoryClient(this,GoogleSignIn.getAccountForExtension(this,fitnessOptions))
.insertData(dataSet)
.addOnSuccessListener(unused->
Log.i(TAG,"DataSet added successfully!"))
.addOnFailureListener(e->
Log.w(TAG,"There was an error adding the DataSet",e));
}

Manage conflicting data points

Each DataPoint in your app's DataSet must have a startTime and an endTime that defines a unique interval within that DataSet, with no overlap between DataPoint instances.

If your app attempts to insert a new DataPoint that conflicts with an existing DataPoint instance, the new DataPoint is discarded. To insert a new DataPoint that might overlap with existing data points, use the HistoryClient.updateData method described in Update data.

A data point can't be inserted if its duration overlaps with any existing data points

Figure 1. How the insertData() method handles new data points that conflict with an existing DataPoint.

Update data

Google Fit lets your app update historical health and wellness data it previously inserted. To add historical data for a new DataSet, or to add new DataPoint instances that don't conflict with existing data points, use the HistoryApi.insertData method.

To update historical data, use the HistoryClient.updateData method. This method deletes any existing DataPoint instances that overlap with DataPoint instances added using this method.

To update historical health and wellness data, first create a DataSet instance:

Kotlin

// Declare that the historical data was collected during the past 50 minutes.
valendTime=LocalDateTime.now().atZone(ZoneId.systemDefault())
valstartTime=endTime.minusMinutes(50)
// Create a data source
valdataSource=DataSource.Builder()
.setAppPackageName(this)
.setDataType(DataType.TYPE_STEP_COUNT_DELTA)
.setStreamName("$TAG - step count")
.setType(DataSource.TYPE_RAW)
.build()
// Create a data set
// For each data point, specify a start time, end time, and the
// data value -- in this case, 1000 new steps.
valstepCountDelta=1000
valdataPoint=DataPoint.builder(dataSource)
.setTimeInterval(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.setField(Field.FIELD_STEPS,stepCountDelta)
.build()
valdataSet=DataSet.builder(dataSource)
.add(dataPoint)
.build()

Java

// Declare that the historical data was collected during the past 50 minutes.
ZonedDateTimeendTime=LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTimestartTime=endTime.minusMinutes(50);
// Create a data source
DataSourcedataSource=newDataSource.Builder()
.setAppPackageName(this)
.setDataType(DataType.TYPE_STEP_COUNT_DELTA)
.setStreamName("$TAG - step count")
.setType(DataSource.TYPE_RAW)
.build();
// Create a data set
// For each data point, specify a start time, end time, and the
// data value -- in this case, 1000 new steps.
intstepCountDelta=1000;
DataPointdataPoint=DataPoint.builder(dataSource)
.setTimeInterval(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.setField(Field.FIELD_STEPS,stepCountDelta)
.build();
DataSetdataSet=DataSet.builder(dataSource)
.add(dataPoint)
.build();

Then, use DataUpdateRequest.Builder() to create a new data update request, and use the HistoryClient.updateData method to make the request:

Kotlin

valrequest=DataUpdateRequest.Builder()
.setDataSet(dataSet)
.setTimeInterval(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.build()
Fitness.getHistoryClient(this,GoogleSignIn.getAccountForExtension(this,fitnessOptions))
.updateData(request)
.addOnSuccessListener{
Log.i(TAG,"DataSet updated successfully!")
}
.addOnFailureListener{e->
Log.w(TAG,"There was an error updating the DataSet",e)
}

Java

DataUpdateRequestrequest=newDataUpdateRequest.Builder()
.setDataSet(dataSet)
.setTimeInterval(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.build();
Fitness.getHistoryClient(this,GoogleSignIn.getAccountForExtension(this,fitnessOptions))
.updateData(request)
.addOnSuccessListener(unused->
Log.i(TAG,"DataSet updated successfully!"))
.addOnFailureListener(e->
Log.w(TAG,"There was an error updating the DataSet",e));

Delete data

Google Fit lets your app delete historical health and wellness data it previously inserted.

To delete historical data, use the HistoryClient.deleteData method:

Kotlin

// Declare that this code deletes step count information that was collected
// throughout the past day.
valendTime=LocalDateTime.now().atZone(ZoneId.systemDefault())
valstartTime=endTime.minusDays(1)
// Create a delete request object, providing a data type and a time interval
valrequest=DataDeleteRequest.Builder()
.setTimeInterval(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.addDataType(DataType.TYPE_STEP_COUNT_DELTA)
.build()
// Invoke the History API with the HistoryClient object and delete request, and
// then specify a callback that will check the result.
Fitness.getHistoryClient(this,GoogleSignIn.getAccountForExtension(this,fitnessOptions))
.deleteData(request)
.addOnSuccessListener{
Log.i(TAG,"Data deleted successfully!")
}
.addOnFailureListener{e->
Log.w(TAG,"There was an error with the deletion request",e)
}

Java

// Declare that this code deletes step count information that was collected
// throughout the past day.
ZonedDateTimeendTime=LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTimestartTime=endTime.minusDays(1);
// Create a delete request object, providing a data type and a time interval
DataDeleteRequestrequest=newDataDeleteRequest.Builder()
.setTimeInterval(startTime.toEpochSecond(),endTime.toEpochSecond(),TimeUnit.SECONDS)
.addDataType(DataType.TYPE_STEP_COUNT_DELTA)
.build();
// Invoke the History API with the HistoryClient object and delete request, and
// then specify a callback that will check the result.
Fitness.getHistoryClient(this,GoogleSignIn.getAccountForExtension(this,fitnessOptions))
.deleteData(request)
.addOnSuccessListener(unused->
Log.i(TAG,"Data deleted successfully!"))
.addOnFailureListener(e->
Log.w(TAG,"There was an error with the deletion request",e));

Apps can delete data from specific sessions or delete all data. For more information, see the API reference for DataDeleteRequest.

Register for data updates

Your app can read raw sensor data in real time by registering with SensorsClient.

For other types of data that are less frequent and are manually counted, your app can register to receive updates when these measurements are inserted into the Google Fit database. Examples of these data types include height, weight, and workouts like weight lifting; for more details, see the full list of supported data types. To register for updates, use HistoryClient.registerDataUpdateListener.

The following code snippet lets an app be notified when the user enters a new value for their weight:

Kotlin

valintent=Intent(this,MyDataUpdateService::class.java)
valpendingIntent=PendingIntent.getService(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT)
valrequest=DataUpdateListenerRegistrationRequest.Builder()
.setDataType(DataType.TYPE_WEIGHT)
.setPendingIntent(pendingIntent)
.build()
Fitness.getHistoryClient(this,GoogleSignIn.getAccountForExtension(this,fitnessOptions))
.registerDataUpdateListener(request)
.addOnSuccessListener{
Log.i(TAG,"DataUpdateListener registered")
}

Java

Intentintent=newIntent(this,MyDataUpdateService.class);
PendingIntentpendingIntent=PendingIntent.getService(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT)
DataUpdateListenerRegistrationRequestrequest=newDataUpdateListenerRegistrationRequest.Builder()
.setDataType(DataType.TYPE_WEIGHT)
.setPendingIntent(pendingIntent)
.build();
Fitness.getHistoryClient(this,GoogleSignIn.getAccountForExtension(this,fitnessOptions))
.registerDataUpdateListener(request)
.addOnSuccessListener(unused->
Log.i(TAG,"DataUpdateListener registered"));

An IntentService can be used to receive notifications of updates:

Kotlin

classMyDataUpdateService:IntentService("MyDataUpdateService"){
overridefunonHandleIntent(intent:Intent?){
valupdate=DataUpdateNotification.getDataUpdateNotification(intent)
// Show the time interval over which the data points were collected.
// To extract specific data values, in this case the user's weight,
// use DataReadRequest.
update?.apply{
valstart=getUpdateStartTime(TimeUnit.MILLISECONDS)
valend=getUpdateEndTime(TimeUnit.MILLISECONDS)
Log.i(TAG,"Data Update start: $start end: $end DataType: ${dataType.name}")
}
}
}

Java

publicclass MyDataUpdateServiceextendsIntentService{
publicMyDataUpdateService(Stringname){
super("MyDataUpdateService");
}
@Override
protectedvoidonHandleIntent(@NullableIntentintent){
if(intent!=null){
DataUpdateNotificationupdate=DataUpdateNotification.getDataUpdateNotification(intent);
// Show the time interval over which the data points
// were collected.
// To extract specific data values, in this case the user's weight,
// use DataReadRequest.
if(update!=null){
longstart=update.getUpdateStartTime(TimeUnit.MILLISECONDS);
longend=update.getUpdateEndTime(TimeUnit.MILLISECONDS);
}
Log.i(TAG,"Data Update start: $start end: $end DataType: ${dataType.name}");
}
}
}

The IntentService must be declared in your AndroidManifest.xml file.

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025年08月28日 UTC.