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 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:2d -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.moveThreadsToTrash(GmailApp.search(mySearch, 0, batchSize));
}
}






cool tip! shared it with employees at my company
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
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
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.
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”.
Thanks, I was hoping it was something like that.
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.
Thanks BryBry. Although this will stop some errors, users will not be aware that the script is not functioning properly.
Wow, this is going to clean my inbox right up. Did not know there were scripts for your Inbox. Thanks!
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?
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.
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!
Make sure you create the “delete me” label in Gmail before you run the script.
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
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();
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.
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.
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.
Did you create the “delete me” label in Gmail before you ran the script?
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”)
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
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?
After you remove the ‘delete me’ label, you can either archive the message or leave it in your inbox.
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
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
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
Ack, just realized that one already picks only read threads, lol.
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.
Hi David. Try this: http://bpaste.net/show/s7GpvXOKvu8evZpSGmEd/
Worked like a charm! You’ve been a lot of help, thanks. I can’t wait to wade in deeper with this.
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?
Hi Tom. One script file may contain multiple functions, just remember to set up a trigger for each function.
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.
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!!!!
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%.
Hi Nick. I didn’t notice anything which could be used to accomplish that but you can check within the different services here: https://developers.google.com/apps-script/defaultservices
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.
I didn’t see anything in the GmailThread Class referencing a maximum length. Are all of its methods limited to 100 threads?
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.
If I am logged in to multiple gmail accounts, will this run on all of them?
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
Hi Alex. Either submit your script on the contact page or get in touch via Skype. We will sort it out.
Hi,
Awesome script but when I run it, I get an “Exceeded maximum execution time” error. How can this be resolved?
Thanks,
Marc
I use the Lab smart labels: http://pastebin.com/1GRkAK1j
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.
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
You should replace the function in your original script with one of the batch functions.
Thanks for the quick response! Worked like a charm (I used BatchA).
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!
Hi Phil. The script is stored in your Google Drive: https://drive.google.com/
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.
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?
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.
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??
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));
}
}
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));
}
}
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));
}
}
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));
}
}
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();
}
}
}
}
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.
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”‘);
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!
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
Thanks for the information, very interesting post !
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
Hi Os. Google recommends that you use an exponential backoff to handle these errors. After a quick search, I found Peter Herrmann’s JavaScript implementation for Google Apps Script.
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?
Hi Jacey. Try:
var label = GmailApp.getUserLabelByName(“Blogs/sublabel”);
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
Hi Jim. Try removeLabel(label)