> 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 search

> Find files by name, metadata, MIME type, tags, size, upload date, or file type using full-text and exact matching.

Search files in your project with `POST /files/search/`. A single request can
combine full-text search, exact matching, range filters, and tag filters. This
page explains the query syntax, how conditions combine, and shows worked
request/response examples.

> **Info**
>
> File search requires API version **0.7**. Send the
> `Accept: application/vnd.uploadcare-v0.7+json` header with every request.

> **Note**
>
> **Note:** Newly uploaded and recently changed files, including tag updates, are indexed
> asynchronously and may not appear in (or disappear from) search results immediately.
> Allow up to 15 seconds and retry if the result set doesn't reflect the change yet.

> **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.

## Quick start

```bash
curl -L -X POST 'https://api.uploadcare.com/files/search/' \
     -H "Accept: application/vnd.uploadcare-v0.7+json" \
     -H "Content-Type: application/json" \
     -H "Authorization: Uploadcare.Simple $YOUR_PUBLIC_KEY:$YOUR_SECRET_KEY" \
     -d '{"query": "vacation"}'
```

At least one search condition is required. With no conditions, the request
returns `400`.

## Search conditions

A request body is a JSON object. Each of the following keys is optional, but at
least one must be present.

| Key                 | Type    | Purpose                                                                                                            |
| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
| `query`             | string  | Quick full-text search across filename, metadata, and MIME type at once (plus a UUID match). Minimum 4 characters. |
| `phrase`            | object  | Full-text search scoped to specific fields.                                                                        |
| `exact`             | object  | Exact value matching per field.                                                                                    |
| `datetime_uploaded` | object  | Range filter on upload date (ISO 8601).                                                                            |
| `size`              | object  | Range filter on file size in bytes.                                                                                |
| `is_image`          | boolean | `true` returns images only; `false` excludes images.                                                               |
| `tags`              | object  | Filter by file [tags](/docs/file-tags/) with `any` / `all` / `none`.                                               |

Plus these modifiers:

| Key         | Type    | Purpose                                                           |
| ----------- | ------- | ----------------------------------------------------------------- |
| `fuzziness` | boolean | Typo-tolerant matching for `query` and `phrase`. Default `false`. |
| `sort`      | array   | 1–4 sort keys; prefix with `-` for descending.                    |

