> ## Documentation Index
> Fetch the complete documentation index at: https://aspect.inc/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Searching and filtering

> Find files by name, search visual content and spoken words, and combine metadata filters with ranked search through Aspect's API or MCP server.

Use the CLI to browse project paths and retrieve files. Use Aspect's API or [MCP server](/docs/api-reference/mcp) to search indexed content and metadata. **CLI v0.1.15 does not have a native `search` command.** The examples below distinguish terminal filtering of `aspect ls` output from content search requests.

## Filter a directory listing

`aspect ls` lists one level. Its JSON entries include `id`, `name`, and `type`; asset entries can also include `size` in bytes.

With [jq](https://jqlang.org/) installed, find filenames containing a word:

```bash theme={null}
aspect ls "aspect://Acme/Documentary/Footage" --json |
  jq '[.[] | select(.type == "asset" and (.name | ascii_downcase | contains("interview")))]'
```

Or select files at least 1 GB in size:

```bash theme={null}
aspect ls "aspect://Acme/Documentary/Footage" --json |
  jq '[.[] | select(.type == "asset" and (.size // 0) >= 1000000000)]'
```

These filters run locally on the returned listing. They do not inspect transcripts, search nested folders, or perform semantic matching. Browse a child folder explicitly, or use project search below.

## Choose a content channel

Search requests contain one or more **signals**. Each signal names a channel and the text to match:

| Channel | Searches | Example query |
| - | - | - |
| `visual` | Visible scenes, objects, actions, settings, and appearance | `person holding a camera outdoors` |
| `transcript` | Spoken words, dialogue, and narration, using text and semantic matching | `discussing the production budget` |
| `document` | Written text inside indexed document pages | `payment terms` |
| `filename` | Asset names, including ranked exact, prefix, substring, and fuzzy matches | `interview_final` |

Use short, direct descriptions. Transcript search concerns speech; a query for `applause` is not an audio-event detector. Filename search does not search folder paths and is not a glob or regular expression. For a strict filename condition, use the **Name** metadata filter described below.

Content channels depend on the asset's available processing results. A recently uploaded file may have a name before its transcript or visual analysis is ready.

## Run a search from the terminal

First find your project ID. This returns the projects in a workspace:

```bash theme={null}
aspect ls "aspect://Acme" --json
```

Set `PROJECT_ID` to the selected project's UUID, and configure `ASPECT_API_KEY` as described in [Authentication](/docs/cli/authentication). Raw `curl` requests use that key directly, independently of whichever desktop account the CLI uses.

```bash theme={null}
PROJECT_ID="your-project-uuid"

jq -n --arg project_id "$PROJECT_ID" '{
  project_id: $project_id,
  spec: {
    signals: [
      {id: "visual-1", signal: "visual", query: "person holding a camera outdoors"}
    ],
    filters: []
  }
}' > request.json

curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $ASPECT_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @request.json \
  https://api.aspect.inc/search/run > results.json

jq '.assets[] | {id, name, score: .search.score, matches: .search.moments}' results.json
```

This is a complete request. The following recipes show only its `spec` value unless stated otherwise. Keep `project_id` at the top level when sending them.

### Combine visual and spoken content

```json theme={null}
{
  "signals": [
    {"id": "visual-1", "signal": "visual", "query": "person holding a camera", "weight": 0.7},
    {"id": "transcript-1", "signal": "transcript", "query": "production budget", "weight": 0.3}
  ],
  "filters": []
}
```

Signals contribute to **ranking**. Combining them does not require every returned asset to match every signal. The weights give visual matching more influence in this example; they are not minimum confidence thresholds or percentages of required matches. Omit weights for equal relative importance, and omit a channel when you do not need it.

Matching happens at the asset level. A visual match and a spoken match can occur at different times in the same video. Inspect their individual moments before assuming that someone says a phrase while the requested action is on screen. Use metadata filters for mandatory constraints.

## Discover metadata filters

Filters refer to attribute UUIDs. Select fields also use **option UUIDs**, rather than their displayed labels. Retrieve the available attributes for your project:

```bash theme={null}
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $ASPECT_API_KEY" \
  "https://api.aspect.inc/metadata/attributes?project_id=$PROJECT_ID" > attributes.json

jq '.[] | select(.is_filterable) | {id, name, data_type, options}' attributes.json
```

The response is an array. For example, discover the Type attribute and its options:

```bash theme={null}
jq '.[] | select(.name == "Type") | {id, options}' attributes.json
```

Use the returned attribute ID and the option ID labeled Video in a Type filter. Discover custom fields in the same way; do not reuse another project's custom attribute IDs.

With MCP, call `assets_search_capabilities` with `project_id` to obtain channel descriptions, filter attributes, valid clauses, and selectable values. Its `select_options` field supplies option IDs. The current capabilities response omits **Path**; use the REST attribute list to discover Path for folder filters.

### Apply mandatory conditions

This `spec` selects videos with a duration from 30 through 120 seconds. Replace every placeholder with a discovered UUID:

```json theme={null}
{
  "signals": [],
  "filters": [
    {"id": "type-attribute-uuid", "clause": "eq", "values": ["video-option-uuid"]},
    {"id": "duration-attribute-uuid", "clause": "gte", "values": [30]},
    {"id": "duration-attribute-uuid", "clause": "lte", "values": [120]}
  ]
}
```

An empty `signals` array makes this a **filter-only search**. At least one signal or filter is required. Add these filters to a visual or transcript search to keep only ranked candidates that satisfy them.

Separate filter entries combine with **AND**. Multiple values inside a positive `eq`, `contains`, or `includes` filter match **any** listed value. For example, one Name `contains` filter with `["interview", "b-roll"]` matches either string. Two separate Name filters require both. For ranges, use separate lower and upper bounds as above.

| Attribute kind | Useful clauses |
| - | - |
| Name or other text | `eq`, `neq`, `contains`, `not_contains`, `starts_with`, `ends_with` |
| Duration, size, or another number | `eq`, `neq`, `gt`, `gte`, `lt`, `lte` |
| Multi-select, uploaded-by user, or People | `includes`, `not_includes` |
| Boolean | `is_true`, `is_false` |

Use the clauses valid for the selected attribute. Text comparisons are case-insensitive; `%` and `_` are literal characters. `includes` matches any selected value, while `not_includes` excludes the listed values. Boolean clauses and supported `exists`/`not_exists` checks need no comparison values.

### Search within a folder

Discover the **Path** attribute's ID from `attributes.json` and the folder's ID by listing its parent with `aspect ls --json`. Add this filter to `spec.filters`:

```json theme={null}
{"id": "path-attribute-uuid", "clause": "includes", "values": ["folder-uuid"]}
```

`includes` covers the folder and all nested folders. Use `eq` to restrict results to immediate children. A list of folder UUIDs matches any listed location. The request still needs the containing project's `project_id`.

### Find a known person

Use the **People** attribute and a known FACE entity UUID. MCP capabilities can provide available person options; `entities_list` can also discover entity IDs. Putting someone's name in a visual query is not equivalent to filtering for their recognized identity.

```json theme={null}
{"id": "people-attribute-uuid", "clause": "includes", "values": ["person-entity-uuid"]}
```

One People filter listing two people matches either person. To require both, add two filters, each containing one person's UUID. Both people may appear at different times. Recognition depends on processed face tracks and the identities available to the project.

## Read results and narrow the search

REST results are in `assets`; each asset's `search` contains an ordering `score`, a `confidence` label, signal summaries, and `moments`. A score is not a probability. Filter-only results use score `1` and confidence `unknown`.

Moments can include `start_time` and `end_time` in seconds, a 1-indexed document `page`, and an `evidence_text` excerpt marked `lexical` or `semantic`. Filename matches are asset-level and need not have timestamps. Excerpts are bounded evidence, not the full transcript.

MCP `assets_search` accepts the same `project_id` and `spec`, plus optional `asset_id`. Its compact results expose `matches` on each asset. In REST, search one asset with `POST /search/asset/<asset-uuid>/run`, sending `project_id` and `spec`; the response is search metadata directly.

Search has no public pagination parameters or total-match count. Signal searches use a bounded candidate pool, and metadata filters can reduce it further. An empty result is not proof that the project contains no relevant file. Narrow the query, try a different channel, or use filter-only search for metadata criteria. MCP's `returned` and `available` describe the returned set, not an independent count of every possible match.

Use the result's folder path with `aspect download` to retrieve the media. You can replace the final filename with its asset UUID to avoid name ambiguity; keep the containing folders in the URL. Or [download a proxy](/docs/cli/proxies) for a smaller viewing copy.
