Export Google Fit Daily Steps, Weight and Distance to a Google Sheet

Updated on Friday, April 28, 2023

Comments Page 18

Return to post.



Hi - Thanks much for the howto. I'm getting hung up at the authorization stage. It goes through such that I see I have given access (in the permissions page of my account) for the application to Google Docs, but NOT for Google Fit. Every time I try to do anything off the menu, it repeats the access process.

Any advice? Could the problem be trying to give the script access to fit data on a different google account than the spreadsheet?

Thanks for any help or pointers!


Hi Robert.

This is exactly what I was trying to figure out. I stopped and googled - thanks man, this is great!

Sachin Jain


Below bit of code fetches based on Month & activity & writes to the history tab the Start Date, End Date, Activity Name & Distance in kms.

It needs a sheet a called ActivityType which is basically a copy paste of all google fitactivity types.

function onOpen() {

var ui = SpreadsheetApp.getUi();

ui.createMenu('Google Fit')

.addItem('Authorize if needed (does nothing if already authorized)', 'showSidebar')

.addItem('Get Metrics Month Wise By Activity', 'getMetricsMonthWise')

.addItem('Reset Settings', 'clearProps')



function getMetricsMonthWise(){

var today = new Date();

for(var month=0; month <= today.getMonth(); month++){

var startDate = new Date();




var endDate = new Date(startDate.getYear(),month + 1, 0 );


getMetricsForDays(startDate, endDate, 'History');



function getMetricsForDays(start, end, tabName) {

Logger.log('Start Date' + start);

Logger.log('End Date' + end);

var fitService = getFitService();

var request = {

"aggregateBy": [


"dataTypeName": "com.google.distance.delta",

"dataSourceId": "derived:com.google.distance.delta:com.google.android.gms:merge_distance_delta"



"bucketByActivityType": { "minDurationMillis": 60000},

"startTimeMillis": start.getTime(),

"endTimeMillis": end.getTime()


var response = UrlFetchApp.fetch('URL', {

headers: {

Authorization: 'Bearer ' + fitService.getAccessToken()


'method' : 'post',

'contentType' : 'application/json',

'payload' : JSON.stringify(request, null, 2)


var json = JSON.parse(response.getContentText());

var ss = SpreadsheetApp.getActiveSpreadsheet();

var sheet = ss.getSheetByName(tabName);

for(var b = 0; b < json.bucket.length; b++) {

var distance = -1;

if (json.bucket[b].dataset[0].point.length > 0) {

distance = json.bucket[b].dataset[0].point[0].value[0].fpVal/1000;


var activity = getActivityName(json.bucket[b].activity);

sheet.appendRow([start, end,


distance == -1 ? ' ' : distance]);



function getActivityName(activityCode) {

var ss = SpreadsheetApp.getActiveSpreadsheet();

var sheet = ss.getSheetByName('ActivityType');

var startColumn = 2;

var startRow = 1;

for (var row_num = startRow; row_num < 120; row_num++) {

var cellValue = sheet.getRange(row_num, startColumn).getValue();

if (cellValue == activityCode) {

var activityName = sheet.getRange(row_num, startColumn-1).getValue();

return activityName;



return 'INVALID CODE:' + activityCode;



Maximal amount of days to request is 90

I'll add a user promt to enter amount of days to load, and then load it all by 90-day chunks.

function showPrompt() {

var ui = SpreadsheetApp.getUi(); // Same variations.

var result = ui.prompt(

'Let\'s get old days!',

'Please enter number of days:',


// Process the user's response.

var button = result.getSelectedButton();

var text = result.getResponseText();

if (button == ui.Button.OK) {

// User clicked "OK".

n = parseInt(text)

if (n <= 90)

getMetricsForDays(1, n, 'History');

if (n > 90)



if (n-90<0)

getMetricsForDays(1, n, 'History');


getMetricsForDays(n-89, n, 'History');





Sachin Jain

Thanks. That did the trick :).

I am still working on the code. Will post it here once it is finished.

Robert Ellison

Sachin - I have not experimented with activity type. Have you tried not specifying activityDataSourceId? It looks like that should return everything and then you could try filtering down from there. If that doesn't help check the API explorer link near the end of the post.

Sachin Jain


Thanks for the code. After the initial hiccups I was able to run this on google sheets.

Do you have an example for bucketByActivityType.

I am not able to figure what value should be put in for parameter activityDataSourceId of this object.

Many Thanks

Sachin Jain

Robert Ellison

Hi Gabriel, not something I've had to deal with before. I'd suggest starting from scratch - new spreadsheet, new Google API Console project - and see if this solves the problem. Something must still be linked somewhere.


Hi there, I've made a mistake and deleted an oAuth Client and now nothing is working anymore, do you have any ideea how to fix this ?

I've created a new client, updated the codes however it doesn't work...


401. That’s an error.

Error: deleted_client

The OAuth client was deleted.

Request Details

That’s all we know.


Robert Ellison

Dom - if you look at line 20 this pulls 60 days of history. You can change this to 365 (or more) to pull for a longer period.

I think the data from the API is in metric units. If not, you could convert in the spreadsheet or multiply by a conversion factor in the script before writing the data (i.e. at line 86).

Not sure why you'd get blank rows. This would happen if there is no data - assume that you see data for those days in the Google Fit app?

Add Comment

All comments are moderated. Your email address is used to display a Gravatar and optionally for notification of new comments and to sign up for the newsletter.

I Thought He Came With You is Robert Ellison's blog.



Average Server Response Time in Azure Metrics