---
name: Google Mail API
description: 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

```bash
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:

1. Create a Google Cloud project
2. Enable the Gmail API
3. Configure OAuth 2.0 credentials (Desktop/Web application)
4. 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)

```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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
# 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

```python
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

```python
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)

```python
# 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

```python
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

```python
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

```python
service.users().messages().trash(
    userId="me",
    id=MESSAGE_ID
).execute()
```

### Delete Message Permanently

```python
service.users().messages().delete(
    userId="me",
    id=MESSAGE_ID
).execute()
```

### Batch Modify Messages

```python
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

```python
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:draft`
- `is:unread`, `is:read`, `is:starred`, `is:important`
- `from:email@example.com`, `to:email@example.com`, `cc:email@example.com`
- `subject:text` - Search in subject line
- `has:attachment` - Only messages with attachments
- `filename:pdf` - Attachment filename
- `before:YYYY/MM/DD`, `after:YYYY/MM/DD`
- `larger: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

```json
{
  "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

```json
{
  "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

```json
{
  "id": "...",
  "historyId": "...",
  "messages": [
    { /* Message objects */ }
  ]
}
```

## Error Handling

```python
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)
