> For the complete documentation index, see [llms.txt](https://academy.shade.inc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://academy.shade.inc/developers/using-the-api/searching-and-filtering-assets.md).

# Searching and filtering assets

Find and list assets with POST /search: AI search, filters on paths, types, dates, and metadata, sorting, and paging through results.

`POST /search` is how you find assets in a drive, whether you're running an AI search, listing a folder, or pulling every file with a given metadata value. This guide covers every part of the request.

## Prerequisites

* A Shade API key, stored in the `SHADE_KEY` environment variable
* The ID of the drive you want to search. See [Finding your IDs](/developers/get-started/integrating-with-shade.md)

## Base information

* **Endpoint**: `POST https://api.shade.inc/search`
* **Authentication**: your API key in the `Authorization` header, with no `Bearer` prefix
* **Response**: a JSON array of assets. Each asset has the same fields as `GET /assets/{asset_id}`

## 1. Your first search

Send a natural-language `query`. Shade's AI search matches it against what's in the media, not only the file names.

```bash
curl -X POST "https://api.shade.inc/search" \
  -H "Authorization: $SHADE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "drive_id": "YOUR_DRIVE_ID",
    "query": "drone shot of a coastline at sunset",
    "limit": 25
  }'
```

Leave `query` out to get every asset that matches your filters. Without a `query` or `sort`, results are sorted by name.

## 2. Filters

`filters` is a list of conditions, and results must match all of them. Every filter has three parts:

* `id`: the field to filter on
* `clause`: how to compare, such as `is`, `is under`, or `after`
* `options`: the values to compare against, always as a list

```json
{
  "id": "type",
  "clause": "is",
  "options": ["VIDEO"]
}
```

### Built-in fields

| Field         | `id`            | Clauses                                                                                          | `options`                                                           |
| ------------- | --------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
| Folder        | `path`          | `is directly in`, `is under`, `is not in`, `is`, `is not`, `contains`, `starts with`             | Folder paths starting with the drive ID                             |
| File type     | `type`          | `is`, `is not`                                                                                   | `VIDEO`, `IMAGE`, `AUDIO`, `DOCUMENT`, and other `AssetType` values |
| File name     | `name`          | `is`, `is not`, `contains`, `does not contain`, `starts with`, `does not start with`, `end with` | Text                                                                |
| Extension     | `extension`     | `is`, `is not`, `contains`, `starts with`, `end with`                                            | Extensions without the dot, such as `mov`                           |
| Date created  | `created`       | `is`, `is not`, `before`, `after`, `between`                                                     | ISO 8601 dates, or epoch milliseconds                               |
| Date modified | `updated`       | `is`, `is not`, `before`, `after`, `between`                                                     | ISO 8601 dates, or epoch milliseconds                               |
| File size     | `size_bytes`    | `is`, `is not`, `<`, `<=`, `>`, `>=`                                                             | Sizes in bytes                                                      |
| Comment count | `comment_count` | `is`, `is not`, `<`, `<=`, `>`, `>=`                                                             | Whole numbers                                                       |
| Collection    | `collection`    | `is directly in`, `is not in`                                                                    | Collection IDs                                                      |
| Person        | `individual`    | `is`, `is not`                                                                                   | Individual IDs, from `GET /individuals`                             |

Most fields also support `is empty` and `is not empty`, which take an empty `options` list.

{% hint style="info" %}
`is directly in` matches files in the folder itself, while `is under` also includes every subfolder. Use `is not in` to exclude a folder and everything under it.
{% endhint %}

### Metadata fields

To filter on a custom metadata field, use the attribute's ID as the filter `id`. Get the IDs from `GET /workspaces/drives/{drive_id}/metadata`. The clauses depend on the field's type:

| Field type               | Clauses                                                                   | `options`                                             |
| ------------------------ | ------------------------------------------------------------------------- | ----------------------------------------------------- |
| `single_select`          | `is`, `is not`                                                            | Option IDs. Several IDs match any of them             |
| `multi_select`           | `includes`, `does not include`                                            | Option IDs. Several IDs match assets with any of them |
| `str`                    | `is`, `is not`, `contains`, `does not contain`, `starts with`, `end with` | Text                                                  |
| `int`, `float`, `rating` | `is`, `is not`, `<`, `<=`, `>`, `>=`                                      | Numbers                                               |
| `datetime`               | `is`, `is not`, `before`, `after`, `between`                              | ISO 8601 dates                                        |
| `bool`                   | `is true`, `is false`                                                     | Leave empty                                           |
| `relation`               | `includes`, `does not include`, `is`, `is not`                            | Record or asset IDs                                   |

Every type also supports `is empty` and `is not empty`.

{% hint style="warning" %}
A clause a field doesn't support is ignored rather than rejected, so that filter returns unfiltered results. If a filter seems to have no effect, check the clause against these tables.
{% endhint %}

### Examples

**Videos anywhere under a folder:**

```json
{
  "drive_id": "YOUR_DRIVE_ID",
  "filters": [
    { "id": "path", "clause": "is under", "options": ["/YOUR_DRIVE_ID/Fall Campaign"] },
    { "id": "type", "clause": "is", "options": ["VIDEO"] }
  ]
}
```

**Files marked "Needs Review" that were created this year:**

```json
{
  "drive_id": "YOUR_DRIVE_ID",
  "filters": [
    { "id": "STATUS_ATTRIBUTE_ID", "clause": "is", "options": ["NEEDS_REVIEW_OPTION_ID"] },
    { "id": "created", "clause": "after", "options": ["2026-01-01T00:00:00Z"] }
  ]
}
```

**Files over 1 GB that nobody has commented on:**

```json
{
  "drive_id": "YOUR_DRIVE_ID",
  "filters": [
    { "id": "size_bytes", "clause": ">", "options": ["1073741824"] },
    { "id": "comment_count", "clause": "is", "options": ["0"] }
  ]
}
```

**Footage of a specific person:**

```json
{
  "drive_id": "YOUR_DRIVE_ID",
  "query": "talking to camera",
  "filters": [
    { "id": "individual", "clause": "is", "options": ["INDIVIDUAL_ID"] }
  ]
}
```

## 3. Other ways to scope a search

| To search                               | Send                               |
| --------------------------------------- | ---------------------------------- |
| Inside a collection                     | `"collection_id": "COLLECTION_ID"` |
| For assets that look like another asset | `"similar_asset_id": "ASSET_ID"`   |

Files in the drive's trash are always left out of results.

## 4. Sorting

Set `sort` to a field `id`, such as `name`, `created`, `updated`, `size_bytes`, or a metadata attribute ID. Add a `-` prefix for descending order:

```json
{ "drive_id": "YOUR_DRIVE_ID", "sort": "-created" }
```

{% hint style="warning" %}
When `sort` is set, `query` is ignored. To rank by relevance to a query, leave `sort` out.
{% endhint %}

## 5. Paging through results

`limit` sets the page size, which defaults to 100. `page` is zero-based. Keep requesting pages until one comes back with fewer than `limit` results:

```python
import os

import requests

SHADE_API = "https://api.shade.inc"
HEADERS = {"Authorization": os.environ["SHADE_KEY"]}
PAGE_SIZE = 100


def search_all(drive_id: str, filters: list[dict]) -> list[dict]:
    assets: list[dict] = []
    page = 0
    while True:
        response = requests.post(
            f"{SHADE_API}/search",
            headers=HEADERS,
            json={
                "drive_id": drive_id,
                "filters": filters,
                "sort": "created",
                "limit": PAGE_SIZE,
                "page": page,
            },
            timeout=60,
        )
        response.raise_for_status()
        batch = response.json()
        assets.extend(batch)
        if len(batch) < PAGE_SIZE:
            return assets
        page += 1


videos = search_all(
    "YOUR_DRIVE_ID",
    [{"id": "type", "clause": "is", "options": ["VIDEO"]}],
)
print(f"{len(videos)} videos")
```

Sort by a stable field such as `created` while paging, so results don't shift between pages.

## 6. Downloading search results

`POST /search/download-urls` returns signed download URLs for up to 1,000 assets at a time, picked by ID or by a search:

```bash
curl -X POST "https://api.shade.inc/search/download-urls" \
  -H "Authorization: $SHADE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "drive_id": "YOUR_DRIVE_ID",
    "origin_type": "SOURCE",
    "search": {
      "path": "/YOUR_DRIVE_ID/Fall Campaign/Deliverables",
      "recursive": true,
      "filters": [{ "id": "type", "clause": "is", "options": ["VIDEO"] }]
    }
  }'
```

Each result has the asset's `id`, `path`, and `url`, or an `error` if that asset couldn't be signed. Use `"origin_type": "PROXY"` for the lightweight preview videos instead of the originals.

## Tips

* Get attribute and option IDs from `GET /workspaces/drives/{drive_id}/metadata` once and reuse them, rather than looking them up for every search.
* Paths in filters start with the drive ID, the same as the `path` field on every asset.
* To look up files, folders, collections, people, and views by name, use `POST /search/simple`.

## Related

* [Metadata guide](/developers/using-the-api/metadata-guide.md)
* [Working with assets](/developers/using-the-api/working-with-assets.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://academy.shade.inc/developers/using-the-api/searching-and-filtering-assets.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `build a script that syncs our docs to a CMS` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
