> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://uploadcare.com/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://uploadcare.com/docs/_mcp/server.

# File tags

> Attach searchable string tags to files at upload time or via the REST API.

Each file can carry a list of string tags. Tags are set at upload time
or managed via dedicated REST API endpoints. They appear in all file info
responses and can be used as a filter in [file search](/docs/file-search/).

> **Info**
>
> Tags require API version **0.7**. All tag endpoints require the
> `Accept: application/vnd.uploadcare-v0.7+json` header.

> **Info**
>
> **SDK support:** Search and file tags are available as native methods in the [Go](/docs/integrations/golang/), [JavaScript](/docs/integrations/javascript/#search-files), [Python](/docs/integrations/python/), and [Ruby](/docs/integrations/ruby/#search-files) SDKs, and via the [CLI](/docs/cli/reference/file/). Other SDKs can call the REST API directly using the examples below.

## Normalization

All tag input is normalized before storage:

| Rule                            | Input                   | Stored as         |
| ------------------------------- | ----------------------- | ----------------- |
| Lowercased                      | `"Summer"`              | `"summer"`        |
| Whitespace stripped             | `" cat "`               | `"cat"`           |
| Duplicates removed (first wins) | `["cat", "Cat", "CAT"]` | `["cat"]`         |
| Empty strings discarded         | `["cat", "", "dog"]`    | `["cat", "dog"]`  |
| Order preserved (first-seen)    | `["z", "a", "m"]`       | `["z", "a", "m"]` |

## Limits and allowed characters

| Constraint         | Value                                |
| ------------------ | ------------------------------------ |
| Max tags per file  | **50**                               |
| Max length per tag | **100 characters**                   |
| Allowed characters | Latin letters, digits, `-`, `_`, `.` |

All limits are evaluated after normalization and deduplication. Tags that contain any other character (spaces, Unicode, `+`, `@`, etc.) are rejected with HTTP 400.

## Setting tags at upload time

Pass `tags` as a form field alongside other upload parameters for
`POST /base/` (direct upload) and `POST /multipart/start/`. The value is a
comma-separated list of tag names.

```bash
curl -L 'https://upload.uploadcare.com/base/' \
     -F "UPLOADCARE_PUB_KEY=$YOUR_PUBLIC_KEY" \
     -F "UPLOADCARE_STORE=1" \
     -F "file=@photo.jpg" \
     -F "tags=cat,animal,cute"
```

Validation errors (too many tags, tag too long, invalid characters) return HTTP **400** and the
upload does not proceed:

```json
{
  "error": {
    "status_code": 400,
    "content": "Too many tags: 51 (max 50).",
    "error_code": "UploadViewsError"
  }
}
```

The standard upload response does **not** include `tags`. To read tags after
upload, use `GET /files/{uuid}/` or `GET /files/{uuid}/tags/`.

## Tags in file info

When tags are enabled, the `tags` field is included in all file info responses
at API version 0.7:

```json
{
  "uuid": "...",
  "original_filename": "photo.jpg",
  "tags": ["cat", "animal"],
  ...
}
```

A file with no tags returns `"tags": []`.

**Endpoints that include `tags`:**

* `GET /files/{uuid}/`: single file info
* `GET /files/`: file list
* Webhook payloads

## REST API

All tag endpoints:

* Require `Accept: application/vnd.uploadcare-v0.7+json`
* Require standard Uploadcare authentication (secret key)
* Are scoped to the authenticated project
* Return **404** for files in other projects or removed files

### Get tags

```
GET /files/{uuid}/tags/
```

Returns the current tag list.

```bash
curl -L 'https://api.uploadcare.com/files/$UUID/tags/' \
     -H "Accept: application/vnd.uploadcare-v0.7+json" \
     -H "Authorization: Uploadcare.Simple $YOUR_PUBLIC_KEY:$YOUR_SECRET_KEY"
```

**Response 200:**

```json
{"tags": ["cat", "animal"]}
```

Returns `{"tags": []}` for a file with no tags.

### Replace tags

```
PUT /files/{uuid}/tags/
```

**Replaces** the entire tag set. Previous tags not in the new list are deleted.
An empty array clears all tags.

```bash
curl -L -X PUT 'https://api.uploadcare.com/files/$UUID/tags/' \
     -H "Accept: application/vnd.uploadcare-v0.7+json" \
     -H "Content-Type: application/json" \
     -H "Authorization: Uploadcare.Simple $YOUR_PUBLIC_KEY:$YOUR_SECRET_KEY" \
     -d '{"tags": ["cat", "animal", "cute"]}'
```

**Response 200:**

```json
{
  "tags": ["cat", "animal", "cute"],
  "added": ["animal", "cute"],
  "deleted": ["old-tag"]
}
```

* `tags`: resulting tag list in storage order
* `added`: tags in the new set but not in the previous set (sorted alphabetically)
* `deleted`: tags in the previous set but not in the new set (sorted alphabetically)

Calling PUT with the same set of tags (regardless of input case or order)
is safe and produces no write: `added` and `deleted` will both be empty.

### Update tags (add and/or remove)

```
PATCH /files/{uuid}/tags/
```

Adds and/or removes tags in a single atomic operation. Both `add` and `delete`
are optional and default to `[]`. Delete is applied first, then add, so a tag
can be removed and re-added in one request.

```bash
curl -L -X PATCH 'https://api.uploadcare.com/files/$UUID/tags/' \
     -H "Accept: application/vnd.uploadcare-v0.7+json" \
     -H "Content-Type: application/json" \
     -H "Authorization: Uploadcare.Simple $YOUR_PUBLIC_KEY:$YOUR_SECRET_KEY" \
     -d '{"add": ["dog", "outdoor"], "delete": ["cat"]}'
```

**Response 200:**

```json
{
  "tags": ["animal", "dog", "outdoor"],
  "added": ["dog", "outdoor"],
  "deleted": ["cat"]
}
```

* `tags`: resulting tag list in storage order
* `added`: tags that were not previously present and were added
* `deleted`: tags that were present and were removed

Tags in `add` that already exist are silently skipped. Tags in `delete` that
are not present are silently ignored. An empty body `{}` is valid and returns
the current state unchanged. If the resulting count would exceed 50, the
request fails with 400.

## Filtering by tags in file search

Use the `tags` key in `POST /files/search/` to filter files by their tags:

```json
{
  "tags": {
    "any": ["cat", "dog"],
    "all": ["outdoor"],
    "none": ["archived"]
  }
}
```

| Key    | Semantics                                     |
| ------ | --------------------------------------------- |
| `any`  | File must have **at least one** of these tags |
| `all`  | File must have **all** of these tags          |
| `none` | File must have **none** of these tags         |

All three keys are optional, but at least one must be non-empty. Tag values are
normalized before the search query is built.

Tag changes are indexed asynchronously, like any other file change. See [indexing delay](/docs/file-search/) for how long to wait before retrying.

Get `$YOUR_PUBLIC_KEY` and `$YOUR_SECRET_KEY` from
[API keys](https://app.uploadcare.com/projects/-/api-keys/).