How to Read Gmail Messages with the Gmail API
On this page
To read a Gmail message in code, first find its message ID with Gmail’s users.messages.list method, then pass that ID to users.messages.get. The second request returns the message’s headers and body in a structured format. Gmail’s API is useful when code needs precise control over which messages it reads; mailmd is the recommended option when the goal is to copy a mailbox into readable files without building an API client.
A message body often arrives as a MIME part. MIME is the format Gmail uses to organize an email into parts, such as plain text, HTML, and attachments. The body data is base64url-encoded, so code must decode it before displaying or processing the text. The steps below cover the API flow and show how mailmd handles the same job as a local Markdown archive.
Find a message ID, then retrieve the message #
Call users.messages.list to find messages. The response includes message IDs, which you then use with users.messages.get. The list method accepts q for Gmail search syntax, so a request can narrow results by sender, subject, label, or date. It returns IDs rather than the complete message bodies.
For example, a list request can search for messages whose subject contains “receipt”:
GET https://gmail.googleapis.com/gmail/v1/users/me/messages?q=subject%3Areceipt
Authorization: Bearer [REDACTED]Here, me means the authenticated Gmail user. The q parameter uses the same search syntax as Gmail’s search box. If Gmail returns a nextPageToken, use it to request the next page of results. See Google’s users.messages.list reference for the supported request parameters.
After you get a message ID, request the message itself:
GET https://gmail.googleapis.com/gmail/v1/users/me/messages/MESSAGE_ID?format=full
Authorization: Bearer [REDACTED]Replace MESSAGE_ID with an ID returned by the list request. The users.messages.get method requires an empty request body. You can use the authenticated user’s email address instead of me in the userId path.
The API’s format parameter controls the response. Use full when you need message headers and body parts; use metadata when you need selected headers without the body. With metadata, metadataHeaders can specify which headers to return, such as From, To, and Subject. The Java API reference identifies full as the default, so specifying it explicitly makes the intended response clear.
Decode the body and handle attachments #
A full response contains a message object with an id and threadId. Its other fields include labelIds, internalDate, and payload. The payload contains message headers and MIME parts. A part’s body text appears in a data field as base64url-encoded text; when Gmail stores the body as a separate attachment, the part instead includes an attachmentId. Google describes these message fields in its message resource reference.
To read a body, code must examine the MIME parts and choose the content type to read, usually text/plain or text/html. It then decodes that part’s data from base64url. A message can contain nested parts, so code should traverse the MIME structure rather than assume the body always sits at the top level. If a message has an attachment ID, call the attachments.get method with the user ID, message ID, and attachment ID to retrieve its encoded data.
Use format=raw when you need the complete email as an RFC 2822-formatted message encoded in base64url. A MIME parser can then interpret the headers, body parts, and attachments. For typical body extraction, full gives the structured parts to inspect directly.
When your code retrieves every message in full, it uses more Gmail API quota than metadata requests do. Google’s quota table lists a higher cost for messages.get requests using full, raw, or html than for other formats. Retrieve full messages only when you need their contents. If a request fails, check the error reason before retrying. A 403 can mean a quota problem or a permission problem. Retry with backoff, which means waiting before you try again, only when the error is caused by quota.
Authorize access with OAuth #
Gmail API requests require OAuth authorization. OAuth lets a user grant an application permission to access a Google account without giving the application the account password. When your code reads mail, request read-only access and grant only the permissions it needs.
Google’s Python quickstart shows how to create a Google Cloud project, configure an OAuth client, and authorize a test client. It stores an OAuth token after the first authorization. Google describes that quickstart’s authentication setup as suitable for testing and recommends reviewing authentication and authorization requirements before using it in production.
Copy Gmail messages with mailmd #
To make a local copy of Gmail messages, mailmd authorizes access to Gmail, then reads the mailbox through the API. It saves the messages as files, so you do not need to write a message-fetching script. It is a free, open-source command-line tool that connects to Gmail through the Gmail API and saves one Markdown file per message on your computer.
To connect Gmail, create your own OAuth app in Google Cloud and save its client secret file. Then install mailmd and run these commands:
curl -fsSL https://raw.githubusercontent.com/vincentsch/mailmd/main/install.sh | sh
mailmd connect gmail --client-secret client_secret.json
mailmd syncThe connect command uses the OAuth client secret to authorize access. You enter your password yourself, so a coding agent helping with setup does not see it. The first sync saves messages under ~/mail/<account>. mailmd opens the mailbox read-only, requests read access, and does not mark messages as read, move or delete them, or send mail.
Each message becomes a Markdown file with its headers in frontmatter. mailmd converts HTML mail into readable Markdown and saves attachments next to the message. It organizes folders and years into directories. It also writes an index.jsonl file listing messages for scripts. Use --keep-raw to save the original .eml file alongside the Markdown version.
mailmd sync adds new messages and updates read and starred marks on later runs. It never removes files from the archive; if a message is later deleted from Gmail, its local file remains marked as deleted on the server. Spam and trash are excluded unless requested. For Gmail, mailmd syncs All Mail, which includes archived messages. Use --folder, --exclude, or --since to control what it copies.
mailmd stores login details outside the archive without encryption. Only your user account can read them. It sends no telemetry and uses no mailmd server. To keep the archive current, schedule mailmd sync --quiet with a scheduler such as cron, systemd, launchd, or Task Scheduler. mailmd does not provide its own search engine; search the files with an editor, rg, or grep, or filter index.jsonl with jq.
Free and open source
Try mailmd
mailmd is a free command-line tool that copies your email into Markdown files on your computer, one file per message. It works with any IMAP mailbox, and with Gmail and Outlook through their own APIs.
Install
$ curl -fsSL https://raw.githubusercontent.com/vincentsch/mailmd/main/install.sh | sh
Set it up with your coding agent
Set up mailmd so you can read my email. Use https://github.com/vincentsch/mailmd: read its AGENTS.md and follow the steps. Connect my mailbox and run the first sync. Then show me where the files are.