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

# Filtering and searching records

Use exact filters, semantic search, or both to find Records and provide context to Flows and Agents.

## Filter records

A record filter selects Records of one type by their field values.
A filter field names either a top-level Record field or a metadata key.
The top-level fields are `id`, `name`, `ownerId`, `createdAt`, `updatedAt`, and `schemaValid`.
Every other field name refers to a metadata key.

A field name is a single top-level key, and a filter does not read values inside nested objects.
For example, a filter cannot select the `city` value inside an `address` object.
To filter on a nested value, store it as its own top-level metadata key.
Field names use letters, digits, underscores, periods, and hyphens, up to 64 characters.
A field name can't start with a period or a hyphen.

When you save a Record in the dashboard or with the Records API, Runtype stores its metadata keys in lowercase, with other characters replaced by underscores.
For example, `orderCount` is stored as `ordercount`, and `Order Count` is stored as `order_count`.
Use the stored key name in your filters.

### Filter records in the dashboard

To filter the Records list, follow these steps:

1. Go to **Records**.
2. In the list view picker, select a Record type instead of **All records**.
   The filter builder appears only for a single Record type.
3. Click **Add filter**.
4. Choose a field, an operator, and a value.
   The list updates to show the matching Records.

The search box on the Records page matches Record names and types, not metadata values.
**Export** and bulk edits use the active filter.
For more information, see [Search, filter, and export Records](/user-guide/records/creating-and-managing-records#search-filter-and-export-records).

### Write a record filter

In a Flow, set the `recordFilter` field of a **List Records** or **Get Record** step to a filter object.
**List Records** returns the matching Records.
**Get Record** returns the most recently updated matching Record.
The filter requires a `type` field and can include a `where` clause with a condition or a group of conditions:

```json
{
  "type": "customers",
  "where": {
    "op": "and",
    "conditions": [
      { "field": "tier", "op": "eq", "value": "premium" },
      { "field": "order_count", "op": "gt", "value": 10 }
    ]
  }
}
```

Use `"op": "and"` or `"op": "or"` in a group to combine conditions.
A group can contain one level of nested groups, and each group holds up to 50 conditions.

The `type` value and every string value in a condition accept `{{variable}}` templates.
Runtype resolves them when the step runs, so a filter can match a value from the current run.
Array items for `in` and `notIn` also accept templates.

**List Records** returns the most recently updated Records first.
It returns up to 50 Records by default.
Set `limit` to return up to 1000 Records.

The same filter object selects Records for bulk edits, for Schedule record targets, and for the Records API.
For bulk edits, see [Bulk edit Records](/user-guide/records/creating-and-managing-records#bulk-edit-records).

### Filter operators

The following table lists the operators that a condition accepts and the value each one requires:

| Operator                             | Value                                             | Example                                                           |
| ------------------------------------ | ------------------------------------------------- | ----------------------------------------------------------------- |
| `eq`, `neq`                          | A string, number, or Boolean. `null` is rejected. | `{ "field": "tier", "op": "eq", "value": "premium" }`             |
| `gt`, `gte`, `lt`, `lte`             | A number                                          | `{ "field": "order_count", "op": "gte", "value": 5 }`             |
| `between`                            | An array of two numbers, `[min, max]`             | `{ "field": "price", "op": "between", "value": [10, 50] }`        |
| `contains`, `startsWith`, `endsWith` | A non-empty string                                | `{ "field": "email", "op": "endsWith", "value": "@example.com" }` |
| `in`, `notIn`                        | A non-empty array of up to 200 values             | `{ "field": "region", "op": "in", "value": ["us", "eu"] }`        |
| `isSet`, `isNotSet`                  | No value                                          | `{ "field": "processed_at", "op": "isNotSet" }`                   |
| `isTrue`, `isFalse`                  | No value                                          | `{ "field": "active", "op": "isTrue" }`                           |
| `withinLastDays`, `olderThanDays`    | A number of days                                  | `{ "field": "updatedAt", "op": "withinLastDays", "value": 7 }`    |

`contains`, `startsWith`, and `endsWith` ignore letter case.
`eq` is case-sensitive.

Comparison and range operators match JSON numbers and numeric strings such as `42` or `3.14`.
They do not match the top-level fields.
The date-relative operators match ISO 8601 date and date-time strings, such as `2026-01-15T00:00:00.000Z`.
They also match the top-level `createdAt` and `updatedAt` fields.
The filter does not match values in other formats.

### Null and missing values

Value operators such as `eq`, `gt`, and `contains` do not match Records where the field is missing or set to JSON `null`.
The `neq` and `notIn` operators also match Records where the field is missing or `null`.
For example, a `neq` filter on a field that most Records do not have also returns those Records.

To select Records where a Boolean flag is not `true`, including Records that do not have the field, use `{ "field": "processed", "op": "neq", "value": true }`.
To filter only by field existence, use `isSet` or `isNotSet`.

### Filter records with the API

`GET /v1/records` and `GET /v1/records/export` accept a record filter in the `recordFilter` query parameter.
The value must be the filter JSON, base64-encoded.
The following request lists premium customers:

```bash
FILTER='{"type":"customers","where":{"field":"tier","op":"eq","value":"premium"}}'

curl --get https://api.runtype.com/v1/records \
  --header "Authorization: Bearer YOUR_API_KEY" \
  --data-urlencode "recordFilter=$(printf '%s' "$FILTER" | base64 | tr -d '\n')"
```

Replace `YOUR_API_KEY` with your Runtype API key.

To check a filter before you use it, send the filter JSON as the body of `POST /v1/records/preview-filter`.
The response includes the Record type, `matchedCount`, and up to 25 sample Records.

The following errors return HTTP status `400`:

* `Invalid recordFilter: expected base64-encoded JSON`: the parameter value does not decode to JSON.
* `Conflicting type: ?type=X but recordFilter.type=Y`: the `type` query parameter and the filter's `type` differ. Send one of them, or make them match.

## Semantic search

Semantic search finds Records by meaning instead of exact keywords.
Runtype converts your query into a vector embedding and returns the Records with the most similar embeddings.

Semantic search returns only Records that have an embedding.
To add embeddings, use a **Generate Embedding** step and a **Store Vector** step in an ingest Flow.
Use the same embedding model to index Records and to search them.
With **PostgreSQL (pgvector)**, the **Vector Search** step returns no results for Records that were embedded with a different model.

Queries such as these can match semantically related Records:

* `"How do I reset my password?"` can match Records about password resets.
* `"Affordable laptops for students"` can match Records about budget laptops.
* `"Shipping delays"` can match shipping policies and updates.

Use semantic search for text-heavy fields such as descriptions, content, and article bodies.
Use record filters for structured values such as IDs and numbers.

### Search in a Flow

To add semantic search to a Flow, follow these steps:

1. Add a **Vector Search** step.
2. In the **Search for** field, enter the query text or a template such as `{{userQuestion}}`.
3. In the **in** list, select the vector store.
4. In the **using** field, select the embedding model that you used to index the Records.

The step card also sets the number of results and the minimum similarity score.
A new step returns 5 results with a similarity of 0.7 or higher.

In a JSON step config, `limit` sets the number of results and defaults to 5.
`threshold` sets the minimum similarity score from 0 to 1.
If you omit `threshold` in JSON, the step applies no minimum.
If you omit `embeddingModel`, the step uses `text-embedding-3-small`.

### Search results

The step writes an array to its output variable, with the most similar Records first.
For **PostgreSQL (pgvector)** and **Cloudflare Vectorize**, each item has these fields:

* `id`: the Record or vector ID.
* `score` and `similarity`: the same similarity score.
* `name` and `type`: the Record name and type, for **PostgreSQL (pgvector)** results.
* `metadata`: the Record metadata or the vector metadata.

The following sample shows one result:

```json
[
  {
    "id": "record_01h2xcejqtf2nbrexx3vqjhp41",
    "score": 0.82,
    "similarity": 0.82,
    "name": "Reset your password",
    "type": "docs",
    "metadata": { "content": "To reset your password, open Settings..." }
  }
]
```

To read a field from the top result, use a template such as `{{searchResults[0].metadata.content}}`.

## Combine filters and semantic search

The **Vector Search** step can narrow a semantic search with `recordType` and `metadataFilters`.
Support depends on the vector store, as the following table shows:

| Vector store              | `recordType`                                                    | `metadataFilters`                                                       |
| ------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------- |
| **PostgreSQL (pgvector)** | Returns only Records of that type. Omit it to search all types. | Ignored                                                                 |
| **Cloudflare Vectorize**  | Matches vectors whose `type` metadata equals the value          | Matches each key exactly. Values must be strings, numbers, or Booleans. |
| **Weaviate**              | Ignored                                                         | Applies only the first key, as an exact match                           |

Weaviate results have a different shape.
Each item has `id`, `score`, `rank`, and `properties` fields instead of `similarity` and `metadata`.

In the dashboard, the **filter by type** field sets `recordType` for **PostgreSQL (pgvector)**.
To search for support articles about one product, set **Search for** to `Technical issues with Widget Pro` and **filter by type** to `support`.

## Search examples

### Customer lookup

The following **List Records** filter finds a customer by the email address in the current run:

```json
{
  "type": "customers",
  "where": {
    "op": "and",
    "conditions": [{ "field": "email", "op": "eq", "value": "{{customerEmail}}" }]
  }
}
```

The `customerEmail` flow variable supplies the email address, such as `dana@example.com`.

### High-value customers

The following filter finds premium customers with a high lifetime value:

```json
{
  "type": "customers",
  "where": {
    "op": "and",
    "conditions": [
      { "field": "lifetime_value", "op": "gt", "value": 10000 },
      { "field": "tier", "op": "eq", "value": "premium" }
    ]
  }
}
```

### Knowledge base search

The following **Vector Search** config searches `docs` Records:

```json
{
  "query": "{{userQuestion}}",
  "recordType": "docs",
  "limit": 3,
  "outputVariable": "searchResults"
}
```

The `userQuestion` flow variable supplies the query text.

### Product recommendations

The following **Vector Search** config returns products similar to a customer's stated preferences:

```json
{
  "query": "{{userPreferences}}",
  "recordType": "products",
  "limit": 5,
  "threshold": 0.7,
  "outputVariable": "recommendations"
}
```

The `userPreferences` flow variable supplies the query text.

## Next steps

Continue with these guides:

* [Building retrieval that works](/user-guide/records/building-retrieval-that-works): chunk, embed, and evaluate retrieval.
* [Defining a schema](/user-guide/records/defining-a-schema): set field types for a Record type.
* [Using record steps (get/list/upsert)](/user-guide/flows/using-record-steps-upsert-retrieve): configure **List Records**, **Get Record**, and empty results.
* [Using records in flows](/user-guide/records/using-records-in-flows): add record lookups to Flows.