šŸ“®`MMailApp` for Google Apps Script: GmailApp+MailApp+Gmail API

Max Makhrov
7 min readSep 9, 2022

This article describes the MmailApp library created for Google Apps Scripts. Please see the library documentation here:

Photo by Tim Mossholder on Unsplash

The library lets you send emails, using MailApp, GmailApp, and Gmail API. The library takes care of parameters, so you do not need to care about which method to use.

Problem

I had trouble with my project when I wanted to automatically send emails and suddenly for me weā€™ve reached the daily limit. This is why Iā€™ve decided to make some research and be sure fewer bad things happen in the future.

MailApp OR GmailApp?

Apps Script native methods for sending emails are:

MailApp.sendEmail(...)
GmailApp.sendEmail(...)

They are mostly identical, but GmailApp has an option ā€œfromā€. This option is available for paid Google accounts and lets you to send emails from one of your aliases:

var aliases = GmailApp.getAliases();

Youā€™d say then, letā€™s always use ā€œGmailAppā€. But, thereā€™s an issue with emojis.

šŸ˜­Emojis

If sending an email with emoji in a subject or body, it may show ugly question marks instead. This issue happens when you use GmailApp:

GmailApp.sendEmail(
'test.test@test.com',
'šŸŽ‚Yammy',
'šŸ˜Eat my cake!'
);
/* =>
ļæ½ļæ½ļæ½ļæ½ļæ½ļæ½Yammy ļæ½ļæ½ļæ½ļæ½ļæ½ļæ½Eat my cake!
*/

ļæ½ļæ½ļæ½ļæ½ļæ½ļæ½ is not yummy at all! Thatā€™s why you may want to use MailApp instead.

Now you may agree, that we always should use MailApp but use GmailApp in case we want to send emails from your aliases. Thatā€™s good until you reach the daily email limit and you want to send more.

Quota

You can send ~100 emails/day and ~1500 for paid accounts. This quota has changed and may change in the future. The latest official quotas are here.

According to this source, the quota is for 24 hours:

If you have a quota of 1500/day, and start sending mails at around 3pm and then exhaust that quota at 10pm, you can expect the quota will refresh sometime around 3pm the next day.

You may get and log your daily quota with this code:

/**
* Get and log your daily mail quota
*/
function getRemainingMailQuota() {
var quota = MailApp.getRemainingDailyQuota();
console.log('You can send ~ ' + quota + ' emails this day.');
return quota;
}

My test showed that the use of both ā€œMailAppā€ and ā€œGmailAppā€ reduced my remaining quota. If you want to increase the quota, you may try paid account. And even this is not the guarantee:

After you convert from a free trial account to a paid subscription, your account limits automatically increase when both of the following are true:
* Your domain has cumulatively paid at least USD $100 (or equivalent).
* At least 60 days have passed since reaching that payment threshold.

One way out of this issue is to try an external API and use Apps Script to launch it. I suggest trying Gmail API before you start with other vendors like Amazon SES.

Gmail API

Whatā€™s the limit for the use of Gmail API? I donā€™t know, really. I cannot believe my calculations. According to this:

You have 1,000,000,000 quota units per day.

messages.send = takes 100 units

ā†‘ Does this mean one can send 10,000,000 emails per day? Letā€™s consider also this limit:

Per User Rate Limit 250 quota units per user per second

If I send 2 emails per second, it would be 24*60*60*2 = 172,800 emails per day. Still much better than 1500. Iā€™m not sure about that. Anyways Iā€™ve checked that using Gmail API does not change my daily quota calculated earlier. And it seems to be free.

Ok, how to use Gmail API in Apps Script?

The code sample of this was provided by šŸ¦øTanaike in this report. Iā€™ve taken his code as a starting point.

There are reasons why I did not want to use Gmail API:

  1. Installation is hard. You have to install the Gmail service into the script, and you may also need to install the API as an Advanced Service.
  2. Code is hard. If you see Tanaikeā€™s report, you may notice itā€™s not easy to understand the correct syntax.
  3. Out of Control. I found no way to programmatically indicate what is my remaining quota for Gmail API.

This is why my logic is to try simple methods first: ā€œMailAppā€ or ā€œGmailAppā€. And then if I reach the limit, use Gmail API.

As Iā€™ve said Gmail API uses code that is hard to implement. We are in Gmail API version 1 and it uses this:

raw RFC 2822

I had no idea what is that. RFC 2822 is an ancient standard, the document dated April 2001. RFC stands for ā€œRequest For Commentsā€ and my guess is it was the easiest way to implement the API for Google engineers.

The heavy task was to understand the standard and find a way to use it in Apps Script. Luckily for me, there were many others who faced the same problem. Iā€™ve found this code which helped me a lot. Hats off to šŸ¦øContributors!

RFC 2822 standard was not easy to understand, but appeared you may discover your own message by selecting the ā€œShow Originalā€ option in Gmail:

Open the email inside Gmail, go to 3-dot menu and choose Show Original. (source by šŸ¦øAmit)

