Create time-based Gmail filters with Google Apps Script

Somewhere between spam and archived messages are the emails that I want to glance at briefly, yet I don’t want clogging my Inbox when I forget to delete them. Apple Mail lets you create a rule for messages received greater than x days old. However, because the condition is only tested as messages arrive, none of the messages will meet the criteria. You can get around this by reapplying the rule, but that defeats the purpose of automating the process. Thankfully, Gmail users can take advantage of Google Apps Script to create time-based events that will move, mark, or label your messages without having to open a mail client.

For this example, let’s create a script that will delete all messages from the “delete me” label that are older than two days. First, make a new Gmail filter and populate the search options so it will catch all of your semi-spam. Remember to create and apply the “delete me” label to the matched messages.

Go to https://script.google.com , create a script for a Blank Project, and paste the code below into the editor. You may edit the delayDays variable to specify how many days should pass before the matched messages will be moved to the trash.

function cleanUp() {
  var delayDays = 2 // Enter # of days before messages are moved to trash
  var maxDate = new Date();
  maxDate.setDate(maxDate.getDate()-delayDays);
  var label = GmailApp.getUserLabelByName("delete me");
  var threads = label.getThreads();
  for (var i = 0; i < threads.length; i++) {
    if (threads[i].getLastMessageDate()<maxDate)
      {
        threads[i].moveToTrash();
      }
  }
}

After you save your script, run it by either clicking the play button or by selecting it from the Run menu at the top of the screen. Authorize the script to access your Gmail account, then schedule the interval in which you would like the script to run. Select Current script’s triggers… from the Resources menu and select a period, I chose every 30 minutes.

Notice that your Trash is now filled with old messages, previously destined to collect dust in your Inbox. To save one of your “delete me” messages, simply remove the label.

Time-based filters can also be used to auto-archive emails older than x days. You can simplify your javascript function by using the search method to combine several advanced search operators. For example, using the script below I can archive every thread in my Inbox that is read, older than two days, and not labeled “delete me”.

function archiveInbox() {
// Every thread in your Inbox that is read, older than two days, and not labeled "delete me".
var threads = GmailApp.search('label:inbox is:read older_than:2d -label:"delete me"');
  for (var i = 0; i < threads.length; i++) {
    threads[i].moveToArchive();
}
}

Deleting or archiving thousands of messages may take some time. You can speed things up and minimize “Exceeded maximum execution time” errors by processing threads in batches of 100. Here are several examples:

Batch Delete Functions

function batchDeleteA() {
  var batchSize = 100 // Process up to 100 threads at once
  var threads = GmailApp.search('label:"delete me" older_than:2d');
  for (j = 0; j < threads.length; j+=batchSize) {
    GmailApp.moveThreadsToTrash(threads.slice(j, j+batchSize));
  }
}

function batchDeleteB() {
  var mySearch = "'label:\"delete me\" older_than:2d'"
  var batchSize = 100 // Process up to 100 threads at once
  while (GmailApp.search(mySearch, 0, 1).length == 1)
  {
    GmailApp.moveThreadsToTrash(GmailApp.search(mySearch, 0, batchSize));
  }  
}

function batchDeletePromotions() {
  // include one or more categories below
  // valid categories include ["primary", "promotions", "social", "updates", "forums"];  
  // category descriptions: https://support.google.com/mail/answer/3094596?hl=en
  var categories = ["social", "promotions", "forums"];
  
  var batchSize = 100 // Process up to 100 threads at once
  for (i = 0; i < categories.length; i++) {
    var threads = GmailApp.search('label:inbox category:'+categories[i]+' older_than:2d');
    for (j = 0; j < threads.length; j+=batchSize) {
      GmailApp.moveThreadsToTrash(threads.slice(j, j+batchSize));
    }
  }
}

function multipleLabels() {
  var myLabels = {'"delete me"': "2d", '"scripts"': "5d"};
  var batchSize = 100; // Process up to 100 threads at once
  for(aLabel in myLabels)
  {
    var threads = GmailApp.search('label:'+aLabel+' older_than:'+myLabels[aLabel]+'');
    for (j = 0; j < threads.length; j+=batchSize) {
      GmailApp.moveThreadsToTrash(threads.slice(j, j+batchSize));
    }
  }  
}

Batch Archive Functions

function batchArchiveA() {
  var batchSize = 100 // Process up to 100 threads at once
  var threads = GmailApp.search('label:inbox is:read older_than:1d -label:"delete me"');
  for (j = 0; j < threads.length; j+=batchSize) {
    GmailApp.moveThreadsToArchive(threads.slice(j, j+batchSize));
  }
}

function batchArchiveB() {
  var mySearch = "'label:inbox is:read older_than:2d -label:\"delete me\"'"
  var batchSize = 100 // Process up to 100 threads at once
  while (GmailApp.search(mySearch, 0, 1).length == 1)
  {
    GmailApp.moveThreadsToArchive(GmailApp.search(mySearch, 0, batchSize));
  }  
}

Other Functions

function markMutedThreadsRead() {
  //mark as read and remove label from muted threads
  var threadLabel = GmailApp.getUserLabelByName("my label");
  var batchSize = 100 // Process up to 100 threads at once
  var threads = GmailApp.search('is:muted');
  for (j = 0; j < threads.length; j+=batchSize) {
    threadLabel.removeFromThreads(threads.slice(j, j+batchSize));
    GmailApp.markThreadsRead(threads.slice(j, j+batchSize));
  }
}

, , , , ,

