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

VerifiedCLI 3.1.1 · Gmail · last tested April 11, 2026

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.

QueryShort answerSource
gmail api etagThe documented Gmail Message schema includes historyId but does not document an etag field.Message resource
gmail.users.messages.get if-none-matchThe 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-matchGmail'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 gmailhistory.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-Match is not documented on users.messages.get.
  • If-Match is not documented on the Gmail message or label methods most people search for.
  • 304 and 412 are 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:

  1. Delete the custom label with the Labels API.
  2. Refresh the label catalog with users.labels.list.
  3. Reconcile any missing label IDs in your local cache.
  4. Continue using history.list for 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 historyId and history.list for incremental sync.
  • Expect a 404 fallback path when the stored startHistoryId ages 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