Gmail doesn’t use folders. It uses labels, and a single message can carry several at once. The Gmail API exposes them through the users.labels resource, with rules that surprise developers: about 14 system labels you can’t touch, a 5,000-label cap per mailbox, and color values restricted to a preset palette. This page covers the native API surface, then the unified alternative.
How does the Gmail API represent labels?
Section titled “How does the Gmail API represent labels?”A Gmail label is a tag attached to a message rather than a container that holds it. The Gmail API represents each label as a users.labels resource with an id, name, type (system or user), and optional color properties. Accounts start with roughly 14 system labels, and Google’s Gmail Help caps user-created labels at 5,000 per mailbox.
According to the users.labels reference, system labels like INBOX, SENT, DRAFT, SPAM, and TRASH can’t be created, deleted, or renamed; the API rejects the attempt with a 400 error. System labels use uppercase IDs, while user labels get generated IDs like Label_42. Label names accept forward slashes for nesting, so Project/Alpha renders as a nested label in the Gmail UI.
How do you create and apply labels with the Gmail API?
Section titled “How do you create and apply labels with the Gmail API?”Creating a label is a POST to users.labels.create with a name and optional visibility and color settings; applying one is a POST to users.messages.modify with an addLabelIds array. Creation needs the gmail.labels OAuth scope, while modifying messages needs the broader gmail.modify scope.
The example below uses google-api-python-client to create a colored label and apply it to a search result in bulk. The batchModify endpoint accepts up to 1,000 message IDs per request and is idempotent: re-applying an existing label is a no-op, not an error. Before any of this runs, the Google Cloud setup (project, consent screen, credentials) takes 15-20 minutes.
from google.oauth2.credentials import Credentialsfrom googleapiclient.discovery import build
creds = Credentials.from_authorized_user_file( "token.json", scopes=["https://www.googleapis.com/auth/gmail.modify"],)service = build("gmail", "v1", credentials=creds)
# Create a nested label with preset colorsnew_label = service.users().labels().create( userId="me", body={ "name": "Receipts/2026", "labelListVisibility": "labelShow", "messageListVisibility": "show", "color": {"textColor": "#ffffff", "backgroundColor": "#4a86e8"}, },).execute()
# Find matching messages and label them in one batch (up to 1,000 IDs)results = service.users().messages().list( userId="me",).execute()ids = [msg["id"] for msg in results.get("messages", [])]
if ids: service.users().messages().batchModify( userId="me", body={"ids": ids, "addLabelIds": [new_label["id"]]}, ).execute()One sharp edge: the color values must come from Google’s preset palette documented in the users.labels reference. Arbitrary hex values outside that set are rejected with a 400 error, even though the fields look like free-form hex strings.
What’s the difference between system and user labels?
Section titled “What’s the difference between system and user labels?”System labels are built into every Gmail account and serve fixed functions; user labels are the ones you create and manage. The API enforces the split: attempts to delete or rename a system label fail with 400, and creating labels past the 5,000-per-mailbox cap fails too.
| Behavior | System labels | User labels |
|---|---|---|
| ID format | Uppercase (INBOX, SENT) | Label_N (Label_42) |
| Create / delete | Not allowed (400 error) | Allowed, up to 5,000 |
| Rename | Not allowed | Allowed with an update call |
| Custom colors | No | Preset palette only |
| Nesting | Fixed hierarchy | Slash (/) in the name |
The CATEGORY_* labels (PERSONAL, SOCIAL, UPDATES, FORUMS, PROMOTIONS) deserve special care. They correspond to Gmail’s inbox tabs, and a message can sit in a category while carrying user labels. Removing CATEGORY_PROMOTIONS from a message moves it to the Primary tab, a side effect that surprises developers expecting a plain label removal.
How do you manage Gmail labels with the Nylas API?
Section titled “How do you manage Gmail labels with the Nylas API?”The Nylas Folders API maps Gmail labels onto its folder resource, so GET /v3/grants/{grant_id}/folders returns every system and user label and POST /v3/grants/{grant_id}/folders creates one. Gmail-specific fields survive the mapping: each folder object carries system_folder (true for Google-created labels), plus the text_color and background_color values on user labels.
The request below lists every label on a connected Gmail account. The response uses the same shape as Outlook folders or IMAP mailboxes, so a parser written once handles all 6 providers, and there’s no Google Cloud project or consent-screen review in the loop.
curl --request GET \ --url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/folders" \ --header 'Accept: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>'{ "request_id": "5fa64c92-e840-4357-86b9-2aa6d43338e5", "data": [ { "id": "INBOX", "grant_id": "5d3qmne77delpuuvqpiqutca2h", "name": "Inbox", "system_folder": true, "attributes": ["\\Inbox"], "object": "folder" }, { "id": "Label_42", "grant_id": "5d3qmne77delpuuvqpiqutca2h", "name": "Receipts/2026", "system_folder": false, "text_color": "#ffffff", "background_color": "#4a86e8", "object": "folder" } ]}Applying labels works through the message’s folders field, where the array sets the full label list in one PUT request. The folders and labels recipe covers that move-vs-tag distinction across providers, and Using the Folders API documents every field, including the system-folder attribute mapping.
Things to know about Gmail labels
Section titled “Things to know about Gmail labels”The 5,000-label cap is real and enforced; large CRM-style integrations that create one label per customer hit it faster than expected, so prefer a few stable labels plus metadata on your side. Label IDs are stable for the life of the account, but display names aren’t unique: deleting and recreating a label with the same name produces a new Label_N ID, which breaks any code that cached the old one.
Filtering messages by label works at list time, too. Pass a label’s ID as the in query parameter on the messages endpoint, or use Gmail search operators like label:receipts-2026 through search_query_native; the Google messages recipe shows both patterns with code.
What’s next
Section titled “What’s next”- Organize email with folders and labels covers the cross-provider folder model and moving messages.
- How to list Google email messages filters messages by label and Gmail search operators.
- Using the Folders API is the full reference, including common attributes.
- Gmail API quotas prices native
labelsandmessagescalls in quota units.