Hereā€™s the code to do the same:

function getEmailRfc822() {
var emailId = '18312a64a8cf8746';
var rawContent = Gmail
.Users
.Messages
.get(
"me",
emailId,
{format: 'raw'}).raw;
var stringRfc822 = Utilities
.newBlob(
rawContent,
"message/rfc822").getDataAsString()
console.log(stringRfc822);
}

ā†‘ Thanks to this comment by šŸ¦øRomain Vialard that helped me to figure out this problem. Note: if you struggle with finding the emailId, please use this code:

var messages = Gmail.Users.Messages.list(
"me", {q: "subject:Test Hatter", maxResults: 1}
).messages;
var emailId = messages[0].id;
console.log(emailId);

Solution

The idea was to have a simple way to send emails via Apps Script. Please think of this solution as one possible and indeed not a perfect one. Iā€™ve decided to create a library called:

MMailApp

The method should look like this:

MMailApp.send(options);

I prefer a simple flat object for options:

{
to: 'test1@test.test,test2@test.test',
subject: 'šŸ„øTest Tets',
htmlbody: '<b>šŸ’ŖBold Hello!</b>'
}

ā†‘ In most cases, you may use JSON to pass the parameters. If you want to additionally use inline images or attachments, youā€™ll have to use blobs. Hereā€™s a sample code to get test parameters for blobs:

/**
* this function demonstrates
* how to get blobs by URL
* for your email
*/
function getTestBlobs_() {
var source = 'https://raw.githubusercontent.com/';
var options = {
urls: {
'coolTablesLogo':
source + 'cooltables/pics/main/logos/ct_logo_small.png',
'partyFace':
source + 'Max-Makhrov/myFiles/master/partyface.png'
},
html: "šŸ” inline CoolTables Logo<img src='cid:coolTablesLogo'> image! <br>" +
"Hoooorrraaay! <img src='cid:partyFace'>"
}
var blobsArray = [], blobsObject = {};
var blob;
for (var k in options.urls) {
blob = UrlFetchApp
.fetch(options.urls[k])
.getBlob()
.setName(k);
blobsArray.push(blob);
blobsObject[k] = blob;
}
return {
array: blobsArray, // for attachments
object: blobsObject, // for inline images
htmlBody: options.html
};
}

I also wanted to make different naming conventions possible:

"to" = "recipient" = "recipients"
"htmlbody" = "htmlBody" = "html_body"
etc.

Here are options possible to set:

var options = {
to: 'makhrov.max@gmail.com', // required
cc: 'max0637859167@gmail.com',
bcc: 'max0637859167@gmail.com,makhrov.max@gmail.com',
name: 'šŸŽ©Mad Hatter',
from: 'max0637859167@gmail.com', // must owe alias
subject: 'šŸ„øTest Test Test',
body: 'šŸ’Ŗ body!',
htmlBody: '<b>šŸŽøBold Hello</b><br>How are you?',
noReply: 1,
replyTo: 'test@test.test',
replyToName: 'šŸ¦øStranger', // for Gmail API only
attachments: array, // [blob, blob]
inlineImages: object // {name: blob}
}

Now the solution is documented, weā€™re into one little problem to solve: write the code and create a library!

Gmail API Bonus

Learning how to use Gmail API was hard, but fun. I felt like I can control the process, and noticed some benefits.

When you send an email with Gmail API, youā€™ve got a response like that:

{ "threadId":"18311c4fd396e1ad",
"id":"18311c4fd396e1ad",
"labelIds":["UNREAD","SENT","INBOX"]}

ā†‘ Notice the id here. It is the id of a message you send. What is interesting about Gmail is thereā€™s a unique URL for each email. And ID is a part of the URL. This means the URL for that email is:

https://mail.google.com/mail/u/0/#inbox/18311c4fd396e1ad

You may use this URL to open your sent email.

Compatibility

The final task is to make the code compatible. Different methods may or may not work for all 3 methods:

GmailApp
MailApp
Gmail API

I needed to create a code that will launch each possibility for each method. Iā€™ve compared the result for possible options to create a common code, and hereā€™s the result:

This email Iā€™ve sent to myself with the help of Gmail API

ā†‘ In my final test message, Iā€™ve tried all possibilities.

NoReply for Gmail API

Here are options that work differently:

noReply: 1, // will NOT work for Gmail API
from: test@mail.test // may only work if this email belongs to you

The solution Iā€™ve used for a no-reply option is to offer a generic email address for the reply. This works only for Gmail API:

Reply-To: No Reply <noreply@test.test>

As you see, we also have a valid no-reply option now. And additionally, we may set a name to reply:

{
replyTo: test@mail.test,
replyToName: šŸ•·ļøPeter Parker // Hidden option! For Gmail API only
}

Final Words

Iā€™ve finally resolved all the issues, and even had fun!

Thank you for reading to the end! Here again, is the link to the library:

Mmail App Library ā€” send emails from Apps Script.

--

--

Max Makhrov

Google Sheets Developer, master of Online Accounting