142 Responses to Create time-based Gmail filters with Google Apps Script

  1. Saqib Ali January 16, 2013 at 4:07 am #

    cool tip! shared it with employees at my company :)

  2. Mel January 18, 2013 at 3:18 am #

    Thanks for writing this tips. Exactly what I’ve been looking for…
    May I repost this in my blog, using my language? Btw, I’m from Indonesia

    • johneday January 18, 2013 at 6:30 am #

      Hi Mel. Feel free to translate the article into your language and repost it to your blog. Please include a link to the original post.
      John

  3. Rhett January 18, 2013 at 10:25 pm #

    Very nice! I’m getting an error on the “var threads = label.getThreads line.

    TypeError: Cannot call method “getThreads” of null. (line 8)

    Any suggestions? I added the “delete-me” to a couple of emails for an initial test, so that value isn’t null. One is less than 2 days, the other more than 2 days old.

    • johneday January 18, 2013 at 11:06 pm #

      Hi Rhett,
      It looks like you added a dash to the “delete me” label. Either remove the dash from your label or update the script to “delete-me”.

      • Rhett January 21, 2013 at 1:38 am #

        Thanks, I was hoping it was something like that.

    • BryBry February 23, 2013 at 4:59 am #

      A simple fix will keep this script from throwing unnecessary errors:

      var label = GmailApp.getUserLabelByName(“delete me”);
      if( label ) {
      var threads = label.getThreads();
      for (var i = 0; i < threads.length; i++) {
      if (threads[i].getLastMessageDate()<maxDate) {
      threads[i].moveToTrash();
      }
      }
      }

      Note the "if label" line.

      • johneday February 23, 2013 at 12:46 pm #

        Thanks BryBry. Although this will stop some errors, users will not be aware that the script is not functioning properly.

  4. Michael February 22, 2013 at 2:34 am #

    Wow, this is going to clean my inbox right up. Did not know there were scripts for your Inbox. Thanks!

  5. Scott February 22, 2013 at 5:00 pm #

    Hi! I’ve been looking for something like this for years!

    I was wondering if the code could be set to archive the emails instead of moving them to the trash?

    • johneday February 22, 2013 at 5:10 pm #

      Hi Scott. You can amend the search criteria of the archiveInbox function at the bottom of the page. label:inbox older_than:2d label:”delete me” would match all messages in your inbox labeled “delete me” that are older than two days.

  6. ChrisP February 22, 2013 at 5:38 pm #

    Hi! this I am trying to get this to work but i keep getting this message:
    TypeError: Cannot call method “getThreads” of null. (line 8, file “Code”)Dismiss

    what am I doing wrong?

    thanks!

    • johneday February 22, 2013 at 11:57 pm #

      Make sure you create the “delete me” label in Gmail before you run the script.

  7. Nick February 22, 2013 at 6:03 pm #

    John,

    This is outstanding, like others I’ve been looking for something like this for awhile. Question, how would you amend the code to apply to multiple labels at once. I have a handful of labels I’d like cleaned out periodically like this. Do I need to run a separate script for each one or can you make this one apply to more than one label at a time?

    Thanks,
    Nick

    • johneday February 22, 2013 at 7:22 pm #

      Hi Nick. An easy way of doing that is to change the search criteria of the archiveInbox function at the bottom of the page to:

      var threads = GmailApp.search(‘label:”first label”|label:”second label” is:read older_than:2d’);

      and then replace
      threads[i].moveToArchive();
      with
      threads[i].moveToTrash();

      • Nick February 22, 2013 at 9:41 pm #

        Thanks! It’s kicking back an illegal character error though, I can’t seem to identify the issue. Here’s the line of code as I would need it:

        var threads = GmailApp.search(‘label:Clothes|label:Daily Deals|label:Discount Sites|label:Google VMail is:older_than:7d’);

        Thanks again for your help.

        • johneday February 22, 2013 at 9:44 pm #

          You need to quote labels with spaces. Try:
          var threads = GmailApp.search(‘label:Clothes|label:”Daily Deals”|label:”Discount Sites”|label:”Google VMail” is:older_than:7d’);
          You can test the search in search bar of Gmail itself.

      • sebell69 December 18, 2013 at 11:24 pm #

        Hi

        I tried adding your lines for nick into the original script but I get a error saying “line 19, file “code”” but I dont see the “code” or file for that matter.

        What I am trying to do is use your script to remove email that is older than x days from specific labels (folders) and dump them in the trash label for disposal.

        Could you help me w/this?

  8. John February 22, 2013 at 11:38 pm #

    When I run the script I get an error message:

    TypeError: Cannot call “getThreads” of null. (line 8, file “Code”)

    What I typed: var threads = label.getThreads();
    What you typed: var threads = label.getThreads();

    How do I correct?

    Thanks.

    • johneday February 22, 2013 at 11:56 pm #

      Did you create the “delete me” label in Gmail before you ran the script?

  9. Manfred February 23, 2013 at 1:24 am #

    I copied your script into Notepad then into the script tool. Looks like yours. Get the following error when I try to run it: Missing ; before statement. (line 1, file “Code”)

    • johneday February 23, 2013 at 3:24 am #

      I would try to either paste the code directly into a new blank project or use the Google Apps script shared in this post: http://goo.gl/s7SDL

  10. spotter February 23, 2013 at 4:40 pm #

    If I do want to save a message can I just move it to the archive, or do I also need to remove the ‘delete me’ label?

    • johneday February 23, 2013 at 5:53 pm #

      After you remove the ‘delete me’ label, you can either archive the message or leave it in your inbox.

  11. cameron February 24, 2013 at 1:01 am #

    Hi I keep getting an error.

    First I had: Cannot call “getThreads” of null. (line 8, file “Code”)

    So I then created the ‘delete me’ label in my gmail.

    Plus I also added this to the code on line 8: if( label ) {

    But now I get this error message:

    Missing } after function body. (line 16, file “Code”)

    My total code I have entered is:

    function cleanUp() {
    var delayDays = 7 // Enter # of days before messages are moved to trash

    var maxDate = new Date();
    maxDate.setDate(maxDate.getDate()-delayDays);

    var label = GmailApp.getUserLabelByName(“delete me”);
    if( label ) {
    var threads = label.getThreads();
    for (var i = 0; i < threads.length; i++) {
    if (threads[i].getLastMessageDate()<maxDate)
    {
    threads[i].moveToTrash();
    }
    }
    }

    i.e. the last } ends on line 16

    What am I doing wrong?

    Thanks
    Fiona

    • johneday February 24, 2013 at 1:56 am #

      Hi Fiona. BryBry’s suggestion will prevent an error from occurring if the label does not exist. You won’t be alerted that the script is not functioning properly either. I suggest you use the script shared in this post: http://goo.gl/s7SDL

      However, this incorporates BryBry’s addition: http://pastebin.com/1y01PnEV

  12. David Rahrer February 24, 2013 at 1:07 am #

    This is really fascinating — thanks!. On the second script, would there be a way to mark the emails as read as well as archiving them? I checked and it doesn’t do it as is.

    I had no idea this was even available :)

    • David Rahrer February 24, 2013 at 1:31 am #

      Ack, just realized that one already picks only read threads, lol.

  13. David Rahrer February 24, 2013 at 4:18 am #

    Here is my problem and I’m stumbling on syntax. In the second example, I’ve removed the criteria that the email be read and I want to mark it read after archiving it. I’m adding the criteria that it not be marked Important, etc. My problem is with marking it read. I found this:

    https://developers.google.com/apps-script/class_gmailmessage#markUnread

    but when I try to add a line to use it after archiving in the For loop, I get “undefined” errors. Thanks for any guidance on this.

  14. tom riddle February 24, 2013 at 12:50 pm #

    hi john! this is what i’m looking for in Gmail ever since i came to know of the ‘schedule-clean up’ feature of Outlook.thank you so much!
    can i add both the scripts on the same page and run?i want to both auto-delete my newsletters and auto-archive other emails. will there be any conflicts?

    • johneday February 24, 2013 at 4:07 pm #

      Hi Tom. One script file may contain multiple functions, just remember to set up a trigger for each function.

  15. Rick February 24, 2013 at 3:10 pm #

    This is really great – thanks for writing this. I’ve thought that the automated cleanup features of Outlook.com were really the one major thing that made it interesting over Gmail. However, with this, there’s no need. Thanks again.

  16. Susanna February 24, 2013 at 6:36 pm #

    For ages, I’ve had coupon emails skip my inbox and go straight to a coupon folder. They were on my phone when I was checking out and needed them, and it was a good money saver. I’ve never cleaned them up. Your script was just what I needed. I changed “delete me” to “Coupons” and let it fly.

    Had over 3500 of these emails junking up my box, and the script actually timed out the box was so big, but it’s working, I just had to run it several times. Thanks so much!!!!

  17. Nick February 25, 2013 at 12:23 pm #

    This is excellent. Is is possible to trigger this based on mailbox size? I’d like to only delete when I have to, so the script would just get triggered when the mailbox size reached 90%.

  18. Wayne February 25, 2013 at 2:41 pm #

    The script can be further simplified.

    var threads = GmailApp.search(“label:delete-me older_than:2d”, 0, 100);
    GmailApp.moveThreadsToTrash(threads);

    The moveThreadToTrash can only do 100 threads at a time, but after the initial run this shouldn’t be an issue if the script is run hourly.

    • johneday February 25, 2013 at 3:33 pm #

      I didn’t see anything in the GmailThread Class referencing a maximum length. Are all of its methods limited to 100 threads?

      • Wayne February 25, 2013 at 4:34 pm #

        I’m not sure if all are limited to 100 threads, but when I tried to apply on more than 100 I received an error stating that you could only use that method on 100 threads.

  19. Andrew February 25, 2013 at 4:40 pm #

    If I am logged in to multiple gmail accounts, will this run on all of them?

  20. Alex February 26, 2013 at 4:42 pm #

    Thanks for making this. It will be very handy!

    I modified it so that it would Archive (rather than delete) the label “AutoArchive”. I have made the AutoArchive label in Gmail and am using it on a couple of test emails that have been in my inbox for months.

    To do this I modified lines 7 and 12 so that they now read:

    Line 7: var label = GmailApp.getUserLabelByName(“AutoArchive”);

    Line 12: threads[i].moveToArchive();

    When I run the code it doesn’t seem to actually work. It says “Running function cleanUp…Cancel/Dismiss” at the top of the page and does not go away.

    Any suggestions?

    Thanks,
    Alex

    • johneday February 27, 2013 at 10:58 pm #

      Hi Alex. Either submit your script on the contact page or get in touch via Skype. We will sort it out.

  21. MG February 26, 2013 at 4:51 pm #

    Hi,

    Awesome script but when I run it, I get an “Exceeded maximum execution time” error. How can this be resolved?

    Thanks,
    Marc

    • ZCMC February 26, 2013 at 6:56 pm #

      I use the Lab smart labels: http://pastebin.com/1GRkAK1j

    • johneday February 27, 2013 at 12:08 am #

      Hi Marc. I have updated the article to include several examples of how to process the threads in batches. Tell me which function works the best for you.

      • MG February 27, 2013 at 7:06 pm #

        Hi,

        Should I create a separate script for this function or include it in the original?

        Also, getting an illegal character on line 3 (sorry, noob as well).

        Thanks,
        Marc

        • johneday February 27, 2013 at 10:45 pm #

          You should replace the function in your original script with one of the batch functions.

          • MG February 27, 2013 at 11:56 pm #

            Thanks for the quick response! Worked like a charm (I used BatchA).

  22. Phil February 26, 2013 at 7:04 pm #

    Outstanding script. I know zero about scripts, but I followed your directions and it worked first time. However, now I want to modify the length of time the email stays in my inbox before it is transferred to my “Delete Me” box…I know, it’s not a box, it’s a label. Anyway, how do I pull up the script to edit it?
    Thanks again!

    • johneday February 26, 2013 at 8:25 pm #

      Hi Phil. The script is stored in your Google Drive: https://drive.google.com/

      • Phil February 27, 2013 at 5:15 pm #

        Thanks John. I went there, saw the delete me script, but when I clicked on it I got a slideshow showing how to use Google apps.
        Sorry, I am Noobie 3rd Class.

  23. Josh February 27, 2013 at 5:06 pm #

    Hi John. The script worked great for a while (doing 100 at a time) however since I had so many (Thousands of messages), I changed the timer to run every minute.

    It then seems to have worked for some short time after that and then stopped. I can run it manually without errors, but it is not deleting anything.

    Any suggestions?

    • johneday February 27, 2013 at 10:50 pm #

      Hi Josh. I would select an interval of at least five minutes. I have updated the article to include several examples of how to process the threads in batches. Tell me which function works the best for you.

  24. James March 8, 2013 at 8:43 pm #

    I would like to find mail that is “read” and included in label A – then remove label A, and apply label B instead. I couldn’t find anything on google’s help section or anywhere else that shows me how to unapply the first label and replace it with a different one. Of course, reading is fundamental, and maybe I am just so excited to try it, that I am missing it??

    • johneday March 9, 2013 at 3:21 am #

      Hi James. Try this: http://bpaste.net/show/ZS93uCPnN33zmnUPQx9U/

      function changeLabels() {
      var batchSize = 100 // Process up to 100 threads at once
      var oldLabel = GmailApp.getUserLabelByName(“old label”);
      var newLabel = GmailApp.getUserLabelByName(“new label”);
      var threads = GmailApp.search(‘label:”scripts” is:read’);
      for (j = 0; j < threads.length; j+=batchSize) {
      newLabel.addToThreads(threads.slice(j, j+batchSize));
      oldLabel.removeFromThreads(threads.slice(j, j+batchSize));
      }
      }

      • James April 6, 2013 at 1:20 am #

        Hi,

        I am having the following issue, and since I’m just barely starting to understand how scripting works, I’m at a loss to know what I’ve done incorrectly below. Any suggestions?

        Your script, AUTO ARCHIVE TV, has recently failed to finish successfully. A summary of the failure(s) is shown below. To configure the triggers for this script, or change your setting for receiving future failure notifications, click here.

        Summary:

        Error Message Count
        TypeError: Cannot find function getuserLabelByName in object GmailApp. (line 4, file “Code”) 24

        function changeLabels() {
        // Every thread in your TV label that is already read.
        var batchSize = 100 // Process up to 100 threads at once
        var oldLabel = GmailApp.getuserLabelByName(“TV”);
        var newLabel = GmailApp.getuserLabelByName(“ARCHIVES/TV RATINGS”);
        var threads = GmailApp.search(‘label:”scripts” is:read’);
        for (j = 0; j < threads.length; j+=batchSize) {
        newLabel.addToThreads(threads.slice(j, j+batchSize));
        oldLabel.removeFromThreads(threads.slice(j, j+batchSize));
        }
        }

        • johneday April 6, 2013 at 3:11 am #

          The “scripts” label was just a placeholder and should have been filled in with the value of your oldLabel. Tell me if you have more luck with THIS:

          function changeLabelsB() {
          var batchSize = 100 // Process up to 100 threads at once
          var oldLabel = GmailApp.getUserLabelByName("TV");
          var newLabel = GmailApp.getUserLabelByName("ARCHIVES/TV RATINGS");
          var threads = GmailApp.search('label:"TV" is:read');
          for (j = 0; j < threads.length; j+=batchSize) {
          newLabel.addToThreads(threads.slice(j, j+batchSize));
          oldLabel.removeFromThreads(threads.slice(j, j+batchSize));
          }
          }

          • TJ Luoma April 16, 2013 at 9:21 pm #

            I was trying to adapt this to create a script which would automatically remove a label (without adding a new one) if the message was 1) read and 2) older than 12 hours.

            The label is @SaneLater and I’m not sure if it was because of the @ sign or what but it would not work until I tried it without the “quotes” around the label in the ‘var threads’ line.

            This is what I finally came up with that seems to work:

            function removeSaneLaterLabel() {
            var batchSize = 100 // Process up to 100 threads at once
            var oldLabel = GmailApp.getUserLabelByName(“@SaneLater”);
            var threads = GmailApp.search(‘is:read older_than:12h label:@SaneLater’);
            for (j = 0; j < threads.length; j+=batchSize) {
            oldLabel.removeFromThreads(threads.slice(j, j+batchSize));
            }
            }

  25. Ruslan March 12, 2013 at 10:54 am #

    Thanks again for excellent idea

    This works for me without getting this exceeding eror

    function Clean_Delete_Me() {
    var delayDays = 7 // Enter # of days before messages are moved to trash
    var myLabelName = “delete me” // label name

    var maxDate = new Date();
    maxDate.setDate(maxDate.getDate()-delayDays);

    var label = GmailApp.getUserLabelByName(myLabelName);
    var threads = label.getThreads();

    for (var j=0; j< parseInt(threads/100)+1; j++) {
    for (var i = 0; i < threads.length; i++) {
    if (threads[i].getLastMessageDate()<maxDate)
    {
    threads[i].moveToTrash();
    }
    }
    }
    }

  26. Scott Jones March 22, 2013 at 2:08 am #

    This is a great tip.

    Am I reading correctly that you can’t create a script that archives unstarred messages? I see a search operator for starred messages, and even types of stars, but not one for messages without a star.

    • johneday March 22, 2013 at 2:42 am #

      Thanks Scott. You can search for unstarred threads with -is:starred. For example:

      var threads = GmailApp.search(‘label:inbox is:read older_than:1d -is:starred -label:”delete me”‘);

      • hucknbid May 23, 2013 at 2:57 pm #

        The only issue I noticed is that if ANY of the messages in a thread are unstarred, it will show up in a search when “-is:starred” is used.

        Meaning that if I have a 20 message email thread, I’ve gotta click each star individually to get it to not show up in this search. Hitting the star from the inbox seems to only apply a start to the most recent message, not each message in the thread.

        Is there any way to change the syntax to avoid this?
        My kludge was to create a “keep” label for messages that I want quick at hand, but aren’t needed in my section of messages awaiting responses. I then excluded that from being archived in any other scripts. My only complaint is that applying the label takes a bit longer than clicking the star. First world problem, I know.

        This article is incredibly helpful though! Thank you!

        • Ricky April 22, 2014 at 3:44 am #

          I second this problem. It seems the ‘-is:starred’ syntax doesn’t work on email conversation threads, meaning that if an email has multiple replies, then it won’t work. Each reply itself is technically a new email, but since Gmail groups conversations together in one message, it’s a bit tricky to find a solution. The only solution I can think of is apply a star to every single reply in that email, but then that would be pointless since I personally star the messages to indicate their importance and if I star an email with 100+ replies then it would be a waste of time.

          • Ricky April 22, 2014 at 5:06 am #

            I think I got it to work…..

            The obvious solution here is to disable the conversation view in the settings. But this would make your inbox even more cluttered, because each individual email reply will be treated as a new email. So if you have an email with 50 replies in one message and you turn off the conversation view, then you’ll have to deal with 50 emails in your inbox instead of just 1.

            For those that are OK with this problem, then this will work because each email can be starred properly, so that when you run the “-is:starred” search function, it will only return emails that are not starred.

            But for me, I’d rather keep the conversation view. So the workaround solution is to use the Important Marker. It’s the yellow arrow icon next to the star.

            Simply add this to the filter:

            “-is:important -is:starred”

            without the quotes.

            For some reason, this worked quite well. For an email message that has 50 replies in it but only has one star added to the email, then doing a search on “-is:starred” will still show this message in the search list. BUT if it also has an important marker added to it, then it won’t show up, which is what I want.

            I guess the reason it works is that Gmail links those 2 functions with the “AND” operator, so that the email has to have both of those criterias to be searched for (which in our case is to NOT search for them).

  27. Roni Benjamini April 8, 2013 at 1:58 am #

    Hi johneday,

    very nice work.

    I use the script to clean out my inbox daily by archiving to a monthly folder which I review later to make sure I havent missed anything critical.

    I’d like to set up a second script to randomly pick 25 messages from the monthly archive folder and repopulate them back into my inbox to save time on the review process as if a new email has come in.

    I’m not sure how to generate the code to pick randomly. Any help would be greatly appreciated.

    Thank you,

    rb

  28. Formation google apps April 22, 2013 at 1:42 pm #

    Thanks for the information, very interesting post !

  29. Oswald April 25, 2013 at 9:20 am #

    Hello John,

    The script worked great without any issues, but now I keep getting a summary of failures from Google Apps Script. This is the error.

    Cannot connect to Gmail (line 5, file “Code”)

    thanks for your time and effort.

    Os

  30. Jacey May 19, 2013 at 7:16 pm #

    Hi John,

    thanks for this article and script. I’ve never used a script before, but always knew they existed to get things to work the way we want. This is definitely helping to clear out my inbox of all those daily subscription emails.

    I am running into trouble with sub-labels. I have one script function for the label “daily deals” and it works great. But the way I’ve organized my blog emails, is that I have one main label “Blogs”, and then each blog title as a sub-label. When I try to run this script using the sub-label name, I get an error message “TypeError: Cannot call method “getThreads” of null. (line 6″

    Any suggestions?

    • johneday May 19, 2013 at 11:48 pm #

      Hi Jacey. Try:
      var label = GmailApp.getUserLabelByName(“Blogs/sublabel”);

  31. Jim May 21, 2013 at 4:19 pm #

    Is there a variable to remove a label from an email. I have your script working to delete the messages with the label “delete me” but rather than delete the message I just want to remove the label delete me

    Thanks

  32. Richard May 30, 2013 at 6:15 am #

    I wonder if the cleanUp function will work with the new categories feature they announced yesterday; move promotions to trash after 30 days for example

    • Richard May 30, 2013 at 3:43 pm #

      The following will move mail in the promotions category to the trash if older than 30 days

      function batchDeletePromotions() {
      var batchSize = 100 // Process up to 100 threads at once
      var threads = GmailApp.search(‘category:promotions older_than:30d’);
      for (j = 0; j < threads.length; j+=batchSize) {
      GmailApp.moveThreadsToTrash(threads.slice(j, j+batchSize));
      }
      }

      • johneday June 3, 2013 at 11:22 pm #

        Thanks Richard, I will update the article. I am sure that will be useful with the new functionality.

        • Richard June 10, 2013 at 5:17 am #

          Hi John,

          I have also adapted your batch delete multipleLabels function to batch archive mail from the other categories after 1 month

          function batchArchiveCategories() {
          var myCategories = {‘”updates”‘:”1m”,’”forums”‘:”1m”,’”social”‘:”1m”};
          var batchSize = 100; // Process up to 100 threads at once
          for(aCategory in myCategories) {
          var threads = GmailApp.search(‘label:inbox category:’+aCategory+’ older_than:’+myCategories[aCategory]+”);
          for (j = 0; j < threads.length; j+=batchSize) {
          GmailApp.moveThreadsToArchive(threads.slice(j,j+batchSize));
          }
          }
          }

      • Ashlyn October 15, 2013 at 9:16 am #

        This is exactly what I have been looking for!! I am getting this error on line 4: Illegal character. (line 4, file “Code”)

        using this exact text:
        function myFunction() {
        function batchDeletePromotions() {
        var batchSize = 100 // Process up to 100 threads at once
        var threads = GmailApp.search(‘category:promotions older_than:30d’);
        for (j = 0; j < threads.length; j+=batchSize) {
        GmailApp.moveThreadsToTrash(threads.slice(j, j+batchSize));
        }
        }

        Any suggestions?
        Thanks!!

        • Sarah R. November 1, 2013 at 12:54 pm #

          I am getting the exact same error as Ashlyn; did anyone have a suggestion on how to fix?

          • Sarah R. November 1, 2013 at 3:27 pm #

            Found a fix by playing around:
            function batchDeletePromotions() {
            var batchSize = 100 // Process up to 100 threads at once
            var threads = GmailApp.search(“category:promotions older_than:30d”);
            for (j = 0; j < threads.length; j+=batchSize) {
            GmailApp.moveThreadsToTrash(threads.slice(j, j+batchSize));
            }
            }

            The line 4 error was remedied by inserting double quotes instead of single quotes in the search string.

  33. James June 2, 2013 at 5:53 pm #

    How do you deal with imap folders generated from devices. I have an [imap]/trash folder that just sits there not trashing emails I delete on devices.

    I have tried the following, but have not had any positive results.

    var myLabels = {‘”Purge”‘: “10d”, ‘”Deleted Messages”‘: “1d”, ‘”[imap]-trash”‘: “2d”, ‘”[imap]/trash”‘: “1d”};

    My Purge Filter and Deleted Messages filter work great. But the Pesky [imap]/trash label is a stubborn one.

    Thanks

    • johneday June 3, 2013 at 11:20 pm #

      Hi James. Try removing the double quotes:
      var myLabels = {'"Purge"': "10d", '"Deleted Messages"': "1d", '[imap]/trash': "1d"};

  34. Erica Ridley June 7, 2013 at 6:27 pm #

    This was brilliant!! I had no idea it could do this, and you had me auto-archiving within seconds. Now I’ll be playing with google scripts all day!

  35. D. James June 19, 2013 at 12:53 pm #

    Thank you SOOOO much for posting this. I’ve already removed ~700 emails that had been clogging up my inbox. Now I’m trying to modify this slightly to move items into a user-created folder, in this case named “Coupons”. But I keep getting an error:

    TypeError: Cannot find function moveTocoupons in object GmailThread. (line 10)

    What I have entered in line 10 is simply:

    threads[i].moveTocoupons();

    I’ve tried all sorts of variations using parentheses and/or quotation marks, but nothing has worked. Any suggestions?

  36. bhoomi August 16, 2013 at 2:32 pm #

    Can anyone please let me know, how do I set the script so that my gmail inbox gets deleted after every one hour in a day?? This is quite urgent,related to a project in my company. I would really appreciate a fast response. Thanks alot!!

    • johneday August 16, 2013 at 3:45 pm #

      Hi bhoomi. Follow the instructions to create an hourly trigger and try this script:

      function deleteInbox() {
      var batchSize = 100 // Process up to 100 threads at once
      while (GmailApp.getInboxThreads(0, 1).length == 1)
      {
      GmailApp.moveThreadsToTrash(GmailApp.getInboxThreads(0, batchSize));
      }
      }

  37. EricS August 18, 2013 at 12:45 pm #

    This filter is great. How can I adapt this to work with the new Gmail categories? I want to be able to autodelete my category:promotions older_than:3m. I have been manually typing in this with search bar every couple weeks. Thanks.

    • Richard September 9, 2013 at 2:06 am #

      Hi Eric, try this:

      function batchDeletePromotions() {
      var batchSize = 100 // Process up to 100 threads at once
      var threads = GmailApp.search(‘category:promotions older_than:3m’);
      for (j = 0; j < threads.length; j+=batchSize) {
      GmailApp.moveThreadsToTrash(threads.slice(j, j+batchSize));
      }
      }

      • Frank November 27, 2013 at 2:41 pm #

        Thank you all for your help, but for the life of me I cannot get this to work. When I copy and paste the above code I get “Illegal character, (line 3, file “Code”). No matter what I do, I can’t get it to work.

        Help me kill old promotional email!

        • johneday November 28, 2013 at 10:57 am #

          Hi Frank. I have updated the post to include a batchDeletePromotions function.

  38. jorge costa August 26, 2013 at 11:02 am #

    hi,
    thanks very much for the script!
    it’s just the function i needed!
    best regards.

    jorge costa
    Portugal

  39. Matthew Desario September 23, 2013 at 11:51 pm #

    Is it possible to modify this script to delete/label/archive all emails sent or received on a specific day of the week? So we have 2 people covering a voice mailbox. Both get a copy of the email with voicemail via Google Groups. I want one user to automatically delete these emails on Wednesdays when he isn’t in the office, and the other user to delete them on every day except Wednesday. Is this possible at all?

    I know it’s beyond the scope of your initial blog post John but if you could help I would really appreciate it. I believe you’ll have my contact info from this post.

    • johneday September 25, 2013 at 12:28 am #

      Hi Matthew. You can try something like this:

      function trashWednesday() {
      var threads = GmailApp.getInboxThreads();
      for (var i = 0; i < threads.length; i++) {
      var latestMsgDate = threads[i].getLastMessageDate();
      if (latestMsgDate.getDay() == 3)
      {
      threads[i].moveToTrash();
      }
      }
      }

  40. Janine October 23, 2013 at 1:58 am #

    I just wanted to say – this is brilliant. So simple and so effective. Thanks!

  41. Ken November 4, 2013 at 10:41 pm #

    John, many thanks for this – I know nothing about scripting but have got this working on 4 different mail accounts. However I have a 5th account and I don’t understand what I have done differently to the other ones where the script works perfectly. I get the error message
    “Service invoked too many times in a short time: gmail rateMax. Try Utilities.sleep(1000) between calls.

    I have put the script together based on your instructions (but of course may have done something wrong). I copied it from one account where it worked to another account and it gives the error.

    The script looks like this on my account – please advise if you can see why I get the error.

    function cleanUp() {
    var delayDays = 2 // Enter # of days before messages are moved to trash
    var maxDate = new Date();
    maxDate.setDate(maxDate.getDate()-delayDays);
    var label = GmailApp.getUserLabelByName(“delete me”);
    var threads = label.getThreads();
    for (var i = 0; i < threads.length; i++) {
    if (threads[i].getLastMessageDate()<maxDate)
    {
    threads[i].moveToTrash();
    }
    }
    }

    function batchArchiveA() {
    var batchSize = 100 // Process up to 100 threads at once
    var threads = GmailApp.search('label:inbox is:read older_than:7d -label:"delete me"');
    for (j = 0; j < threads.length; j+=batchSize) {
    GmailApp.moveThreadsToArchive(threads.slice(j, j+batchSize));
    }
    }

    function batchArchiveB() {
    var mySearch = "'label:inbox is:read older_than:7d -label:\"delete me\"'"
    var batchSize = 100 // Process up to 100 threads at once
    while (GmailApp.search(mySearch, 0, 1).length == 1)
    {
    GmailApp.moveThreadsToTrash(GmailApp.search(mySearch, 0, batchSize));
    }
    }

  42. redzep November 10, 2013 at 1:25 pm #

    Have to chime in and add my profound thanks. Whenever someone comes up with a tip to automated repetitive drudgery, it’s seconds, minutes, hours of life given back. I kid you not!

  43. bksathesh November 22, 2013 at 11:03 pm #

    Hi John, I am a little concerned with the script. I’m a total noob and have no idea about scripting. I don’t intend to delete my mails but archive them into a specific label. I am also unable to get the graphical interface as the 2nd image which has the Current Project Triggers.

    Any little advise, would be most appreciated. Thank you.

    • johneday November 23, 2013 at 3:52 pm #

      Hi bksathesh. Create your new label in gmail and then paste it into the labelName variable below. Good luck.

      function label_archive() {
      var labelName = "Insert Label Name"
      var myLabel = GmailApp.getUserLabelByName(labelName);

      var batchSize = 100 // Process up to 100 threads at once
      var threads = GmailApp.search('label:inbox is:read older_than:1d');
      for (j = 0; j < threads.length; j+=batchSize) {
      myLabel.addToThreads(threads.slice(j, j+batchSize));
      GmailApp.moveThreadsToArchive(threads.slice(j, j+batchSize));
      }
      }

    • btreu December 10, 2013 at 10:34 am #

      In my case, I’m sorting about 13,000 threads (very overdue for cleanup), and I noticed that the script would cut off at around 275 threads. My script excludes threads that are unread, starred, and that I’ve added to the label “NO_DELETE”. I corrected my issue by telling the script to exclude emails that already have the destination label – in my case, the label “TEST”. I’m not sure if this will help anyone else, but thanks to everyone who posted before me, especially johneday!

      My script, based on johneday’s example:

      function labelTest() {
      var batchSize = 100 // Process up to 100 threads at once
      var threads = GmailApp.search(‘label:all is:read older_than:2d -is:starred -label:NO_DELETE -label:TEST’); // Every thread in “all” mail that is read, older than 2 days, not starred, not labeled “NO_DELETE”, and not already labeled “TEST”.
      var myLabel = GmailApp.getUserLabelByName(“TEST”);

      for (j = 0; j < threads.length; j+=batchSize) {
      myLabel.addToThreads(threads.slice(j, j+batchSize));
      }
      }

  44. Paul Gomez December 20, 2013 at 5:00 pm #

    Thanks for this! I’ve been looking for a way to expire messages from my mailbox and this is perfect. I’ve made a few changes to it to allow the label to drive the expiration period, but this is awesome. Thanks again!

  45. Mike December 27, 2013 at 2:45 pm #

    I am new to scripting as well.

    How do I set the script so that my gmail TRASH gets deleted after x amount of time?

    • johneday December 27, 2013 at 2:56 pm #

      Hi Mike. I don’t believe you can do that.

  46. JoshInGeneral January 4, 2014 at 10:39 am #

    Thank you so much for this, you are going to save me days of productivity!

  47. Vy January 9, 2014 at 2:05 am #

    This is wonderful John, a big lifesaver.

    Can you help me set the auto cleanup a little differently? I have emails in label:A and a few in both label:A and label:B. In my clean up, I would like to delete emails that are only label:A, but save the ones both in label:A and label:B. I can’t seem to alter the script to fit that description, hopefully you can help. Thank you.

    • johneday February 26, 2014 at 12:30 am #

      Try this:
      var threads = GmailApp.search('label:"label A" -label:"label B"');

  48. mrsklb January 12, 2014 at 8:24 pm #

    Hi, first off this is awesome! I’m in to coding at the moment :)

    2ndly, I want to create a code with nested functions, when I ran the following only the “Move to Trash” worked, not the Archive. As you will see the Archive is first in the list so it doesn’t make sense. I received no error details.

    I have them set up in 1 project with 2 different scripts so that they work. Ideally I want them on the one script page.

    The Trash function is to take all (regardless of the location) messages +21 days and trash them with the specific label.

    I want to run similar functions over 2-3 scripts depending on my label.

    CODE
    function archiveInbox() {
    // Every thread in your Inbox that is older than two days
    var threads = GmailApp.search(‘label:”AutoGenerated emails/Login Test” older_than:1d’);
    GmailApp.markThreadsRead(threads);
    for (var i = 0; i < threads.length; i++) {
    threads[i].moveToArchive();
    }
    }

    function cleanUp() {
    var delayDays = 21 // Enter # of days before messages are moved to trash
    var maxDate = new Date();
    maxDate.setDate(maxDate.getDate()-delayDays);
    var label = GmailApp.getUserLabelByName("AutoGenerated emails/Login Test");
    var threads = label.getThreads();
    for (var i = 0; i < threads.length; i++) {
    if (threads[i].getLastMessageDate()<maxDate)
    {
    threads[i].moveToTrash();
    }
    }
    }

    /CODE

    Any advice would be super, thank you.

  49. parshand January 22, 2014 at 5:57 am #

    Made a few changes. It executes but nothing’s happening to the messages in the ‘To the Archives’ label
    Any suggestions?

    function Archive() {
    var delayDays = 0 // Enter # of days before messages are moved to trash
    var maxDate = new Date();
    maxDate.setDate(maxDate.getDate()-delayDays);
    var label = GmailApp.getUserLabelByName(“To the Archives”);
    var threads = label.getThreads();
    for (var i = 0; i < threads.length; i++) {
    if (threads[i].getLastMessageDate()<=maxDate)
    {
    threads[i].moveToArchive();
    }
    }
    }

  50. Phillip February 4, 2014 at 12:51 am #

    This is truly awesome. This combined with a script that automatically downloads attachments to gmail has already made my analytics reporting periods(And therefore my life) SO much easier. This is icing on the cake. Plus now I found an awesome blog that I am going to haunt.

  51. johan February 13, 2014 at 1:07 pm #

    Counting labels.

    It looks like you are a clever guy.

    I need some stats from my gmail.

    I receive enquiries and label them enquiries.

    I want to count how many enquiries I get a day.

    When I only select “conversation view off” I can see all the emails with enquiry tab.

    But then I need to go and count then for 365 days to get some stats.

    This is a real pain.

    Is there a way to do this?

    • johneday February 26, 2014 at 12:41 am #

      Hi Johan. If you would like to discuss a project, please send me your contact information in the custom development page above.

  52. Sam February 17, 2014 at 11:15 am #

    This is great – I’ve been using it for months, but it has suddently stopped working. Any advice?

    • johneday February 23, 2014 at 8:15 am #

      Rob G. fixed this problem by deleting the script and creating a new one.

  53. Evan February 19, 2014 at 8:01 am #

    Hi John,

    I copied the script line by line and was getting the error I saw many times above, so I added the delete me label and ran it, but it isn’t doing anything and the authorize pop up isn’t showing anymore. What am I doing wrong?

  54. Rob G February 22, 2014 at 12:49 pm #

    I installed this script a few months ago, and it works perfectly but now less than a month later it wont run?

    • Rob G February 23, 2014 at 6:24 pm #

      I deleted the script and created a new one with the same language from the original one, but this time when I went to run it, Gmail asked for authorization. Which I granted, and then it worked. Just thought I would let you know.

      From looking at some of the other recent posts, it failing to ask for authorization any more might be the issue.

      • johneday February 23, 2014 at 6:59 pm #

        Thanks for the update Rob.

  55. mikecal85 February 25, 2014 at 7:09 pm #

    I’m trying (and failing) to create a script that either:

    (1) finds all muted email threads and marks conversation as read

    OR

    (2) finds all muted email threads and deletes a specific label from that thread

    The script needs to run every hour or so. Please help!

    • johneday February 25, 2014 at 11:45 pm #

      Hi Mike. I have updated the post with a markMutedThreadsRead function.

      • mikecal85 February 26, 2014 at 12:48 pm #

        Works (almost) perfectly!

        I had to delete the following line, it gave me an error:

        threadLabel.removeFromThreads(threads.slice(j, j+batchSize));

        Once I did, though, it worked. Thank you so much!

        • johneday February 26, 2014 at 12:52 pm #

          Did you remember to change “my label” to the label you were trying to remove?
          var threadLabel = GmailApp.getUserLabelByName("my label");

  56. user0 February 27, 2014 at 3:03 pm #

    batchArchiveB() above deletes emails.

    • johneday February 27, 2014 at 5:14 pm #

      Good catch. Thanks.

  57. Alison March 3, 2014 at 11:35 am #

    Have you thought of publishing this as a Lab for gmail?

  58. Konstantin Dokuchaev April 24, 2014 at 1:17 am #

    Hi. Is it possible to delete all messages between 2 and 6 pm?

  59. Rob D April 28, 2014 at 5:37 pm #

    Hi John

    Thanks for these scripts.

    I would like to delete the “category” applied to some of my message threads.

    For a nomal label I use

    var myLabel = GmailApp.getUserLabelByName(“”)
    … // eg search for threads with label:”” + other conditions ..

    threads[i].removeLabel(myLabel);

    But if I then try the above logic for the inbox categories …
    e.g.
    var myLabel = GmailApp.getUserLabelByName(“promotions”)
    this returns NULL as the value for myLabel

    Any idea how I can remove a category from a thread ?
    It seems as though the only support for categories in gmail scripting is searching via the GmailApp.search() method??

    thanks

    Rob

  60. Joffrey May 2, 2014 at 3:01 pm #

    Hi,

    The delayDays is in term of days, so is maxDate and the Google API.

    But what if I want to look at a condition for hours (i.e. take action if a message hasn’t been looked at in the past hour)?

    How would I adapt the time difference to be in mins or hours?

  61. Nathan May 17, 2014 at 6:11 am #

    Hi there,

    This is nearly exactly what I am looking for.

    I currently get emails sent to me hourly from my Raspberry Pi informing me of its external IP address so I can access it away from home if necessary, obviously this means I get 24 email per day sent to me from my RPi. I would like to set up this script so that it deletes all of the emails with the ‘Pi IP’ label that are older than 1 hour (so instead of integers of days I entered 0.04167 which is the decimal for 1 hour of a day) however the script does not delete the email.

    Does anyone have any insight as how I can fix this?

    Nathan

  62. Lisa Biesininger May 24, 2014 at 7:34 am #

    Thank you! I love the new gmail tabs, but they do collect a lot of junk that is irrelevant after a day or two. This will let me clean them up. Yay!!

  63. danr June 11, 2014 at 10:07 pm #

    Great script, thank you for sharing.
    Question, is it possible to move to trash individual messages instead of the entire thread?

    Where do I go to learn more about how to do this scripting?

    Thank you.

  64. google plus android apps June 17, 2014 at 11:57 pm #

    Oh my goodness! Amazing article dude! Thanks, However I am going through issues with your RSS.
    I don’t understand the reason why I cannot subscribe to it.
    Is there anybody else having the same RSS problems?

    Anyone who knows the answer will you kindly respond?
    Thanx!!

Trackbacks/Pingbacks

  1. Automatically Clean Up Gmail On A Schedule With This Script | Lifehacker Australia - February 22, 2013

    [...] Create time-based Gmail filters with Google Apps Script [Johneday via adayzdone] [...]

  2. Automatically Clean Up Gmail on a Schedule with This Script | Tips for the Unready - February 23, 2013

    [...] Create time-based Gmail filters with Google Apps Script | Johneday via adayzdone [...]

  3. Automatically Clean Up Gmail on a Schedule with This Script | Rob's Personal Aggregator - February 23, 2013

    [...] Create time-based Gmail filters with Google Apps Script | Johneday via adayzdone Tweet [...]

  4. Automatically Clean Up Gmail on a Schedule with This Script - February 23, 2013

    [...] Create time-based Gmail filters with Google Apps Script | Johneday via adayzdone [...]

  5. Zaplanuj automatyczne kasowanie odłożonych i zapomnianych wiadomości dzięki Google Apps Script | AntyWeb - February 28, 2013

    [...] tracą swoją „ważność”. W takich przypadka znakomicie sprawdził mi się ten skrypt, który dodajemy do swojego Gmaila, wchodząc na Google Apps [...]

  6. Have You Heard Of?… | Abiding Marriage - March 1, 2013

    [...] Time-based Gmail filters: Another one I found through Lifehacker. I used this website to create a Gmail filter that takes emails from certain senders, applies a label to them, and then runs an hourly check to delete any emails with this label older than 30 days. This is useful for emails that quickly become stale, like those Evites, Groupon deals, etc. [...]

  7. Un filtro de Gmail para borrar correos automáticamente - March 4, 2013

    [...] Para Mayor Información haz Click Aquí [...]

  8. Email Tips: Clean Up Your Inbox With A Google Apps Script | V3 Kansas City Integrated Marketing and Social Media Agency - March 5, 2013

    [...] inbox is anything like mine, well, it’s a jungle. Ready to tidy things up a bit? We stumbled on a Google Apps script that can help you better manage your Gmail inbox—no apps or extensions [...]

  9. Email Tips: Clean Up Your Inbox With A Google Apps Script | V3 Kansas City Integrated Marketing and Social Media Agency | Tisel Milan Vukovic - March 5, 2013

    [...] A quick note? If you want to clean up thousands of email messages, know that it will take time for the script to fully work. If you’d rather tackle your inbox in smaller batches, follow John’s steps to process email threads in groups of 100. [...]

  10. LPT: Use Google Script with your Gmail account to automate time-delayed cleanup of your incoming mail. Great for auto-archiving messages that are only useful when fresh (notifications, sales promos, statements, etc.). Instructions inside. | Awesome F - September 29, 2013

    [...] Here's a page with full instructions, but here are the essentials: [...]

  11. Awesome Things You Can Do With Google Scripts | Nickinfotech - February 14, 2014

    […] Gmail Clean-up - Create time-based filters in Gmail that will automatically move, archive or even delete all messages from any particular Gmail label that are older than “n” days. Written by John E. Day. […]

Leave a Reply