16 KiB
name, description
| name | description |
|---|---|
| Google Mail API | Access, manage, and send Gmail messages using the Google Mail API. Use when the user wants to read emails, send messages, manage labels, organize threads, work with drafts, or automate email operations programmatically. |
Google Mail API
RESTful API reference for the Google Mail API — access and manage Gmail mailbox data including messages, threads, labels, and drafts.
Official docs: https://developers.google.com/workspace/gmail/api/guides API Reference: https://developers.google.com/workspace/gmail/api/reference/rest Python Client: https://googleapis.github.io/google-api-python-client/
Key Concepts
Message
An email message containing sender, recipients, subject, and body. Messages are immutable after creation. Each message has a unique id.
Thread A collection of related messages forming a conversation. When one or more recipients respond to a message, a thread is formed. Threads cannot be created directly, only deleted. Labels can be applied to threads.
Label A mechanism for organizing messages and threads. Two types exist:
- System labels: Pre-defined labels like
INBOX,TRASH,SPAM,DRAFT,SENT. Cannot be deleted or modified (but some can be applied/removed). - User labels: Custom labels created by users. Can be created, modified, and deleted.
Draft
An unsent message. The message within a draft can be replaced before sending. Sending a draft automatically deletes it and creates a message with the SENT label.
Installation
Python Client Library
python3 -m pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
Authentication & Authorization
The Gmail API uses OAuth 2.0 for authentication. You must:
- Create a Google Cloud project
- Enable the Gmail API
- Configure OAuth 2.0 credentials (Desktop/Web application)
- Obtain credentials file (
credentials.json)
Scopes
Different scopes control the level of access. Common scopes:
| Scope | Permissions |
|---|---|
https://www.googleapis.com/auth/gmail.readonly |
Read-only access to Gmail mailbox and metadata |
https://www.googleapis.com/auth/gmail.send |
Send messages only |
https://www.googleapis.com/auth/gmail.modify |
Full access to read/write/modify messages and labels |
https://www.googleapis.com/auth/gmail.compose |
Compose drafts and send messages |
https://www.googleapis.com/auth/gmail.labels |
Manage labels |
OAuth 2.0 Setup (Python)
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
import os.path
SCOPES = ["https://www.googleapis.com/auth/gmail.modify"]
def authenticate():
creds = None
# Load cached credentials if available
if os.path.exists("token.json"):
creds = Credentials.from_authorized_user_file("token.json", SCOPES)
# If no valid credentials, authenticate
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
"credentials.json", SCOPES
)
creds = flow.run_local_server(port=0)
# Save credentials for next run
with open("token.json", "w") as token:
token.write(creds.to_json())
return build("gmail", "v1", credentials=creds)
# Usage
service = authenticate()
Core API Methods
Users
| Method | Description |
|---|---|
users().getProfile(userId="me") |
Get user's Gmail profile |
users().watch(userId="me", body={...}) |
Watch for mailbox changes (push notifications) |
users().stop(userId="me") |
Stop watching mailbox |
Messages
| Method | Description |
|---|---|
users().messages().list(userId="me", ...) |
List messages in mailbox |
users().messages().get(userId="me", id=MESSAGE_ID, ...) |
Get full message details |
users().messages().send(userId="me", body={...}) |
Send a message |
users().messages().create(userId="me", body={...}) |
Insert a message directly |
users().messages().import_(userId="me", body={...}) |
Import a message |
users().messages().delete(userId="me", id=MESSAGE_ID) |
Delete a message |
users().messages().trash(userId="me", id=MESSAGE_ID) |
Move message to trash |
users().messages().untrash(userId="me", id=MESSAGE_ID) |
Restore message from trash |
users().messages().modify(userId="me", id=MESSAGE_ID, body={...}) |
Modify message labels |
users().messages().batchModify(userId="me", body={...}) |
Modify multiple messages at once |
users().messages().batchDelete(userId="me", body={...}) |
Delete multiple messages at once |
Attachments
| Method | Description |
|---|---|
users().messages().attachments().get(userId="me", messageId=MESSAGE_ID, id=ATTACHMENT_ID) |
Get attachment data |
Threads
| Method | Description |
|---|---|
users().threads().list(userId="me", ...) |
List threads |
users().threads().get(userId="me", id=THREAD_ID, ...) |
Get thread with all messages |
users().threads().modify(userId="me", id=THREAD_ID, body={...}) |
Modify thread labels |
users().threads().trash(userId="me", id=THREAD_ID) |
Move thread to trash |
users().threads().untrash(userId="me", id=THREAD_ID) |
Restore thread from trash |
users().threads().delete(userId="me", id=THREAD_ID) |
Delete thread permanently |
Labels
| Method | Description |
|---|---|
users().labels().list(userId="me") |
List all labels |
users().labels().get(userId="me", id=LABEL_ID) |
Get label details |
users().labels().create(userId="me", body={...}) |
Create a new label |
users().labels().update(userId="me", id=LABEL_ID, body={...}) |
Update label |
users().labels().patch(userId="me", id=LABEL_ID, body={...}) |
Patch label |
users().labels().delete(userId="me", id=LABEL_ID) |
Delete label |
Drafts
| Method | Description |
|---|---|
users().drafts().list(userId="me", ...) |
List drafts |
users().drafts().get(userId="me", id=DRAFT_ID) |
Get draft |
users().drafts().create(userId="me", body={...}) |
Create draft |
users().drafts().update(userId="me", id=DRAFT_ID, body={...}) |
Update draft content |
users().drafts().send(userId="me", body={...}) |
Send draft |
users().drafts().delete(userId="me", id=DRAFT_ID) |
Delete draft |
Settings
| Method | Description |
|---|---|
users().settings().getAutoForwarding(userId="me") |
Get auto-forwarding settings |
users().settings().getImap(userId="me") |
Get IMAP settings |
users().settings().getPop(userId="me") |
Get POP settings |
users().settings().getVacation(userId="me") |
Get vacation auto-reply settings |
users().settings().updateAutoForwarding(userId="me", body={...}) |
Update auto-forwarding |
users().settings().updateVacation(userId="me", body={...}) |
Update vacation settings |
Common Recipes
List Labels
service = authenticate()
results = service.users().labels().list(userId="me").execute()
labels = results.get("labels", [])
for label in labels:
print(f"{label['name']} (ID: {label['id']})")
List Messages in Inbox
results = service.users().messages().list(
userId="me",
q="in:inbox", # Gmail query syntax
maxResults=10
).execute()
messages = results.get("messages", [])
for message in messages:
msg_details = service.users().messages().get(
userId="me",
id=message["id"],
format="metadata",
metadataHeaders=["Subject", "From"]
).execute()
headers = msg_details["payload"]["headers"]
subject = next((h["value"] for h in headers if h["name"] == "Subject"), "No Subject")
sender = next((h["value"] for h in headers if h["name"] == "From"), "Unknown")
print(f"{sender}: {subject}")
Get Full Message
message = service.users().messages().get(
userId="me",
id=MESSAGE_ID,
format="full" # or "minimal" for metadata only
).execute()
# Access payload
payload = message["payload"]
headers = payload["headers"]
body = payload.get("body", {}).get("data", "")
# Decode body if base64url encoded
import base64
if body:
decoded_body = base64.urlsafe_b64decode(body).decode()
print(decoded_body)
Send a Message
import base64
from email.message import EmailMessage
# Create email message
message = EmailMessage()
message.set_content("This is the email body")
message["To"] = "recipient@example.com"
message["From"] = "sender@example.com"
message["Subject"] = "Test Email"
# Encode to base64url
encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
# Send
result = service.users().messages().send(
userId="me",
body={"raw": encoded_message}
).execute()
print(f"Message sent with ID: {result['id']}")
Send Email with Attachment
import base64
import mimetypes
from email.message import EmailMessage
message = EmailMessage()
message.set_content("Email with attachment")
message["To"] = "recipient@example.com"
message["From"] = "sender@example.com"
message["Subject"] = "Message with File"
# Add attachment
with open("document.pdf", "rb") as fp:
attachment_data = fp.read()
message.add_attachment(
attachment_data,
maintype="application",
subtype="pdf",
filename="document.pdf"
)
# Send
encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
result = service.users().messages().send(
userId="me",
body={"raw": encoded_message}
).execute()
Create Draft
import base64
from email.message import EmailMessage
message = EmailMessage()
message.set_content("Draft email body")
message["To"] = "recipient@example.com"
message["From"] = "sender@example.com"
message["Subject"] = "Draft Subject"
encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
draft = service.users().drafts().create(
userId="me",
body={"message": {"raw": encoded_message}}
).execute()
print(f"Draft created with ID: {draft['id']}")
Apply Label to Message
# Get label ID first
labels = service.users().labels().list(userId="me").execute()
label_id = next(
(l["id"] for l in labels["labels"] if l["name"] == "Important"),
None
)
if label_id:
service.users().messages().modify(
userId="me",
id=MESSAGE_ID,
body={"addLabelIds": [label_id]}
).execute()
List Threads
results = service.users().threads().list(
userId="me",
q="in:inbox",
maxResults=10
).execute()
threads = results.get("threads", [])
for thread in threads:
thread_data = service.users().threads().get(
userId="me",
id=thread["id"]
).execute()
num_messages = len(thread_data["messages"])
print(f"Thread {thread['id']}: {num_messages} messages")
Get Thread Messages in Order
thread = service.users().threads().get(
userId="me",
id=THREAD_ID,
format="full"
).execute()
messages = thread.get("messages", [])
for msg in messages:
headers = msg["payload"]["headers"]
subject = next((h["value"] for h in headers if h["name"] == "Subject"), "")
sender = next((h["value"] for h in headers if h["name"] == "From"), "")
print(f"From: {sender}")
print(f"Subject: {subject}")
print("---")
Search Messages (Gmail Query Syntax)
# Common query operators:
# in:inbox, in:trash, in:spam, in:sent, in:draft
# is:unread, is:read, is:starred
# from:sender@example.com, to:recipient@example.com
# subject:"search term", has:attachment
# after:YYYY/MM/DD, before:YYYY/MM/DD
# larger:1M, smaller:100K
results = service.users().messages().list(
userId="me",
q='is:unread from:boss@example.com after:2024/01/01',
maxResults=10
).execute()
messages = results.get("messages", [])
Create Custom Label
label = service.users().labels().create(
userId="me",
body={
"name": "My Project",
"labelListVisibility": "labelShow", # Show in label list
"messageListVisibility": "show" # Show messages in list
}
).execute()
print(f"Label created: {label['id']}")
Modify Thread Labels
service.users().threads().modify(
userId="me",
id=THREAD_ID,
body={
"addLabelIds": [LABEL_ID_1, LABEL_ID_2],
"removeLabelIds": [LABEL_ID_3]
}
).execute()
Move Message to Trash
service.users().messages().trash(
userId="me",
id=MESSAGE_ID
).execute()
Delete Message Permanently
service.users().messages().delete(
userId="me",
id=MESSAGE_ID
).execute()
Batch Modify Messages
service.users().messages().batchModify(
userId="me",
body={
"ids": [MESSAGE_ID_1, MESSAGE_ID_2, MESSAGE_ID_3],
"addLabelIds": [LABEL_ID],
"removeLabelIds": []
}
).execute()
Get User Profile
profile = service.users().getProfile(userId="me").execute()
print(f"Email: {profile['emailAddress']}")
print(f"Messages Total: {profile['messagesTotal']}")
print(f"Threads Total: {profile['threadsTotal']}")
Query String Format
Gmail API uses Gmail search syntax for q parameter. Common operators:
in:inbox,in:trash,in:spam,in:sent,in:draftis:unread,is:read,is:starred,is:importantfrom:email@example.com,to:email@example.com,cc:email@example.comsubject:text- Search in subject linehas:attachment- Only messages with attachmentsfilename:pdf- Attachment filenamebefore:YYYY/MM/DD,after:YYYY/MM/DDlarger:1M,smaller:500K- Message size- Combine with
AND,OR,-(NOT)
Example: q='is:unread has:attachment from:boss@example.com after:2024/01/01'
Message Resource Structure
{
"id": "...", // Message ID
"threadId": "...", // Thread ID
"labelIds": ["INBOX", "..."], // Applied labels
"snippet": "...", // Preview text
"historyId": "...", // History record ID
"internalDate": "...", // Unix timestamp
"payload": {
"partId": "",
"mimeType": "...", // e.g., "text/plain", "multipart/mixed"
"filename": "",
"headers": [
{ "name": "Subject", "value": "..." },
{ "name": "From", "value": "..." },
{ "name": "To", "value": "..." },
{ "name": "Date", "value": "..." }
],
"body": {
"size": 0,
"data": "base64url encoded body"
},
"parts": [...] // For multipart messages
},
"sizeEstimate": 0 // Size in bytes
}
Label Resource Structure
{
"id": "...",
"name": "...",
"messageListVisibility": "show", // or "hide"
"labelListVisibility": "labelShow", // or "labelHide"
"type": "user", // or "system"
"messagesTotal": 0,
"messagesUnread": 0,
"threadsTotal": 0,
"threadsUnread": 0
}
Thread Resource Structure
{
"id": "...",
"historyId": "...",
"messages": [
{ /* Message objects */ }
]
}
Error Handling
from googleapiclient.errors import HttpError
try:
result = service.users().messages().get(
userId="me",
id=MESSAGE_ID
).execute()
except HttpError as error:
print(f"Error {error.resp.status}: {error.content}")
# Common errors:
# 400 - Bad request
# 403 - Forbidden (missing scope or permission)
# 404 - Not found
# 429 - Rate limited
Source
- Main Documentation: https://developers.google.com/workspace/gmail/api/guides
- API Reference: https://developers.google.com/workspace/gmail/api/reference/rest
- Python Quickstart: https://developers.google.com/workspace/gmail/api/quickstart/python
- Python Client Docs: https://googleapis.github.io/google-api-python-client/docs/dyn/gmail_v1.html
- Authentication: https://developers.google.com/identity/protocols/oauth2
- Service: gmail.googleapis.com (v1)