Pagination and `appdata` are controlled with query parameters. See
[Pagination](#pagination) and [Including appdata](#including-appdata).

## How conditions combine

Understanding the boolean model is the key to building complex queries.

* **Different top-level conditions are combined with AND.** A request with
  `phrase`, `size`, and `is_image` returns files that match *all three*.
* **There is no top-level OR and no general NOT.** You cannot ask for
  "filename contains `photo` OR is an image". The only ways to express OR and
  NOT are the narrow cases listed below.

| Where                                                                               | Logic                      |
| ----------------------------------------------------------------------------------- | -------------------------- |
| Multiple top-level keys (`phrase` + `size` + …)                                     | **AND**                    |
| Multiple fields inside `phrase`                                                     | **AND**                    |
| Multiple values in an `exact` field, e.g. `"original_filename": ["a.jpg", "b.png"]` | **OR** (matches any)       |
| Multiple keys inside `exact`                                                        | **AND**                    |
| `query` across its fields                                                           | **OR** (matches any field) |
| `tags.any`                                                                          | **OR** (at least one)      |
| `tags.all`                                                                          | **AND** (every tag)        |
| `tags.none`                                                                         | **NOT** (exclude)          |
| `gte` + `lt` inside `datetime_uploaded` / `size`                                    | **AND** (a range)          |

## Full-text search: `query` and `phrase`

`query` and `phrase` run full-text search. `query` searches all full-text fields
(`original_filename`, `metadata`, `detected_mime_type`) at once, while `phrase`
targets specific fields:

```json
{
  "phrase": {
    "original_filename": "report",
    "detected_mime_type": "pdf"
  }
}
```

Both are **substring-aware**: a term matches anywhere inside a word, not only at
the start. Searching `photo` finds `photo.jpg`, `photographer.png`, and
`holiday-photo.jpg`. Filenames are also split on separators such as `-`, `_`,
and case boundaries before matching.

A few rules follow from this:

* **Minimum 4 characters** for every `query` and `phrase` value. Shorter terms
  cannot match. Use [`exact`](#exact-matching) for short or precise values.
* Very long terms (over \~15 characters) may not match via full-text; prefer
  `exact` for full filenames or full MIME types.
* `phrase.metadata` searches across **all** metadata keys and values at once. To
  match a specific metadata key, use `exact` with `metadata[key]`.

`query` also matches against the file UUID with a relevance boost: if you paste a
UUID into `query`, that exact file is returned at the top of the results while
the rest of the term is still matched as full text.

### Typo tolerance with `fuzziness`

Set `fuzziness: true` to tolerate small spelling differences in `query` and
`phrase` (it does not affect `exact`). The allowed edit distance scales with
term length (roughly 1 edit for 3–5 characters, 2 for longer).

```json
{ "query": "barbarain", "fuzziness": true }
```

> **Tip**
>
> **Tip:** Fuzzy matching significantly increases query latency. Enable it only when you
> need it.

## Exact matching

`exact` matches stored values exactly. Each field takes a non-empty array;
multiple values in one field are OR-ed:

```json
{
  "exact": {
    "original_filename": ["logo.png", "icon.png"],
    "detected_mime_type": ["image/png"],
    "uuid": ["21975c81-7f57-4c7a-aef9-acfe28779f78"]
  }
}
```

To match an arbitrary metadata field, use `metadata[key]` syntax:

```json
{
  "exact": {
    "metadata[camera]": ["Canon"],
    "metadata[order_id]": ["12345", "12346"]
  }
}
```

A field cannot appear in both `phrase` and `exact` in the same request.

## Range filters: `datetime_uploaded` and `size`

Both accept the operators `gt`, `gte`, `lt`, and `lte`. At least one operator is
required; combine two to form a range.

```json
{
  "datetime_uploaded": { "gte": "2024-01-01T00:00:00Z", "lt": "2025-01-01T00:00:00Z" },
  "size": { "gte": 1024, "lte": 10485760 }
}
```

`datetime_uploaded` values are ISO 8601 timestamps; `size` is in bytes.

## Filter by tags

Use `tags` to filter by file [tags](/docs/file-tags/). All three keys are
optional, but at least one must be non-empty. Tag values are normalized
(lowercased, whitespace-stripped) before matching.

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

## Sorting

`sort` is an array of 1–4 keys. Prefix a key with `-` for descending order. You
cannot pass both directions of the same key, and keys must be unique.

| Key                                        | Sorts by    |
| ------------------------------------------ | ----------- |
| `score` / `-score`                         | Relevance   |
| `datetime_uploaded` / `-datetime_uploaded` | Upload date |
| `size` / `-size`                           | File size   |
| `original_filename` / `-original_filename` | Filename    |

```json
{ "sort": ["-datetime_uploaded", "original_filename"] }
```

If `sort` is omitted, results are ordered by relevance. **For filter-only
requests** (no `query` or `phrase`), there is no relevance to rank by, so the
order is undefined. Always pass an explicit `sort` in that case.

## Pagination

Pagination uses the `limit` and `offset` **query parameters** (not the body):

| Parameter | Default | Range                                   |
| --------- | ------- | --------------------------------------- |
| `limit`   | `20`    | 1–100                                   |
| `offset`  | `0`     | `offset + limit` must not exceed `1000` |

```bash
curl -L -X POST 'https://api.uploadcare.com/files/search/?limit=50&offset=50' \
     -H "Accept: application/vnd.uploadcare-v0.7+json" \
     -H "Content-Type: application/json" \
     -H "Authorization: Uploadcare.Simple $YOUR_PUBLIC_KEY:$YOUR_SECRET_KEY" \
     -d '{"query": "invoice"}'
```

The response includes ready-to-use `next` and `previous` URLs. Results beyond
the first 1000 cannot be paged through; narrow your query instead of deep
paging.

## Including appdata

Pass `include=appdata` as a query parameter to embed
[application data](/docs/file-analysis/) (such as moderation or virus-scan
results) in each result:

```
POST /files/search/?include=appdata
```

`appdata` is currently the only supported value.

## Response

```json
{
  "next": "https://api.uploadcare.com/files/search/?limit=20&offset=20",
  "previous": null,
  "total": 42,
  "per_page": 20,
  "results": [
    {
      "uuid": "575ed4e8-f4e8-4c14-a58b-1527b6d9ee46",
      "original_filename": "vacation-photo.jpg",
      "size": 145212,
      "highlight": {
        "original_filename": ["<em>vacation</em>-photo.jpg"]
      }
    }
  ]
}
```

* `total`: number of matching files (may be approximate for very large result
  sets).
* `results`: file objects in the same shape as
  [file info](/docs/managing-files/), plus a `highlight` field.
* `highlight`: for fields that matched a full-text condition, the matched
  tokens wrapped in `<em>` tags. `original_filename` and `detected_mime_type`
  are arrays of strings; `metadata` is an object keyed by metadata field name.
  Absent for filter-only matches.

## Worked examples

### Recent images, largest first

```json
{
  "is_image": true,
  "datetime_uploaded": { "gte": "2024-06-01T00:00:00Z" },
  "sort": ["-size"]
}
```

### PDFs whose name contains "invoice", uploaded in 2024

```json
{
  "phrase": { "original_filename": "invoice" },
  "exact": { "detected_mime_type": ["application/pdf"] },
  "datetime_uploaded": {
    "gte": "2024-01-01T00:00:00Z",
    "lt": "2025-01-01T00:00:00Z"
  }
}
```

### Outdoor photos tagged cat or dog, excluding archived

```json
{
  "is_image": true,
  "tags": {
    "all":  ["outdoor"],
    "any":  ["cat", "dog"],
    "none": ["archived"]
  },
  "sort": ["-datetime_uploaded"]
}
```

### Files with a specific metadata value

```json
{
  "exact": { "metadata[order_id]": ["12345"] }
}
```

## Errors

A `400` response body is a JSON object mapping field names to arrays of error
strings. Errors that span more than one field appear under `non_field_errors`.

```json
{ "non_field_errors": ["At least one search criterion must be specified."] }
```

| Example error                                                           | Cause                                     |
| ----------------------------------------------------------------------- | ----------------------------------------- |
| `At least one search criterion must be specified.`                      | Empty request body.                       |
| `Fields cannot appear in both phrase and exact: ['original_filename'].` | Same field used in `phrase` and `exact`.  |
| `Must be at least 4 characters.`                                        | A `query` or `phrase` value is too short. |
| `offset + limit must be <= 1000.`                                       | Paging past the 1000-result window.       |
| `Sort keys must be unique.`                                             | Duplicate keys in `sort`.                 |

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