Skip to content

Instantly share code, notes, and snippets.

@abhijeetchopra
Last active April 24, 2024 20:40
Show Gist options
  • Save abhijeetchopra/99a11fb6016a70287112 to your computer and use it in GitHub Desktop.
Save abhijeetchopra/99a11fb6016a70287112 to your computer and use it in GitHub Desktop.
Creating automatic scheduled backup copies of your Google Sheets using Google Apps Script

How to "Schedule Automatic Backups" of your Google Sheets

This tutorial demonstrates how to use Google Apps Script to:

  • Create copies of the Google Sheet in the desired destination folder automatically at set intervals.

  • Append the time stamp with each backup file's name.

  • Adjust time trigger for backing up every day/hour/minute.

  1. Open your Google Drive and create a new folder right where your Google Sheet is. You may name it anything you like. step 1

  2. Open the folder you just created. You will be able to see its ID in the URL displayed in your browser's address bar. We will need this later. step 2

  3. Open your Google Sheet and select menu item "Tools" --> "Script Editor" step 3

  4. Once the script editor opens, copy and paste the code from the 2-MakeCopy.gs file (see file below) into the "Code.gs" file, replacing the code that's already included. step 4

  5. Copy the ID of the folder you created earlier and paste in place of the xxxxxxxxxxxxxxxxxxxxxxx in the code. step 5

  6. Go to menu "File" --> "Save". step 6

  7. Click on the clocked shaped "Trigger" button. step 7

  8. Click on the blue link to add a trigger. step 8

  9. Select the time interval as per your desire. step 9

  10. Click on "Continue" to authorize running the script you just made. step 10

  11. Click on "Allow" to let the script run on your Google Sheet. step 11

  12. After the set interval(which was every minute in my case), you can see the automatic scheduled backup copies of your Google Sheet saved in the folder you created. step 12

// Abhijeet Chopra
// 26 February 2016
// Google Apps Script to make copies of Google Sheet in specified destination folder
function makeCopy() {
// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second
var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");
// gets the name of the original file and appends the word "copy" followed by the timestamp stored in formattedDate
var name = SpreadsheetApp.getActiveSpreadsheet().getName() + " Copy " + formattedDate;
// gets the destination folder by their ID. REPLACE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with your folder's ID that you can get by opening the folder in Google Drive and checking the URL in the browser's address bar
var destination = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
// gets the current Google Sheet file
var file = DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId())
// makes copy of "file" with "name" at the "destination"
file.makeCopy(name, destination);
}
@sergioferras
Copy link

Great, thanks for this

@MHarriss44
Copy link

I have copied this and substituted my folderid correctly, but it fails But when I execute it I get
ReferenceError: file is not defined
makeCopy @ Code.gs:20

I have modified this code thus:
var destination = DriveApp.getFolderById("1ksDmM2Ks_vPQZJANXaHgw5E3JHKX6IKl");
var file = DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId())

But when I run under the debugger, both these variables are shown as {...} which presumably means undefined. Why can't either of these IDs be found?
MH

@MHarriss44
Copy link

Whatever debug is telling me, the app is correctly executing and copying the sheet when I run it manually. I will see if it runs automatically.

@tsindiastore
Copy link

Back Script Works Fantastic!! Thanks a Ton!! God Bless!! You are the Best!! 😊🙏

@pbanigo
Copy link

pbanigo commented Apr 25, 2021

If you're using a Document file and not a spreadsheet,
Use DocumentApp.getActiveDocument() or you would get a TypeError

@jerrychong25
Copy link

Thanks for the great sharing! It is working fine!

@youssefanis
Copy link

It's an amazing solution and really helpful

Are there similar script for Zoho sheets ? Or any hint where to start from?

Thanks in advance

@Markyb09
Copy link

thank you so much, love you

@jacq-vaucan
Copy link

Thank you for this great script, was really helpful

@zsurat
Copy link

zsurat commented Sep 6, 2021

HI ABHIJEET SIR,

I'M HAVING ISSUE TO MY BACK UP SHEET. I USED IMPORT RANGE IN MY MASTER SHEET, SO I HAVE TO CHANGE IMPORT RANGE IN ALL MY BACKUP FILES.

