# Gmail labels API: create and manage labels

Source: https://developer.nylas.com/docs/cookbook/email/gmail-labels-api/

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?

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](https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.labels), 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?

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.

```python
from google.oauth2.credentials import Credentials
from 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 colors
new_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",
    q="from:billing@stripe.com after:2026/01/01",
).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?

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?

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.

```bash
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>'
```

```json
{
  "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](/docs/cookbook/email/organize-folders/) covers that move-vs-tag distinction across providers, and [Using the Folders API](/docs/v3/email/folders/) documents every field, including the system-folder attribute mapping.

## 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](/docs/cookbook/email/messages/list-messages-google/) shows both patterns with code.

## What's next

- [Organize email with folders and labels](/docs/cookbook/email/organize-folders/) covers the cross-provider folder model and moving messages.
- [How to list Google email messages](/docs/cookbook/email/messages/list-messages-google/) filters messages by label and Gmail search operators.
- [Using the Folders API](/docs/v3/email/folders/) is the full reference, including common attributes.
- [Gmail API quotas](/docs/cookbook/email/gmail-api-quotas/) prices native `labels` and `messages` calls in quota units.