DEV Community

Scott Windon
Scott Windon

Posted on

Turn Gmail into a Free Serverless Email API in 5 Minutes!

Every side project or simple client build ends up needing the exact same thing: a dead-simple way to send transactional emails.

Whether it is a contact form on a static site, a failure alert from a cron job, or a notification ping from a small internal tool, you almost always need an API endpoint to fire off an email.

The problem is the modern email API landscape for small projects has gotten annoying:

  • Big providers now want credit cards upfront just to touch their free tier.
  • Setup involves domain DNS verifications, DKIM, SPF records, and waiting on sandbox approvals.
  • You end up managing API keys, dependencies, and complex SDKs just to send 5 emails a day.

If you already have a Google account, you can build and host your own secure REST email API for free in about 80 lines of Google Apps Script. No credit card, no server maintenance, and no domain DNS config required.

Here is what it is, how it works, what Google gives you for free, and how to set it up.


What It Is

It is a lightweight Google Apps Script deployed as a serverless Web App that exposes an authenticated HTTP POST endpoint.

When your backend or serverless function sends a JSON payload to the endpoint, the script validates a bearer token stored in your Google Script Properties and sends the email straight from your Gmail or Google Workspace account using Google's native MailApp service.

It supports:

  • Simple JSON over HTTP POST
  • Token-based bearer authentication
  • Plain text and HTML emails
  • Custom sender display names
  • CC, BCC, and Reply-To headers
  • Returning your remaining daily recipient quota directly in the API response

What You Get for Free

Google Apps Script runs entirely on Google's infrastructure at zero cost. You do not need to supply billing details or provision a cloud instance.

Google provides generous daily email sending quotas based on your account type:

Account Type Daily Email Recipient Quota
Personal Google Account (@gmail.com) 100 recipients / day
Google Workspace (formerly G Suite) 1,500 recipients / day

For a personal project, portfolio contact form, or dev monitoring script, 100 free transactional emails every single day is more than enough. If you run it on an existing Workspace account, 1,500 per day covers plenty of early-stage SaaS applications or internal company tools.

Even better: because the emails are sent directly through Gmail under your authenticated Google account, inbox deliverability is solid right out of the box without configuring custom SMTP servers.


How It Works

The entire API is contained in a single Code.gs file.

Here is the breakdown of the logic:

1. Generating a Secure Token

We store a unique token inside PropertiesService.getScriptProperties(). This acts like an environment variable that persists across executions and is never exposed in the source code or client requests:

const TOKEN_KEY = 'AUTH_TOKEN'

function generateToken() {
  const newToken = Utilities.getUuid() + '-' + Utilities.getUuid()
  PropertiesService.getScriptProperties().setProperty(TOKEN_KEY, newToken)
  Logger.log('Your Auth Token: ' + newToken)
}
Enter fullscreen mode Exit fullscreen mode

Running this function once generates a secure, randomized token and saves it in the script storage.

2. Handling Incoming POST Requests

The entry point for web apps in Google Apps Script is doPost(e). The script parses the incoming JSON, validates the token against the stored property, checks required fields, and sends the message:

function doPost(e) {
  try {
    if (!e.postData || !e.postData.contents) {
      return jsonResponse({ success: false, error: 'Missing request body' }, 400)
    }

    const data = JSON.parse(e.postData.contents)

    // Auth check
    const storedToken = PropertiesService.getScriptProperties().getProperty(TOKEN_KEY)
    if (!storedToken || data.token !== storedToken) {
      return jsonResponse({ success: false, error: 'Invalid or missing auth token' }, 401)
    }

    // Required fields check
    if (!data.to || !data.subject || (!data.body && !data.htmlBody)) {
      return jsonResponse({
        success: false,
        error: 'Missing required fields: to, subject, and body (or htmlBody)'
      }, 400)
    }

    // Build optional parameters
    const options = {}
    if (data.htmlBody) options.htmlBody = data.htmlBody
    if (data.cc) options.cc = data.cc
    if (data.bcc) options.bcc = data.bcc
    if (data.replyTo) options.replyTo = data.replyTo
    if (data.name) options.name = data.name

    // Send the email via Gmail
    MailApp.sendEmail(data.to, data.subject, data.body || '', options)

    // Return success and remaining quota
    return jsonResponse({
      success: true,
      message: 'Email sent successfully',
      remainingQuota: MailApp.getRemainingDailyQuota()
    })

  } catch (err) {
    return jsonResponse({
      success: false,
      error: err.message
    }, 500)
  }
}

function jsonResponse(obj) {
  return ContentService
    .createTextOutput(JSON.stringify(obj, null, 2))
    .setMimeType(ContentService.MimeType.JSON)
}
Enter fullscreen mode Exit fullscreen mode

Notice MailApp.getRemainingDailyQuota(). Returning this in the response lets your calling app monitor your usage without guessing where you stand against Google limits.


5-Minute Setup Guide

Setting this up takes only a few steps:

1. Create the Script

  1. Head over to script.google.com and click New Project.
  2. Replace whatever is in the editor with the Code.gs script above.
  3. Name your project (e.g. Gmail Email API).

2. Generate Your Secret Token

  1. In the Apps Script toolbar, select generateToken from the function dropdown.
  2. Click Run.
  3. Google will ask you to review permissions. Authorize the script to access your Gmail account.
  4. Open the Execution log (View → Execution log) and copy the generated token string. Store this securely in your .env file.

3. Deploy as a Web App

  1. Click Deploy → New deployment (top right).
  2. Choose Web app as the deployment type.
  3. Configure the options:
    • Description: Production v1
    • Execute as: Me (your email address)
    • Who has access: Anyone
  4. Click Deploy and copy your Web app URL. It will look like this:
https://script.google.com/macros/s/AKfycbx.../exec
Enter fullscreen mode Exit fullscreen mode

That is your live REST API endpoint.


Sending an Email

Once deployed, you can trigger emails from any language or framework using a standard HTTP POST request.

cURL

curl -X POST "https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "YOUR_AUTH_TOKEN",
    "to": "dev@example.com",
    "name": "App Monitor",
    "subject": "System Status Update",
    "body": "Daily backup completed with zero errors.",
    "htmlBody": "<h3>System Status Update</h3><p>Daily backup completed with <strong>zero errors</strong>.</p>"
  }'
Enter fullscreen mode Exit fullscreen mode

Node.js / Next.js / Astro

const API_URL = process.env.GMAIL_API_URL
const API_TOKEN = process.env.GMAIL_API_TOKEN

async function sendNotification({ to, subject, body, htmlBody }) {
  const res = await fetch(API_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      token: API_TOKEN,
      to,
      subject,
      body,
      htmlBody,
      name: 'My Service'
    })
  })

  const data = await res.json()
  if (!data.success) {
    throw new Error(data.error || 'Failed to send')
  }

  console.log(`Sent! Remaining quota today: ${data.remainingQuota}`)
  return data
}
Enter fullscreen mode Exit fullscreen mode

Python

import os
import requests

def send_email(to, subject, body):
    res = requests.post(
        os.environ["GMAIL_API_URL"],
        json={
            "token": os.environ["GMAIL_API_TOKEN"],
            "to": to,
            "subject": subject,
            "body": body,
        },
        timeout=15,
    )
    res.raise_for_status()
    return res.json()
Enter fullscreen mode Exit fullscreen mode

Practical Use Cases

This setup is ideal when you want reliable transactional delivery without taking on extra SaaS bills or infrastructure overhead:

  1. Static Site Contact Forms: If you run a static site on Netlify, Vercel, Cloudflare Pages, or GitHub Pages, route your form submission to a serverless backend function that hits your Apps Script endpoint.
  2. Build and Deployment Alerts: Hook it into GitHub Actions, GitLab CI, or custom webhooks to notify your team when a scheduled build or release succeeds or fails.
  3. Server Monitoring & Heartbeats: Drop a quick Python script or curl check on a cheap VPS or Raspberry Pi. If a service drops offline or disk usage hits 90%, fire an alert to your personal inbox.
  4. Internal Micro-Tools & Staging Environments: Great for dev and staging environments where you need real emails to test workflows without burning paid quotas on your production email provider.
  5. Database Backup Reports: Trigger a daily status summary from your Postgres backup scripts.

Limitations and Gotchas to Keep in Mind

A quick reminder on practical constraints:

  • Security rule: Always keep your AUTH_TOKEN on a server or behind an edge function. Never put the token inside client-side JavaScript or a public repo. Anyone who gets that token can send email through your account.
  • Attachments: This basic script handles text and HTML bodies. If you need PDF invoices or zip attachments, you would need to extend the script to accept base64-encoded file data and pass them as blobs to MailApp.
  • HTTP Status Codes in Apps Script: Google Apps Script web apps return HTTP 200 or 302 redirects for most ContentService text outputs. The script passes error status codes in jsonResponse, but you should always inspect the success: true/false key in the returned JSON body rather than relying strictly on the HTTP status code.
  • Bulk Marketing: Do not use this for newsletters or cold outreach. Google will throttle or flag accounts that violate standard Gmail usage policies. Keep this strictly for transactional notifications and alerts.

Wrap Up

If you are sick of configuring third-party email providers for tiny projects, Google Apps Script gives you a dependable, completely free alternative that you can roll out in five minutes.

Check out the full repository and documentation here:

👉 swindon/gmail-email-api on GitHub

Feel free to star it, fork it, or adapt the payload to your own workflow.

Top comments (0)