A reference guide for building AI agents: every method, how to authenticate, and the permissions each one needs.
The Gmail API is how an app or AI agent works with a Gmail or Google Workspace mailbox: reading and searching mail, sending and drafting messages, and organising what's there with labels. Access is granted through OAuth and a set of scopes, and an agent only ever reaches what those scopes cover. Gmail can also push a signal whenever the mailbox changes, so an app can react to new mail without checking on a timer.
An app reaches Gmail over REST, sending HTTPS requests and reading JSON back. Several calls can be bundled into one request to cut round-trips, and Gmail can push a signal when the mailbox changes. Each route is bound by the OAuth scopes the app was granted.
An app or AI agent makes direct HTTPS calls to the Gmail API and reads JSON back.
Google hosts a remote Model Context Protocol server for Gmail at gmailmcp.googleapis.com that exposes Gmail to MCP-aware agents, such as Claude and Google Antigravity, as standard tools. It is in Google Workspace Developer Preview.
Several Gmail calls are bundled into one HTTP request to cut round-trips.
Gmail pushes a mailbox-change signal to a Cloud Pub/Sub topic the app owns.
OAuth 2.0 is the standard path, with a signed-in user granting an app access to their mailbox. The restricted scopes (readonly, modify, compose, mail.google.com, metadata, insert) require Google app verification and, for production, a CASA security assessment.
A service account with domain-wide delegation works across a Google Workspace organisation but not consumer Gmail. A Workspace admin grants the service account's client ID the chosen scopes in the Admin console, and the app then impersonates any user in the domain.
Gmail's API is divided into areas that map to parts of a mailbox: messages and the threads that group them, labels, drafts, the history change log, attachments, account settings, and push notifications. Each area carries its own scopes, and some reach further than others.
Read, send, label, trash and delete messages.
Read and act on whole conversations at once.
Create, read, update and delete labels.
Create, read, update, send and delete drafts.
Track incremental mailbox changes since a point in time.
Fetch attachment bodies from a message.
Read and change mail settings: filters, vacation auto-reply, send-as aliases and delegates.
Read the profile and control push notifications.
Filter by method, access, or permission, or search any path. Select a row for version detail, rate limits, the related webhook event, and the source.
| Method | Endpoint | What it does | Access | Permission | Version | |
|---|---|---|---|---|---|---|
MessagesRead, send, label, trash and delete messages.11 | ||||||
| GET | /gmail/v1/users/{userId}/messages | List messages in the mailbox, with optional Gmail search query. | read | gmail.readonly | Current | |
Also satisfied by gmail.modify, gmail.metadata (metadata returns no body), or mail.google.com. Acts onmessage Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit5 quota units SourceOfficial documentation ↗ | ||||||
| GET | /gmail/v1/users/{userId}/messages/{id} | Get a single message; format=full|metadata|minimal|raw. | read | gmail.readonly | Current | |
The metadata format works with gmail.metadata; full and raw need gmail.readonly or broader. Acts onmessage Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit20 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/messages/send | Send an RFC 2822 message (raw, base64url) to recipients. | write | gmail.send | Current | |
Minimal scope is gmail.send; also gmail.compose, gmail.modify, mail.google.com. Acts onmessage Permission (capability) gmail.sendVersionAvailable since the API’s base version Webhook event messagesAddedRate limit100 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/messages | Insert a message directly into the mailbox (no SMTP send); like an IMAP append. | write | gmail.insert | Current | |
gmail.insert (also gmail.modify, mail.google.com). Acts onmessage Permission (capability) gmail.insertVersionAvailable since the API’s base version Webhook event messagesAddedRate limit25 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/messages/import | Import a message as if received via SMTP (runs spam/filters); max 150 MB message. | write | gmail.insert | Current | |
gmail.insert (also gmail.modify, mail.google.com). Acts onmessage Permission (capability) gmail.insertVersionAvailable since the API’s base version Webhook event messagesAddedRate limit25 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/messages/{id}/modify | Add/remove labels on a message. | write | gmail.modify | Current | |
gmail.modify (or mail.google.com). Acts onmessage Permission (capability) gmail.modifyVersionAvailable since the API’s base version Webhook event labelsAddedRate limit5 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/messages/{id}/trash | Move a message to Trash. | write | gmail.modify | Current | |
gmail.modify (or mail.google.com). Acts onmessage Permission (capability) gmail.modifyVersionAvailable since the API’s base version Webhook event messagesDeletedRate limit20 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/messages/{id}/untrash | Remove a message from Trash. | write | gmail.modify | Current | |
gmail.modify (or mail.google.com). Acts onmessage Permission (capability) gmail.modifyVersionAvailable since the API’s base version Webhook eventNone Rate limit5 quota units SourceOfficial documentation ↗ | ||||||
| DELETE | /gmail/v1/users/{userId}/messages/{id} | Permanently delete a message (bypasses Trash; irreversible). | write | mail.google.com | Current | |
Permanent delete requires full-access mail.google.com only. Acts onmessage Permission (capability) mail.google.comVersionAvailable since the API’s base version Webhook event messagesDeletedRate limit10 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/messages/batchModify | Add/remove labels on many messages in one call. | write | gmail.modify | Current | |
gmail.modify (or mail.google.com). Acts onmessage Permission (capability) gmail.modifyVersionAvailable since the API’s base version Webhook event labelsAddedRate limit50 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/messages/batchDelete | Permanently delete many messages by ID (irreversible). | write | mail.google.com | Current | |
Permanent delete requires full-access mail.google.com only. Acts onmessage Permission (capability) mail.google.comVersionAvailable since the API’s base version Webhook event messagesDeletedRate limit50 quota units SourceOfficial documentation ↗ | ||||||
ThreadsRead and act on whole conversations at once.5 | ||||||
| GET | /gmail/v1/users/{userId}/threads | List threads, with optional Gmail search query. | read | gmail.readonly | Current | |
Also gmail.modify, gmail.metadata, mail.google.com. Acts onthread Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit10 quota units SourceOfficial documentation ↗ | ||||||
| GET | /gmail/v1/users/{userId}/threads/{id} | Get a thread and all its messages. | read | gmail.readonly | Current | |
Also gmail.modify, gmail.metadata, mail.google.com. Acts onthread Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit40 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/threads/{id}/modify | Add/remove labels on all messages in a thread. | write | gmail.modify | Current | |
gmail.modify (or mail.google.com). Acts onthread Permission (capability) gmail.modifyVersionAvailable since the API’s base version Webhook event labelsAddedRate limit10 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/threads/{id}/trash | Move a thread to Trash. | write | gmail.modify | Current | |
gmail.modify (or mail.google.com). Acts onthread Permission (capability) gmail.modifyVersionAvailable since the API’s base version Webhook event messagesDeletedRate limit20 quota units SourceOfficial documentation ↗ | ||||||
| DELETE | /gmail/v1/users/{userId}/threads/{id} | Permanently delete a thread and all its messages (irreversible). | write | mail.google.com | Current | |
Permanent delete requires full-access mail.google.com only. Acts onthread Permission (capability) mail.google.comVersionAvailable since the API’s base version Webhook event messagesDeletedRate limit20 quota units SourceOfficial documentation ↗ | ||||||
LabelsCreate, read, update and delete labels.5 | ||||||
| GET | /gmail/v1/users/{userId}/labels | List all labels in the mailbox (system + user). | read | gmail.readonly | Current | |
Also gmail.labels, gmail.modify, gmail.metadata, mail.google.com. Acts onlabel Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit1 quota units SourceOfficial documentation ↗ | ||||||
| GET | /gmail/v1/users/{userId}/labels/{id} | Get a label, including message/thread counts. | read | gmail.readonly | Current | |
Also gmail.labels, gmail.modify, gmail.metadata, mail.google.com. Acts onlabel Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit1 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/labels | Create a new user label. | write | gmail.labels | Current | |
gmail.labels (also gmail.modify, mail.google.com). Acts onlabel Permission (capability) gmail.labelsVersionAvailable since the API’s base version Webhook eventNone Rate limit5 quota units SourceOfficial documentation ↗ | ||||||
| PUT | /gmail/v1/users/{userId}/labels/{id} | Replace a label's definition (full update). | write | gmail.labels | Current | |
gmail.labels (also gmail.modify, mail.google.com). A PATCH variant exists for partial update. Acts onlabel Permission (capability) gmail.labelsVersionAvailable since the API’s base version Webhook eventNone Rate limit5 quota units SourceOfficial documentation ↗ | ||||||
| DELETE | /gmail/v1/users/{userId}/labels/{id} | Delete a user label and remove it from all messages. | write | gmail.labels | Current | |
gmail.labels (also gmail.modify, mail.google.com). Acts onlabel Permission (capability) gmail.labelsVersionAvailable since the API’s base version Webhook eventNone Rate limit5 quota units SourceOfficial documentation ↗ | ||||||
DraftsCreate, read, update, send and delete drafts.6 | ||||||
| GET | /gmail/v1/users/{userId}/drafts | List drafts in the mailbox. | read | gmail.readonly | Current | |
Also gmail.compose, gmail.modify, mail.google.com. Acts ondraft Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit5 quota units SourceOfficial documentation ↗ | ||||||
| GET | /gmail/v1/users/{userId}/drafts/{id} | Get a single draft. | read | gmail.readonly | Current | |
Also gmail.compose, gmail.modify, mail.google.com. Acts ondraft Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit20 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/drafts | Create a draft from a raw message. | write | gmail.compose | Current | |
gmail.compose (also gmail.modify, mail.google.com). Acts ondraft Permission (capability) gmail.composeVersionAvailable since the API’s base version Webhook eventNone Rate limit10 quota units SourceOfficial documentation ↗ | ||||||
| PUT | /gmail/v1/users/{userId}/drafts/{id} | Replace a draft's content. | write | gmail.compose | Current | |
gmail.compose (also gmail.modify, mail.google.com). Acts ondraft Permission (capability) gmail.composeVersionAvailable since the API’s base version Webhook eventNone Rate limit15 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/drafts/send | Send an existing draft to its recipients. | write | gmail.compose | Current | |
gmail.compose (also gmail.modify, mail.google.com). gmail.send alone does not cover drafts.send. Acts ondraft Permission (capability) gmail.composeVersionAvailable since the API’s base version Webhook eventNone Rate limit100 quota units SourceOfficial documentation ↗ | ||||||
| DELETE | /gmail/v1/users/{userId}/drafts/{id} | Permanently delete a draft. | write | gmail.compose | Current | |
gmail.compose (also gmail.modify, mail.google.com). Acts ondraft Permission (capability) gmail.composeVersionAvailable since the API’s base version Webhook eventNone Rate limit10 quota units SourceOfficial documentation ↗ | ||||||
HistoryTrack incremental mailbox changes since a point in time.1 | ||||||
| GET | /gmail/v1/users/{userId}/history | List mailbox change history (added/removed messages, label changes) since a startHistoryId. | read | gmail.readonly | Current | |
Also gmail.modify, gmail.metadata, mail.google.com. Drives polling after a push notification. Acts onhistoryRecord Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit2 quota units SourceOfficial documentation ↗ | ||||||
AttachmentsFetch attachment bodies from a message.1 | ||||||
| GET | /gmail/v1/users/{userId}/messages/{messageId}/attachments/{id} | Get an attachment body (base64url) by attachment ID from a message. | read | gmail.readonly | Current | |
gmail.readonly (also gmail.modify, mail.google.com). The attachment ID comes from messages.get. Acts onmessagePartBody Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit20 quota units SourceOfficial documentation ↗ | ||||||
SettingsRead and change mail settings: filters, vacation auto-reply, send-as aliases and delegates.5 | ||||||
| GET | /gmail/v1/users/{userId}/settings/vacation | Get the vacation auto-reply (out-of-office) settings. | read | gmail.settings.basic | Current | |
gmail.settings.basic (also gmail.readonly, gmail.modify, mail.google.com). Acts onsetting Permission (capability) gmail.settings.basicVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PUT | /gmail/v1/users/{userId}/settings/vacation | Update the vacation auto-reply settings. | write | gmail.settings.basic | Current | |
gmail.settings.basic (or mail.google.com). Acts onsetting Permission (capability) gmail.settings.basicVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/settings/filters | Create a mail filter (criteria + action). | write | gmail.settings.basic | Current | |
gmail.settings.basic (or mail.google.com). Acts onsetting Permission (capability) gmail.settings.basicVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /gmail/v1/users/{userId}/settings/sendAs | List the send-as aliases configured for the account. | read | gmail.settings.basic | Current | |
gmail.settings.basic (also gmail.readonly, gmail.modify, mail.google.com). Acts onsetting Permission (capability) gmail.settings.basicVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/settings/delegates | Add a delegate (another account allowed to read/send mail). Workspace + domain-wide delegation only. | write | gmail.settings.sharing | Current | |
gmail.settings.sharing (restricted; admin and domain-wide delegation use only). Also mail.google.com. Acts onsetting Permission (capability) gmail.settings.sharingVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Users / WatchRead the profile and control push notifications.3 | ||||||
| GET | /gmail/v1/users/{userId}/profile | Get the user's profile: emailAddress, messagesTotal, threadsTotal, historyId. | read | gmail.readonly | Current | |
Any Gmail read scope works (gmail.readonly, gmail.modify, gmail.metadata, mail.google.com). Acts onuser Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit1 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/watch | Start push notifications: register a Cloud Pub/Sub topic to receive mailbox-change events. | read | gmail.readonly | Current | |
gmail.readonly (or any broader read scope). Returns historyId and expiration. Acts onuser Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit100 quota units SourceOfficial documentation ↗ | ||||||
| POST | /gmail/v1/users/{userId}/stop | Stop push notifications for the mailbox. | read | gmail.readonly | Current | |
gmail.readonly (or any broader read scope). Acts onuser Permission (capability) gmail.readonlyVersionAvailable since the API’s base version Webhook eventNone Rate limit50 quota units SourceOfficial documentation ↗ | ||||||
Gmail sends a single mailbox-change signal to a Cloud Pub/Sub topic an app owns rather than a detailed event for each change. The signal carries a historyId, and the app reads the history change log from its last known point to find exactly what changed. The change types below are what the history log reports.
| Event | What it signals | Triggered by |
|---|---|---|
messagesAdded | A message was added to the mailbox. | /gmail/v1/users/{userId}/messages/send/gmail/v1/users/{userId}/messages/gmail/v1/users/{userId}/messages/import |
messagesDeleted | A message was removed from the mailbox. | /gmail/v1/users/{userId}/messages/{id}/gmail/v1/users/{userId}/messages/{id}/trash/gmail/v1/users/{userId}/messages/batchDelete/gmail/v1/users/{userId}/threads/{id}/trash/gmail/v1/users/{userId}/threads/{id} |
labelsAdded | A label was added to a message. | /gmail/v1/users/{userId}/messages/{id}/modify/gmail/v1/users/{userId}/messages/batchModify/gmail/v1/users/{userId}/threads/{id}/modify |
labelsRemoved | A label was removed from a message. | /gmail/v1/users/{userId}/messages/{id}/modify/gmail/v1/users/{userId}/messages/batchModify/gmail/v1/users/{userId}/threads/{id}/modify |
Gmail meters how fast an app can call the API and how much mail an account can send. Request rate is measured in quota units, an abstract cost that varies by method, and sending is capped separately by account policy.
Gmail meters requests in quota units, an abstract cost that varies by method (a send costs 100 units, a list costs 5). Each method's cost is shown on its row. The default per-user limit is about 6,000 units per minute (the 2015 release notes cite 250 units per second), inside a larger per-project ceiling. Going over returns an HTTP 429, or a 403 rateLimitExceeded, so an app should back off and retry with jitter. Mail sending is capped separately by account policy, covered under request size below.
List methods page through results with pageToken and maxResults. A response carries a nextPageToken, which is passed back as the pageToken for the next page. For messages.list and threads.list, maxResults defaults to 100 and tops out at 500.
messages.import accepts a message up to 150 MB. A standard send tops out at roughly 35 MB once MIME-encoded, a figure that is widely documented but not stated on the v1 messages.send reference page. A single message can address up to 500 unique recipients through the API. Separately, account policy caps sending at roughly 500 messages per day on consumer Gmail and roughly 2,000 messages per day, with roughly 10,000 recipients per day, on Google Workspace. These caps are account or Workspace policy rather than API quota, counted over a rolling 24-hour window.
The status codes an agent should handle, and what to do about each.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | badRequest / invalidArgument | The request is malformed: a required field is missing, a value is invalid, an attachment is bad, or the raw message is malformed. | Fix the request per the error message and do not retry it unchanged, as it is not retriable. |
| 400 | failedPrecondition | The request cannot run in the current state, because a precondition such as a required mailbox state is not met. | Resolve the precondition, often by re-fetching state, before retrying. |
| 401 | authError (Invalid Credentials) | The access token has expired, is invalid, or is missing the required scope. | Refresh the access token with the refresh token, and if a scope is missing, re-run OAuth consent. Retry once after the refresh. |
| 403 | rateLimitExceeded / userRateLimitExceeded | The per-user or per-project request-rate quota has been exceeded. | Slow down and retry with exponential backoff and jitter, smoothing bursts to stay under 250 units per user per second. |
| 403 | dailyLimitExceeded | The project's daily API quota has been reached. | Reduce usage or request a quota increase in the Cloud Console, since a simple retry will not fix it. |
| 403 | domainPolicy | A Workspace admin has disabled API or Gmail-app access for the domain. | The user must contact their Workspace admin to enable access, as the app cannot retry past this. |
| 404 | notFound | The requested message, thread, label, or draft ID does not exist or is not visible to this user. | Verify the ID and the userId or impersonated user, and do not retry unchanged. |
| 429 | tooManyRequests | Per-user limits on mail sending, bandwidth, or concurrent requests have been exceeded. | Back off and retry with exponential backoff and jitter, and reduce concurrency. |
| 500 | backendError | A transient server-side error occurred. | Retry with exponential backoff, as this is retriable. |
| 503 | backendError / unavailable | The service is temporarily unavailable. | Retry with exponential backoff, as this is retriable. |
Gmail's API is at v1, with the version written into the URL path. New capabilities are added to v1 rather than starting a new version.
Gmail's API is at v1 and stable, versioned in the URL path at /gmail/v1/. New capabilities are added to v1 rather than minting a new version, and Google's deprecation policy gives notice before any breaking change. The dated entries below are notable changes from the release notes.
Google changed the quota-unit cost of several read and trash methods under a new quota model.
Google launched a Model Context Protocol server for Gmail in developer preview, exposing Gmail to MCP-aware agents as standard tools.
Endpoints for managing mail settings were added.
Push notifications arrived through Cloud Pub/Sub.
Two scopes were introduced for finer-grained access.
The default per-user request quota was raised tenfold.
v1 is the only version. New features arrive in v1, and Google's deprecation policy gives advance notice before any breaking change, so the release notes are where additions and changes are announced.
Gmail API release notes ↗Bollard AI sits between a team's AI agents and Gmail. Grant each agent exactly the access it needs, read or send, label by label, and every call is checked and logged.