master1

CAN YOU PLEASE HELP ME TO ADVICE OR ANYONE CAN HELP ME?

THANKS~!

@Yonatankh
Copy link

Awesome post and very helpful! Made some adjustments if someone wants to backup a google doc or google slides.

For Doc:


function makeCopy() {

// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second
var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");

// gets the name of the original file and appends the word "copy" followed by the timestamp stored in formattedDate
var name = DocumentApp.getActiveDocument().getName() + " Copy " + formattedDate;

// gets the destination folder by their ID. REPLACE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with your folder's ID that you can get by opening the folder in Google Drive and checking the URL in the browser's address bar
var destination = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

// gets the current Google Sheet file
var file = DriveApp.getFileById(DocumentApp.getActiveDocument().getId())

// makes copy of "file" with "name" at the "destination"
file.makeCopy(name, destination);
}

For Slides:


function makeCopy() {

// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second
var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");

// gets the name of the original file and appends the word "copy" followed by the timestamp stored in formattedDate
var name = SlidesApp.getActivePresentation().getName() + " Copy " + formattedDate;

// gets the destination folder by their ID. REPLACE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with your folder's ID that you can get by opening the folder in Google Drive and checking the URL in the browser's address bar
var destination = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

// gets the current Google Sheet file
var file = DriveApp.getFileById(SlidesApp.getActivePresentation().getId())

// makes copy of "file" with "name" at the "destination"
file.makeCopy(name, destination);
}

@shmayahaskel
Copy link

Hi,

I implemented the code exactly as described and i am getting this error:
Exception: We're sorry, a server error occurred. Please wait a bit and try again. (line 14, file "Code")
Screenshot 2022-02-22 18 21 29

@Korb
Copy link

Korb commented Aug 11, 2022

  1. Open your Google Sheet and select menu item "Tools" --> "Script Editor"

I have only the following items in the "Tools" menu:

  • Creates new form
  • Spelling >
  • Auto-complete >
  • Notification rules
  • Accessibility

@yamanjain
Copy link

yamanjain commented Oct 15, 2022

@Korb: The menu option for Script Editor is changed in the latest version of Google Sheets. Script editor is now called Apps Script and is under "Extensions" --> "Apps Script"

@just1984
Copy link

HEyhey, thanks for your Script. I am pretty sure that i did it all as mentioned above.. but when i run the function it just gives me "Execution started" but never ever finishes.. i never had one successful backup. Any one else having this issue?

@cmwilli224
Copy link

Is there a (simple) way to integrate the SpreadsheetApp function of getSheetByName to only copy a specific sheet within a spreadsheet?

@Haytamol
Copy link

Thanks for the post! It is helpful! Since the post is a bit old, you may encounter some issues with running it. Here is how you solve this error:

Error:

Exception: You do not have permission to call DriveApp.getFolderById. Required permissions: (https://www.googleapis.com/auth/drive.readonly || https://www.googleapis.com/auth/drive)

Solution:

1/ Open AppsScript => Project settings (gear icon on left), and check the box "Show "appsscript.json" manifest file in editor"

2/ Go back to the editor and you'll find an "appsscript.json" file that showed up. Add the following lines of code:

"oauthScopes": [
    "https://www.googleapis.com/auth/drive"
  ],

3/ Delete the trigger you already made and create a new one. And the problem is solved!

Happy coding!

@xmodiify
Copy link

xmodiify commented Jul 28, 2023

This keeps up to 10 backups

function makeCopy() {
    // generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second
    var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");

    // gets the name of the original file and appends the word "copy" followed by the timestamp stored in formattedDate
    var name = SpreadsheetApp.getActiveSpreadsheet().getName() + " Copy " + formattedDate;

    // gets the destination folder by their ID. REPLACE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with your folder's ID
    var destination = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

    // gets the current Google Sheet file
    var file = DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId())

    // makes copy of "file" with "name" at the "destination"
    file.makeCopy(name, destination);

    // get all files in the destination folder
    var files = destination.getFiles();
    
    // create an array of files and sort by creation date, oldest first
    var fileArray = [];
    while(files.hasNext()) {
        var file = files.next();
        fileArray.push(file);
    }
    
    fileArray.sort(function(a, b) {
        return a.getDateCreated() - b.getDateCreated();
    });

    // if more than 10 files, remove the oldest ones
    while(fileArray.length > 10) {
        var fileToDelete = fileArray.shift(); // get the oldest file
        destination.removeFile(fileToDelete); // delete the oldest file
    }
}

