Guide
Gmail API Search Query Examples
Gmail API search query examples for the q parameter, labels, categories, dates, attachments, and rfc822msgid. See direct API syntax, then translate common cases into supported CLI filters.
Written by Qasim Muhammad Staff SRE
Reviewed by Nick Barraclough
How does Gmail API search query syntax work?
Gmail API search query syntax works by passing a Gmail search expression into the q query parameter on users.messages.list. Google documents that the parameter supports the same query format as the Gmail search box, so one HTTP request can filter by sender, recipient, subject, unread state, date, attachment status, and message ID.
The key limitation is that messages.list returns message IDs, not full message bodies. The method defaults to 100 results and accepts up to 500 results per page, so a search that returns 1,500 matches still needs at least 3 list calls before you fetch any content. The official users.messages.list reference also notes that q cannot be used with the gmail.metadata scope.
This minimal Python example shows the direct API shape. It searches unread mail from one sender, caps the first page at 50 IDs, and leaves message-body reads for a second step. That separation matters because every body read is another API call.
response = service.users().messages().list(
userId="me",
q="from:billing@stripe.com is:unread",
maxResults=50,
).execute()
message_ids = [message["id"] for message in response.get("messages", [])]How do you search by sender, recipient, subject, and unread state?
Gmail search queries combine field operators in one string, so sender, recipient, subject, and unread filters can live in the same q value. The practical pattern is to keep the query narrow before pagination starts, because every extra result can become one more messages.get call later.
Google's examples include operators such as from:, rfc822msgid:, and is:unread. The Gmail UI also supports to:, subject:, exact phrases, and negation. In API code, the operators are just a URL-encoded string; the server does the search and returns matching message IDs. A 25-result cap is a good first pass for agent or cron jobs because it gives enough candidates without forcing a full mailbox read.
This CLI command uses verified flags from nylas email search. The positional query is invoice because the current command treats a non-wildcard query as subject text; field operators such as from: are Gmail API syntax, not raw CLI pass-through. The command returns 25 JSON results for scripts or agent tools.
nylas email search "invoice" \
--from "billing@stripe.com" \
--unread \
--limit 25 \
--jsonHow do Gmail labels and category searches work?
Gmail labels are not folders; they are tags that can appear on many messages, and one message can carry several labels at once. Google documents 2 label families: reserved SYSTEM labels such as INBOX and custom USER labels created by the mailbox owner or an integration.
The Gmail labels guide lists common system labels including INBOX, SPAM, TRASH, UNREAD, STARRED, and 5 category labels such as CATEGORY_PERSONAL, CATEGORY_SOCIAL, CATEGORY_PROMOTIONS, CATEGORY_UPDATES, and CATEGORY_FORUMS. Search queries often use human-facing syntax like category:promotions, while API label filters use label IDs such as CATEGORY_PROMOTIONS.
The CLI exposes Gmail category labels through the same folder-style interface used for other providers. This example searches 25 Promotions messages, then runs a subject search against Social messages. Both commands use --in, which is the verified folder filter for nylas email search.
nylas email search "*" --in "CATEGORY_PROMOTIONS" --limit 25 --json
nylas email search "webinar" \
--in "CATEGORY_SOCIAL" \
--limit 25 \
--jsonHow do you search for attachments without decoding MIME first?
Gmail attachment searches should start with the search index, not with MIME parsing. The query has:attachment narrows the result set before your code downloads message payloads, which avoids decoding every message in a large inbox just to discover that only 3 messages contain files.
Attachment download is still a second API step. The Gmail attachment resource describes attachmentId as the value used in a separate messages.attachments.get request, and the returned data is base64url encoded. That means a direct Gmail integration needs search, message-part traversal, attachment ID lookup, byte decoding, and file writing.
The CLI keeps the first pass small and delays file download until you select a message and attachment ID. These 3 commands search for attachment-bearing messages, list attachments on one selected message, and then download a specific attachment to a local path.
nylas email search "*" --has-attachment --from "billing@stripe.com" --limit 25 --json
nylas email attachments list <message-id> --json
nylas email attachments download <attachment-id> <message-id> --output ./invoice.pdfHow do you search date ranges and old mail?
Gmail date searches use operators such as after:, before:, older:, and newer: inside the search expression, while the CLI exposes date bounds as explicit flags. Both approaches should use fixed dates for automation so the same job produces repeatable results across 24-hour reruns.
Fixed windows prevent hidden mailbox scans. A monthly invoice job can search January 2026 only, then read selected messages after ranking the subject and sender. In API code, that date range sits inside the q string. In the CLI, --after and --before make the date bounds visible to a reviewer and easy to enforce in a wrapper.
This command searches invoice-subject messages between January 1 and February 1, 2026. It returns 50 candidates at most, which is enough for a human report or an agent ranking pass without letting the retrieval step expand into thousands of bodies.
nylas email search "invoice" \
--after 2026-01-01 \
--before 2026-02-01 \
--limit 50 \
--jsonHow do you find one message with rfc822msgid?
The rfc822msgid: operator searches for one specific Internet message ID, which is useful when a webhook, bounce, log line, or support ticket records the original Message-ID header. Instead of scanning by subject, the search targets a globally meaningful identifier that normally maps to 1 conversation.
Google includes rfc822msgid: in the users.messages.list documentation examples. The operator is most useful for debugging duplicate sends, webhook fan-out, and reply threading because it bypasses fuzzy text search. The result can still include more than 1 message if copies exist across labels or thread views, so keep a low limit and inspect IDs before reading bodies.
The CLI does not currently expose a raw Gmail q pass-through for rfc822msgid:. Use the direct Gmail API for this exact operator, and use CLI flags only when you can approximate the lookup with sender, subject, date, folder, or attachment filters. This example caps the API result set at 5 IDs.
response = service.users().messages().list(
userId="me",
q="rfc822msgid:<20260516.12345@example.com>",
maxResults=5,
).execute()
message_ids = [message["id"] for message in response.get("messages", [])]How do you page through Gmail search results safely?
Safe Gmail search pagination keeps the query stable, stores the current nextPageToken, and stops as soon as the business limit is reached. The API permits up to 500 results per page, but production jobs should usually define a smaller total cap such as 200 or 1,000 messages.
Pagination is where direct API code grows quickly. You need a loop for nextPageToken, a result cap, retry behavior for transient failures, and a way to resume without reprocessing the same page twice. If a daily job only needs the newest 200 receipts, fetching 2,000 matches increases quota cost and review time without improving the output.
This Python loop keeps the search query constant and stops after 200 message IDs. It demonstrates the extra state a direct integration owns before it can even call messages.get for subjects, snippets, or bodies.
query = "from:billing@stripe.com has:attachment after:2026/01/01"
page_token = None
message_ids = []
while len(message_ids) < 200:
response = service.users().messages().list(
userId="me",
q=query,
maxResults=min(100, 200 - len(message_ids)),
pageToken=page_token,
).execute()
message_ids.extend(message["id"] for message in response.get("messages", []))
page_token = response.get("nextPageToken")
if not page_token:
breakHow do you turn Gmail search results into readable messages?
Gmail search results become readable only after a second fetch step because messages.list returns lightweight message records. To show a subject, sender, snippet, or body, code must call messages.get for selected IDs, then parse headers and base64url-encoded payload data.
This is the part many examples hide. A search returning 50 message IDs can turn into 50 follow-up calls if your workflow reads every match. For agent workflows, a safer pattern is search first, rank metadata, read 1 to 5 selected messages, and stop. The CLI mirrors that retrieval boundary with separate nylas email search and nylas email read commands.
These commands search first and read one chosen message second. That 2-step shape is intentional: it lets a script or model inspect IDs and snippets before spending more calls on body content.
nylas email search "contract" --from legal@example.com --limit 10 --json
nylas email read <message-id> --jsonHow does the CLI map search across providers?
The CLI gives developers one search command while 6 provider families keep their own search engines and mailbox models. Gmail has q and labels, Microsoft mail has Graph filtering and folder APIs, and IMAP servers expose SEARCH plus mailbox folders. The useful contract is one command surface with provider-specific behavior hidden behind it.
Provider-side behavior in this guide is described from Google documentation and verified CLI help, not from a live end-to-end test on every backend. Gmail-only operators such as category:promotions and rfc822msgid: are direct API syntax here. For cross-provider workflows, prefer CLI flags like --from, --to, --subject, --after, --before, and --has-attachment.
| Need | Gmail API shape | CLI shape |
|---|---|---|
| Sender search | q="from:alice@example.com" | nylas email search "*" --from alice@example.com |
| Unread search | q="is:unread" | nylas email search "*" --unread |
| Attachment search | q="has:attachment" | nylas email search "*" --has-attachment |
| Date window | q="invoice after:2026/01/01 before:2026/02/01" | nylas email search "invoice" --after 2026-01-01 --before 2026-02-01 |
How do labels, folders, and categories affect search quality?
Gmail search quality depends on choosing the right mailbox concept: labels, folders, or categories. Gmail labels are many-to-many tags, provider folders are usually one location, and Gmail categories are system labels assigned by Google. Mixing those 3 mailbox concepts is a common reason searches return too many messages or miss expected messages.
Use full-text search when the user remembers words in the message. Use labels when your workflow has already classified messages. Use categories when the target is a Gmail tab such as Promotions or Social. Use folders when you want a provider-neutral command that also maps well to Outlook, Exchange, Yahoo, iCloud, and IMAP.
A 2-step pattern is usually easiest to debug: list available folders or labels first, then search inside the chosen location. That keeps the folder name visible in logs and avoids hard-coding a Gmail-only label where a cross-provider workflow should use the CLI's folder abstraction.
How do you test Gmail search queries before shipping a job?
Test Gmail search queries with a 4-check loop before putting them in cron, CI, or an agent tool: run a small limit, inspect IDs, read 1 selected message, and save the exact query string. This catches overly broad searches before they read hundreds of message bodies.
The first run should use --limit 5 or --limit 10, not the production cap. Check that the newest 5 matches are the kind of mail you expected, then widen the limit only after the sender, date, folder, and attachment filters behave correctly. For a monthly job, test at least 2 date windows: one with known matches and one empty window.
This smoke test keeps output small and easy to review. The first command proves the search is scoped; the second command reads one candidate. If the selected message is wrong, fix the query before adding pagination or send logic.
nylas email search "invoice" --from billing@example.com --limit 5 --json
nylas email read <message-id> --jsonHow should AI agents use Gmail search safely?
AI agents should use Gmail search as a bounded retrieval step, not as permission to read the whole mailbox. A safe agent loop searches narrowly, limits candidates, inspects metadata, reads only selected messages, and asks for approval before any send or destructive action.
The difference is measurable. An agent that searches 25 messages and reads 3 bodies has a much smaller prompt, cost, and privacy footprint than one that reads 250 bodies first. The search query also becomes an audit artifact: reviewers can see whether the agent searched for from:customer@example.com, has:attachment, or a broad term like contract.
This shell pattern is a good baseline for agent tools. It collects 25 candidate IDs, lets a ranking step choose one, and only then reads body content. The command names link cleanly to audit logs and command allowlists.
nylas email search "contract" --from customer@example.com --limit 25 --json \
| jq -r '.[].id' \
| head -5
nylas email read <selected-message-id> --jsonWhen should you use the Gmail API instead of the CLI?
Use the Gmail API directly when your application needs 1 Gmail-only control plane: raw q operators such as rfc822msgid:, Gmail administration, custom Google Cloud ownership, domain-wide delegation, Pub/Sub watch renewal, or a UI that already embeds Google's OAuth flow. Use the CLI when you want a terminal workflow, agent tool, cron job, or multi-provider script with fewer moving parts.
The decision is not about whether the Gmail API is good. It is about who should own the plumbing. Direct API code owns OAuth setup, query construction, pagination, message reads, base64url decoding, retry behavior, and provider lock-in. The CLI path owns command selection and output handling, while Nylas handles the provider integration behind the scenes.
For most developer automation, start with the CLI path and move to direct Gmail API calls only when you can name the Gmail-only feature. That rule keeps 1-off scripts, AI-agent tools, and CI jobs from becoming long-lived OAuth clients.
What should you build next?
- Gmail API pagination and sync -- nextPageToken, historyId, label scoping, and search result pagination
- List Gmail emails from terminal -- Gmail CLI examples for list, search, read, labels, and JSON output
- Gmail API quotas in 2026 -- method costs, bounded search loops, and safer agent patterns
- Gmail API limits for AI agents -- OAuth, MIME parsing, quotas, and tool-design risks
- Email APIs for AI agents compared -- choose between Gmail API, Graph, IMAP, MCP, and CLI tools
- Email search command -- exact flags for sender, dates, attachments, folders, limits, and JSON output
- Email read command -- read one selected message after a bounded search
- Full command reference -- every email, calendar, contact, webhook, MCP, and audit command
- Gmail API search and filter guide -- Google's provider-native guide for
messages.list,threads.list, and search queries