Guide
Gmail API ETag, If-Match, and history.list
Exact answers for Gmail API ETag, If-Match, If-None-Match, messages.get, 304 Not Modified, and history.list when deleting labels. Also points to a provider-neutral alternative if you do not want to hand-roll mailbox sync.
Written by Qasim Muhammad Staff SRE
Reviewed by Nick Barraclough
Exact answers to the top Gmail API queries
These are the exact search intents driving impressions to this page. The short answers below are based on Google's Gmail API reference and sync guide, not on generic HTTP advice.
| Query | Short answer | Source |
|---|---|---|
gmail api etag | The documented Gmail Message schema includes historyId but does not document an etag field. | Message resource |
gmail.users.messages.get if-none-match | The users.messages.get method page documents path params and response shape, but it does not document If-None-Match or a 304 contract. | users.messages.get |
gmail api if-match | Gmail's method docs do not document If-Match behavior for message or label endpoints, so do not rely on it as a Gmail-specific concurrency contract. | Gmail REST reference |
delete a label history.list gmail | history.list documents label changes on messages, not a label resource deleted event. Refresh users.labels.list after deleting custom labels. | history.list |
Does users.messages.get return an etag field?
Not in the documented JSON resource. Google's Message reference lists fields such as id, threadId, labelIds, snippet, and historyId. It does not list an etag property. That matters because many search queries assume Gmail exposes a stable message ETag in the resource body. The docs do not support that assumption.
If your HTTP client exposes response headers, treat that separately from the documented Gmail resource schema. The safe, Gmail-specific change marker in the official docs is historyId.
curl -H "Authorization: Bearer $TOKEN" \
"https://gmail.googleapis.com/gmail/v1/users/me/messages/$MESSAGE_ID?format=metadata&fields=id,threadId,labelIds,historyId,snippet" \
| jq
# Documented response fields look like:
# {
# "id": "18c7...",
# "threadId": "18c7...",
# "labelIds": ["INBOX", "UNREAD"],
# "historyId": "442991",
# "snippet": "Preview text..."
# }What Gmail documents about If-Match, If-None-Match, 304, and 412
General HTTP docs explain what If-Match, If-None-Match, 304 Not Modified, and 412 Precondition Failed mean. Gmail's own method pages are narrower: the reference for users.messages.get, users.messages.modify, users.labels.patch, and users.labels.delete does not document conditional request behavior for those headers.
If-None-Matchis not documented onusers.messages.get.If-Matchis not documented on the Gmail message or label methods most people search for.304and412are not described as part of the Gmail contract on those method pages.
In other words: use generic HTTP documentation as background knowledge, but build Gmail sync and concurrency around the behaviors Google actually documents for Gmail.
How history.list actually relates to label deletion
history.list is Gmail's documented incremental sync mechanism. Google's sync guide says to store the latest historyId, then call users.history.list later with startHistoryId. If that start value is out of date or invalid, the API returns 404 and you must do a full sync.
The history response documents message-level change buckets such as messagesAdded, messagesDeleted, labelsAdded, and labelsRemoved. That means Gmail history is very useful when a label is added to or removed from a message. It is not documented as a change feed for the label catalog itself.
curl -H "Authorization: Bearer $TOKEN" \
"https://gmail.googleapis.com/gmail/v1/users/me/history?startHistoryId=$START_HISTORY_ID" \
| jq '.history[] | {id, messagesAdded, messagesDeleted, labelsAdded, labelsRemoved}'
# If START_HISTORY_ID is too old, Gmail returns HTTP 404.
# At that point, do a full sync and store a newer historyId.What to do after deleting a custom Gmail label
If you call users.labels.delete to remove a custom label, do not wait for a documented "label deleted" event from history.list. The Gmail history reference does not describe one. The safer pattern is:
- Delete the custom label with the Labels API.
- Refresh the label catalog with
users.labels.list. - Reconcile any missing label IDs in your local cache.
- Continue using
history.listfor message-level changes.
# Refresh the current label catalog after deleting a custom label
curl -H "Authorization: Bearer $TOKEN" \
"https://gmail.googleapis.com/gmail/v1/users/me/labels" \
| jq '.labels[] | {id, name, type}'
# Compare the returned IDs to your cached label list.
# Missing IDs are deleted labels.The safer Gmail architecture to build around
If you are building directly on Gmail, the documented path is straightforward:
- Use
historyIdandhistory.listfor incremental sync. - Expect a
404fallback path when the storedstartHistoryIdages out. - Re-fetch the label catalog separately when label definitions change.
- Avoid depending on undocumented Gmail-specific ETag semantics for correctness.
If you need a provider-neutral alternative, the rest of this site shows how to push mailbox events through Nylas so the same integration pattern works across Gmail, Outlook, Exchange, Yahoo, iCloud, and IMAP without hand-rolling each provider's sync edge cases.
Next steps
- Gmail Message resource - the documented fields for
users.messages.get - Gmail history.list reference - documented history buckets and
startHistoryIdbehavior - Gmail sync guide - Google's documented incremental sync flow and 404 fallback
- Command reference - Nylas CLI commands for mail, calendars, webhooks, and MCP
- Gmail API Pagination and Sync - next page tokens, history windows, and sync fallbacks
- Why Gmail API Breaks AI Agents - quotas, OAuth friction, and retry overhead
- List Gmail Emails - inspect Gmail data without writing API client code first
- Give AI Agents Email Access via MCP - use one MCP surface across mail providers