Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Gmail: delete old emails automatically

Gmail: delete old emails automatically

Automatically deletes old emails that match the specified label.

Get started

  • Create a new Google Apps Script at https://script.google.com
  • Overwrite the placeholder with the javascript below
  • Update the following constants:
    • LABEL_TO_DELETE: the label that should be have old messages deleted
    • DELETE_AFTER_DAYS: the age of messsages after which they will be moved to trash
  • Save the script, then run:
    • Initialize
    • Install

If you ever want to remove the script, run Uninstall to remove any left over triggers.

Changelog

2017-07-31

  • Added support for multiple labels
  • Added configurable TRIGGER_NAME
  • Increased default page size
  • Decreased default delay between receipt and delete

2016-01-21

  • Removed use of deprecated Session.getTimeZone()
  • Improved efficiency for long threads by checking thread.getLastMessageDate()

Acknowledgements

H/T: Arun's post How to Auto Delete Old Emails In Any Gmail Label

// The name of the Gmail Label that is to be checked for purging?
var LABELS_TO_DELETE = [
"notifications-github",
"notifications-trello",
"notifications-hipchat"
];
var TRIGGER_NAME = "dailyDeleteGmail";
// Purge messages in the above label automatically after how many days?
var DELETE_AFTER_DAYS = "4";
var TIMEZONE = "AEST";
// Maximum number of threads to process per run
var PAGE_SIZE = 150;
// If number of threads exceeds page size, resume job after X mins (max execution time is 6 mins)
var RESUME_FREQUENCY = 10;
/*
IMPLEMENTATION
*/
function Intialize() {
return;
}
function Install() {
// First run 2 mins after install
ScriptApp.newTrigger(TRIGGER_NAME)
.timeBased()
.at(new Date((new Date()).getTime() + 1000*60*2))
.create();
// Run daily there after
ScriptApp.newTrigger(TRIGGER_NAME)
.timeBased().everyDays(1).create();
}
function Uninstall() {
var triggers = ScriptApp.getProjectTriggers();
for (var i=0; i<triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
function dailyDeleteGmail() {
var age = new Date();
age.setDate(age.getDate() - DELETE_AFTER_DAYS);
var purge = Utilities.formatDate(age, TIMEZONE, "yyyy-MM-dd");
var search = "(label:" + LABELS_TO_DELETE.join(" OR label:") + ") before:" + purge;
Logger.log("PURGE: " + purge);
Logger.log("SEARCH: " + search);
try {
var threads = GmailApp.search(search, 0, PAGE_SIZE);
// Resume again in 10 minutes
if (threads.length == PAGE_SIZE) {
Logger.log("Scheduling follow up job...");
ScriptApp.newTrigger(TRIGGER_NAME)
.timeBased()
.at(new Date((new Date()).getTime() + 1000*60*RESUME_FREQUENCY))
.create();
}
// Move threads/messages which meet age criteria to trash
Logger.log("Processing " + threads.length + " threads...");
for (var i=0; i<threads.length; i++) {
var thread = threads[i];
if (thread.getLastMessageDate() < age) {
thread.moveToTrash();
} else {
var messages = GmailApp.getMessagesForThread(threads[i]);
for (var j=0; j<messages.length; j++) {
var email = messages[j];
if (email.getDate() < age) {
email.moveToTrash();
}
}
}
}
} catch (e) {}
}
@knireis

This comment has been minimized.

Copy link

knireis commented May 28, 2018

i have labels with many sublabels, is there an easy way to include these sublabels automatically.
Maybe with help of wildcards? Like Label/*?

@sansal

This comment has been minimized.

Copy link

sansal commented Jun 8, 2018

Hello,
I want to use "Automatically deletes old emails that older than 1 month."
With this command "older_than:1m" can you help, thanks.

@Raboo

This comment has been minimized.

Copy link

Raboo commented Oct 25, 2018

it doesn't work for me. I guess it have to do with the delete not being able to delete over million e-mails. I don't get any useful logs.
Is it possible to delete in chunks?

@reidrivenburgh

This comment has been minimized.

Copy link

reidrivenburgh commented Jan 7, 2019

Hi. I've been fiddling with this version and another for awhile now. I noticed that at times my triggers seem to fire and then stick around in a disabled state. I'm a novice when it comes to google scripting, but this version of your main function with the added deleteTrigger() call seems to keep things tidy for me. Feel free to use as you see fit.... Thanks.

function dailyDeleteGmail(ev) {
  var age = new Date();  
  age.setDate(age.getDate() - DELETE_AFTER_DAYS);    
  
  var purge  = Utilities.formatDate(age, TIMEZONE, "yyyy-MM-dd");
  var search = "(label:" + LABELS_TO_DELETE.join(" OR label:") + ") before:" + purge;
  console.log("PURGE: " + purge);
  console.log("SEARCH: " + search);
  
  try {
    var threads = GmailApp.search(search, 0, PAGE_SIZE);
    
    // Resume again in 10 minutes
    if (threads.length == PAGE_SIZE) {
      console.log("Scheduling follow up job...");
      ScriptApp.newTrigger(TRIGGER_NAME)
               .timeBased()
               .at(new Date((new Date()).getTime() + 1000*60*RESUME_FREQUENCY))
               .create();
    }
    
    // Move threads/messages which meet age criteria to trash
    console.log("Processing " + threads.length + " threads...");
    for (var i=0; i<threads.length; i++) {
      var thread = threads[i];
      
      if (thread.getLastMessageDate() < age) {
        thread.moveToTrash();
      } else {
        var messages = GmailApp.getMessagesForThread(threads[i]);
        for (var j=0; j<messages.length; j++) {
          var email = messages[j];       
          if (email.getDate() < age) {
            email.moveToTrash();
          }
        }
      }
    }
    
  } catch (e) {}
  
  /* Delete the trigger that caused this (if there is one), otherwise the trigger
  seems to linger in a disabled state. */
  if (ev != null) {
    if (!ScriptApp.getProjectTriggers().some(function (trigger) {
      if (trigger.getUniqueId() === ev.triggerUid) {
        ScriptApp.deleteTrigger(trigger);
        return true;
      }
      
      return false;
    })) {
      console.error("Could not find trigger with id '%s'", triggerUid);
    }
  }
}
@sergey777s

This comment has been minimized.

Copy link

sergey777s commented Jan 10, 2019

in this script you delete messages after 4 days, but I neet to delete for example 40 oldest threads in gmail, in my account this year is 2017. How to get last thread date in script for delete only 40 very old threads?

@reidrivenburgh

This comment has been minimized.

Copy link

reidrivenburgh commented Jan 15, 2019

I rewrote the "delete trigger" section above, since it seemed to be deleting my nightly trigger. Here is that section:

  /* Delete the trigger that caused this (if there is one), otherwise the trigger
  seems to linger in a disabled state. Only delete if there are two, otherwise the
  nightly one seems to be getting deleted. */
  if (ev != null) {
    // Count the number of triggers.
    var triggers = ScriptApp.getProjectTriggers();
    var triggerCounter = triggers.length;  
    
    if (triggerCounter == 2) {
    if (!ScriptApp.getProjectTriggers().some(function (trigger) {
      if (trigger.getUniqueId() === ev.triggerUid) {
        console.log("Deleting " + trigger.getUniqueId());
        ScriptApp.deleteTrigger(trigger);
        return true;
      }
      
      return false;
    })) {
      console.error("Could not find trigger with id '%s'", triggerUid);
    }
    }
  }
@trusktr

This comment has been minimized.

Copy link

trusktr commented Jun 5, 2019

@sansal you can replace the line

  var search = "(label:" + LABELS_TO_DELETE.join(" OR label:") + ") before:" + purge;

with

  var search = "(label:" + LABELS_TO_DELETE.join(" OR label:") + ") older_than:1m"; // <--- HERE, use it
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.