@edkk2021
Copy link

@xmodiify this is perfect and thanks! Any opportunity that we can back up the file to a folder in another Google drive account?

@edkk2021
Copy link

@xmodiify I think I got it figured by sharing the folder from one account to another, then look under "Shared with me", thanks.

@syahz86
Copy link

syahz86 commented Oct 22, 2023

thanks for the sharing, its working like charms... but the autocopy is save as original format.. it is possible if the script can convert the format from original to pdf format??

@syahz86
Copy link

syahz86 commented Oct 22, 2023

Awesome post and very helpful! Made some adjustments if someone wants to backup a google doc or google slides.

For Doc:

function makeCopy() {

// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");

// gets the name of the original file and appends the word "copy" followed by the timestamp stored in formattedDate var name = DocumentApp.getActiveDocument().getName() + " Copy " + formattedDate;

// gets the destination folder by their ID. REPLACE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with your folder's ID that you can get by opening the folder in Google Drive and checking the URL in the browser's address bar var destination = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

// gets the current Google Sheet file var file = DriveApp.getFileById(DocumentApp.getActiveDocument().getId())

// makes copy of "file" with "name" at the "destination" file.makeCopy(name, destination); }

For Slides:

function makeCopy() {

// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");

// gets the name of the original file and appends the word "copy" followed by the timestamp stored in formattedDate var name = SlidesApp.getActivePresentation().getName() + " Copy " + formattedDate;

// gets the destination folder by their ID. REPLACE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with your folder's ID that you can get by opening the folder in Google Drive and checking the URL in the browser's address bar var destination = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

// gets the current Google Sheet file var file = DriveApp.getFileById(SlidesApp.getActivePresentation().getId())

// makes copy of "file" with "name" at the "destination" file.makeCopy(name, destination); }

thanks for the sharing, its working like charms... but the autocopy is save as original format.. it is possible if the script can convert the format from original to pdf format??

@xmodiify
Copy link

Awesome post and very helpful! Made some adjustments if someone wants to backup a google doc or google slides.
For Doc:
function makeCopy() {
// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");
// gets the name of the original file and appends the word "copy" followed by the timestamp stored in formattedDate var name = DocumentApp.getActiveDocument().getName() + " Copy " + formattedDate;
// gets the destination folder by their ID. REPLACE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with your folder's ID that you can get by opening the folder in Google Drive and checking the URL in the browser's address bar var destination = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
// gets the current Google Sheet file var file = DriveApp.getFileById(DocumentApp.getActiveDocument().getId())
// makes copy of "file" with "name" at the "destination" file.makeCopy(name, destination); }
For Slides:
function makeCopy() {
// generates the timestamp and stores in variable formattedDate as year-month-date hour-minute-second var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");
// gets the name of the original file and appends the word "copy" followed by the timestamp stored in formattedDate var name = SlidesApp.getActivePresentation().getName() + " Copy " + formattedDate;
// gets the destination folder by their ID. REPLACE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with your folder's ID that you can get by opening the folder in Google Drive and checking the URL in the browser's address bar var destination = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
// gets the current Google Sheet file var file = DriveApp.getFileById(SlidesApp.getActivePresentation().getId())
// makes copy of "file" with "name" at the "destination" file.makeCopy(name, destination); }

thanks for the sharing, its working like charms... but the autocopy is save as original format.. it is possible if the script can convert the format from original to pdf format??

I’m not sure if it can convert to pdf. But I’d say copy the code to chat gpt and ask if it’s possible to export the file as pdf.

@estebanthi
Copy link

Hey, here's my version of the script that lets you back up several files in the same parent backup folder, and keep x backups of these files (it creates a folder in the parent folder for each file you want to backup, and store the different versions of the file in this folder). It works with any file (not just sheets). To use it you just have to create a project and create a trigger for the run function.

const filesToBackup = [
   'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',  // file id
   'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
]
const backupFolderId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'  // folder id
const limit = 10  // number of backups to keep

function run() {
   filesToBackup.forEach(fileId => makeCopy(fileId, backupFolderId))
   clearAll(backupFolderId, limit)
}

function makeCopy(fileId, backupFolderId) {
   var file = DriveApp.getFileById(fileId)
   var backupFolder = DriveApp.getFolderById(backupFolderId)

   var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");
   var rootName = file.getName()
   var copyName = rootName + "_COPY_" + formattedDate

   destination = getDestinationFolder(rootName, backupFolder)
   file.makeCopy(copyName, destination)
}

function getDestinationFolder(rootName, backupFolder) {
   var children = backupFolder.getFoldersByName(rootName)
   if (!children.hasNext()) {
      var destination = DriveApp.createFolder(rootName)
      destination.moveTo(backupFolder)
   } else {
      var destination = children.next()
   }
   return destination
}

function clearAll(backupFolderId, limit = 10) {
   var backupFolder = DriveApp.getFolderById(backupFolderId);
   var folders = backupFolder.getFolders()

   while (folders.hasNext()) {
      const folder = folders.next()
      clearFolder(folder)
   }
}

function clearFolder(folder) {
   const files = folder.getFiles()
   const filesArray = [];
   while (files.hasNext()) {
      var file = files.next();
      filesArray.push(file);
   }

   filesArray.sort(function (a, b) {
      return b.getDateCreated() - a.getDateCreated(); // Sort in descending order of creation date
   });
   while (filesArray.length > limit) {
      var fileToDelete = filesArray.pop();
      destination.removeFile(fileToDelete);
   }
}

@shkamaru
Copy link

The comments are not copied with the file. is there a solution?

@estebanthi
Copy link

I could not find any solution for Google Apps Script. The only thing I could find is this: https://stackoverflow.com/questions/58708728/apply-original-comments-in-a-copied-document

@svgadkari
Copy link

Hello Team,
We want to scheduled backup Google drive folder instead of Google Sheet. Is there any way to do it.

@GitHubAl
Copy link

GitHubAl commented Nov 10, 2023

In case useful: creates a copy of the sheets and copy/paste only values.

  • This script replaces formula with calculated static values.

  • Huge sheet? -> might run into execution time limit (which is 6 minutes for consumer accounts). Because script reads and writes each cell individually.

function makeCopyWithValuesOnly() {
  var sourceSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var formattedDate = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyyy-MM-dd' 'HH:mm:ss");
  var name = sourceSpreadsheet.getName() + " backup " + formattedDate;
  var destinationFolderId = 'your-folder-id'; // <-- Replace with your actual folder ID
  var destination = DriveApp.getFolderById(destinationFolderId);
  var originalFile = DriveApp.getFileById(sourceSpreadsheet.getId());
  var newFile = originalFile.makeCopy(name, destination);
  var newSpreadsheet = SpreadsheetApp.openById(newFile.getId());
  var sheets = newSpreadsheet.getSheets();

  sheets.forEach(function(sheet) {
    var range = sheet.getDataRange();
    var values = range.getDisplayValues(); // Get displayed values which include evaluated importrange data

    // Remove all formulas and keep only values
    var plainValues = values.map(function(row) {
      return row.map(function(cell) {
        return cell; // Directly use the displayed value
      });
    });

    // Set the plain values back to the range to overwrite all formulas
    range.setValues(plainValues);
  });

  Logger.log('New spreadsheet created: ' + newSpreadsheet.getUrl());
}

If =IMPORTRANGE() is buggy, resulting in #REF! -> try following and ensure that, original spreadsheet (is fully loaded and all IMPORTRANGE functions have completed loading their data before you run this script + source and target spreadsheet have same structure and sheet names.

function copyValuesToAnotherSpreadsheet() {
  var sourceSpreadsheetId = 'your-sourceSpreadsheetId-here'; // <- Add your sourceSpreadsheetId
  var targetSpreadsheetId = 'your-targetSpreadsheetId-here'; // <- Add your targetSpreadsheetId.
  
  // Open the source and target spreadsheets
  var sourceSpreadsheet = SpreadsheetApp.openById(sourceSpreadsheetId);
  var targetSpreadsheet = SpreadsheetApp.openById(targetSpreadsheetId);
  
  // Iterate over each sheet in the source spreadsheet
  var sourceSheets = sourceSpreadsheet.getSheets();
  sourceSheets.forEach(function(sheet) {
    var sheetName = sheet.getName();
    var targetSheet = targetSpreadsheet.getSheetByName(sheetName);
    
    // If the sheet exists in the target spreadsheet
    if (targetSheet) {
      var range = sheet.getDataRange();
      var values = range.getDisplayValues(); // Get the displayed values
      
      // Get the range in the target sheet with the same dimensions
      var targetRange = targetSheet.getRange(1, 1, values.length, values[0].length);
      
      // Set the values in the target sheet
      targetRange.setValues(values);
    }
  });
}

@vehgroshop
Copy link

vehgroshop commented Feb 28, 2024

Hey, here's my version of the script that lets you back up several files in the same parent backup folder, and keep x backups of these files (it creates a folder in the parent folder for each file you want to backup, and store the different versions of the file in this folder). It works with any file (not just sheets). To use it you just have to create a project and create a trigger for the run function.

const filesToBackup = [
   'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',  // file id
   'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
]
const backupFolderId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'  // folder id
const limit = 10  // number of backups to keep

function run() {
   filesToBackup.forEach(fileId => makeCopy(fileId, backupFolderId))
   clearAll(backupFolderId, limit)
}

function makeCopy(fileId, backupFolderId) {
   var file = DriveApp.getFileById(fileId)
   var backupFolder = DriveApp.getFolderById(backupFolderId)

   var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd' 'HH:mm:ss");
   var rootName = file.getName()
   var copyName = rootName + "_COPY_" + formattedDate

   destination = getDestinationFolder(rootName, backupFolder)
   file.makeCopy(copyName, destination)
}

function getDestinationFolder(rootName, backupFolder) {
   var children = backupFolder.getFoldersByName(rootName)
   if (!children.hasNext()) {
      var destination = DriveApp.createFolder(rootName)
      destination.moveTo(backupFolder)
   } else {
      var destination = children.next()
   }
   return destination
}

function clearAll(backupFolderId, limit = 10) {
   var backupFolder = DriveApp.getFolderById(backupFolderId);
   var folders = backupFolder.getFolders()

   while (folders.hasNext()) {
      const folder = folders.next()
      clearFolder(folder)
   }
}

function clearFolder(folder) {
   const files = folder.getFiles()
   const filesArray = [];
   while (files.hasNext()) {
      var file = files.next();
      filesArray.push(file);
   }

   filesArray.sort(function (a, b) {
      return b.getDateCreated() - a.getDateCreated(); // Sort in descending order of creation date
   });
   while (filesArray.length > limit) {
      var fileToDelete = filesArray.pop();
      destination.removeFile(fileToDelete);
   }
}

Hi estebanthi,

I created a new script and pasted your code in it, added 2 file-id's and the id of the google drive root folder, but when I run the code I get the following error:

Exception: Unexpected error while getting the method or property getFileById on object DriveApp.
makeCopy @ Backups.gs:15
(anoniem) @ Backups.gs:10
run @ Backups.gs:10

I suspect that I have made a slight error in the file ID, but it might be something else.
Am I correct in assuming, that the file ID of a google sheet is found in the url of the sheet between the following pieces?:
https://docs.google.com/spreadsheets/d/ FILE-ID-HERE /edit#gid=XXXXXXXXX
If I'm right about this assumption, could you tell me what else might be causing the error?

@irvtech
Copy link

irvtech commented Apr 24, 2024

Thanks, @abhijeetchopra This is great! You saved me a ton of time. The solution works like a charm and took moments to implement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment