Set up the Google Workspace email API on your VPS: send mail through the Gmail API over HTTPS (port 443), so blocked SMTP ports 25, 465 and 587 no longer get in the way.
This is a guide for using Google Workspace to send mail over HTTPS (port 443), bypassing blocked SMTP ports entirely.
On this page
- How the Google Workspace email API works
- Part 1: Google Cloud Console setup
- Part 2: Google Workspace Admin setup
- Part 3: Sending mail from your VPS (Python)
- Part 4: Node.js equivalent
- Part 5: DNS and deliverability
- Part 6: Limits and gotchas
- Appendix: OAuth flow instead of a service account
- Questions
How the Google Workspace email API works
Instead of connecting to smtp.gmail.com on ports 25/465/587, your server makes authenticated HTTPS requests to https://gmail.googleapis.com. Since port 443 is never blocked, this works on any VPS.
There are two authentication approaches:
- Service account + domain-wide delegation. Best for: servers, cron jobs, apps (headless). Recommended for VPS use. No browser needed, no token refresh headaches. Requires Workspace admin access.
- OAuth 2.0 user consent. Best for: desktop apps, scripts run by a human. Requires a one-time browser login and storing a refresh token.
This guide focuses on the service account approach since it suits unattended server use, with the OAuth flow covered briefly at the end.
Part 1: Google Cloud Console setup
1.1 Create a project
- Go to console.cloud.google.com.
- Click the project dropdown (top left), then New Project.
- Name it something like
vps-mailerand create it.
1.2 Enable the Gmail API
- With your project selected, go to APIs & Services › Library.
- Search for Gmail API.
- Click it, then click Enable.
1.3 Create a service account
- Go to APIs & Services › Credentials.
- Click Create Credentials › Service account.
- Name it (e.g.
mailer), click Create and Continue. - Skip the optional role/access steps, click Done.
- Click the new service account, go to the Keys tab.
- Add Key › Create new key › JSON, then download the file.
Keep this JSON file secret. It is effectively a password.
1.4 Note the client ID
On the service account's Details tab, copy the Unique ID (a long number). You need it for the next step.
Part 2: Google Workspace Admin setup
Domain-wide delegation lets the service account send mail as a real user in your domain (e.g. noreply@yourdomain.com).
- Go to admin.google.com as a super admin.
- Navigate to Security › Access and data control › API controls.
- Click Manage Domain Wide Delegation.
- Click Add new.
- Paste the service account's Client ID (the unique ID from step 1.4).
- In OAuth scopes, enter:
https://www.googleapis.com/auth/gmail.send - Click Authorize.
Use only the gmail.send scope unless you also need to read mail. Narrow scopes limit damage if the key leaks.
The user you impersonate (e.g. noreply@yourdomain.com) must be a real Workspace user (or an alias of one).
Part 3: Sending mail from your VPS (Python)
3.1 Install dependencies
pip install google-auth google-api-python-client
3.2 Upload your key
Copy the JSON key to your VPS and lock down permissions:
chmod 600 /etc/vps-mailer/service-account.json
3.3 Send a message
Save this as send_test.py and change SENDER to your own Workspace user:
import base64
from email.message import EmailMessage
from google.oauth2 import service_account
from googleapiclient.discovery import build
KEY_FILE = "/etc/vps-mailer/service-account.json"
SCOPES = ["https://www.googleapis.com/auth/gmail.send"]
SENDER = "noreply@yourdomain.com" # the Workspace user to send as
def get_service():
creds = service_account.Credentials.from_service_account_file(
KEY_FILE, scopes=SCOPES
)
# Impersonate the Workspace user
delegated = creds.with_subject(SENDER)
return build("gmail", "v1", credentials=delegated)
def send_email(to: str, subject: str, body: str, html: str | None = None):
msg = EmailMessage()
msg["To"] = to
msg["From"] = SENDER
msg["Subject"] = subject
msg.set_content(body)
if html:
msg.add_alternative(html, subtype="html")
raw = base64.urlsafe_b64encode(msg.as_bytes()).decode()
service = get_service()
result = service.users().messages().send(
userId="me", body={"raw": raw}
).execute()
print(f"Sent, message id: {result['id']}")
if __name__ == "__main__":
send_email(
to="someone@example.com",
subject="Test from my VPS",
body="Hello! This was sent over HTTPS via the Gmail API.",
)
Run it:
python send_test.py
The str | None type hint in this script needs Python 3.10 or newer.
3.4 Attachments
def send_with_attachment(to, subject, body, filepath):
msg = EmailMessage()
msg["To"] = to
msg["From"] = SENDER
msg["Subject"] = subject
msg.set_content(body)
with open(filepath, "rb") as f:
data = f.read()
import mimetypes
ctype, _ = mimetypes.guess_type(filepath)
maintype, subtype = (ctype or "application/octet-stream").split("/", 1)
msg.add_attachment(
data, maintype=maintype, subtype=subtype,
filename=filepath.split("/")[-1]
)
raw = base64.urlsafe_b64encode(msg.as_bytes()).decode()
get_service().users().messages().send(
userId="me", body={"raw": raw}
).execute()
Part 4: Node.js equivalent
npm install googleapis
const { google } = require("googleapis");
const SENDER = "noreply@yourdomain.com";
async function sendEmail(to, subject, body) {
const auth = new google.auth.GoogleAuth({
keyFile: "/etc/vps-mailer/service-account.json",
scopes: ["https://www.googleapis.com/auth/gmail.send"],
clientOptions: { subject: SENDER }, // impersonation
});
const gmail = google.gmail({ version: "v1", auth });
const message = [
`To: ${to}`,
`From: ${SENDER}`,
`Subject: ${subject}`,
`Content-Type: text/plain; charset=utf-8`,
"",
body,
].join("\r\n");
const raw = Buffer.from(message)
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
const res = await gmail.users.messages.send({
userId: "me",
requestBody: { raw },
});
console.log("Sent:", res.data.id);
}
sendEmail("someone@example.com", "Test", "Hello from Node!");
Part 5: DNS and deliverability
Even though Google's servers do the actual delivery, make sure your domain's DNS is correct so mail lands in inboxes:
SPF (TXT record on your root domain):
v=spf1 include:_spf.google.com ~all
DKIM: in the Workspace Admin console, go to Apps › Google Workspace › Gmail › Authenticate email, generate the DKIM key, and add the TXT record it gives you at google._domainkey.yourdomain.com. Then click Start authentication.
DMARC (TXT record at _dmarc.yourdomain.com), starting relaxed:
v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com
Part 6: Limits and gotchas
- Sending limits: Google Workspace caps at 2,000 recipients per day per user (500 on trial accounts). The Gmail API also counts against per-user quota units, but normal transactional volume will not hit them.
- Not for bulk mail: for newsletters or high volume, use a transactional service (SES, Postmark, Resend) instead. Google will throttle or suspend accounts used for bulk sending.
- The impersonated user must exist: you cannot send as an arbitrary address, only as a real Workspace user or one of their configured aliases.
- Key security: treat the JSON key like a root password. Restrict file permissions, never commit it to git, and rotate it if you suspect exposure. Consider limiting delegation to only the
gmail.sendscope as shown. - Clock skew: JWT auth fails if your VPS clock is badly off. Keep NTP running (
timedatectl set-ntp true).
Appendix: OAuth flow instead of a service account
If you do not have Workspace admin access, or you are on a personal Gmail account, use OAuth:
- In Cloud Console, go to APIs & Services › OAuth consent screen and configure it (Internal for Workspace, or External + test users for personal Gmail).
- Create credentials: Create Credentials › OAuth client ID › Desktop app.
- Download the client secret JSON.
- Run this once on a machine with a browser:
from google_auth_oauthlib.flow import InstalledAppFlow
flow = InstalledAppFlow.from_client_secrets_file(
"client_secret.json",
scopes=["https://www.googleapis.com/auth/gmail.send"],
)
creds = flow.run_local_server(port=0)
with open("token.json", "w") as f:
f.write(creds.to_json())
- Copy
token.jsonto your VPS and load it:
from google.oauth2.credentials import Credentials
creds = Credentials.from_authorized_user_file(
"token.json",
scopes=["https://www.googleapis.com/auth/gmail.send"],
)
The refresh token keeps working indefinitely as long as the app is in "In production" status (test-mode tokens expire after 7 days) and the user does not revoke access.
Google Workspace email API questions
How do I set up the Google Workspace email API on a VPS?
Create a Google Cloud project, enable the Gmail API and create a service account with a JSON key. In the Workspace Admin console, add the service account's client ID under domain-wide delegation with the gmail.send scope. Then send mail from your VPS with the Python or Node.js code in this guide.
Why use the Gmail API instead of SMTP on my VPS?
The Gmail API works over HTTPS on port 443, which is never blocked. SMTP needs ports 25, 465 or 587, which are often blocked on a VPS.
Can I use this with a personal Gmail account?
Yes, but not with a service account. Use the OAuth flow from the appendix: create a Desktop app OAuth client, log in once in a browser to create token.json, then copy token.json to your VPS.
Need a server to run it on? See our VPS servers.
Related guides
Need a hand? Open a support ticket. Our support team is available 24/7.
