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

# Improve Retrieval Results with Metadata Filtering

> Learn how to refine searches using metadata filtering for precise document retrieval. Perfect for targeted searches.

Some use-cases of Vellum Search require you to narrow in on a subset of documents prior to searching based on keyword match / semantic similarity. For example, you might want to search across historical conversations *for a specific user* or only across documents *that have specific tags*.

You can do this through metadata filtering.

Metadata filtering requires that you:

1. Provide structured metadata for your documents either upon initial upload or later; and
2. Provide filter criteria when performing a search.

Let’s see how to do each.

# Specifying Metadata

You can specify metadata for documents through both the UI and API.

### Through the UI

You can provide metadata upon initial upload.

![Metadata Specification](https://storage.googleapis.com/vellum-public/help-docs/document_metadata_specification.png)

You can also view metadata associated with a document and edit it after it’s been uploaded.

![Viewing Metadata](https://storage.googleapis.com/vellum-public/help-docs/edit_document_metadata.png)

### Through the API

You can provide metadata as *stringified* JSON upon initial upload using the [upload Documents API here](https://docs.vellum.ai/api-reference/api-reference/documents/upload).

You can also update a document’s metadata after-the-fact using the the `Document - Partial Update` [endpoint here](https://docs.vellum.ai/api-reference/api-reference/documents/partial-update).

Note that in this endpoint, you can simply provide a JSON object (rather than a stringified JSON object as is required during initial upload).

## Filtering Against Metadata

You use the `search` endpoint to perform a search against an index (documented [here](https://docs.vellum.ai/api-reference/api-reference/search)). This endpoint exposes an `options.filters.metadata` field for filtering against your provided metadata prior to matching on keywords/semantic similarity.

The syntax of the `metadata` property supports complex boolean logic and was borrowed from [React Query Builder](https://react-querybuilder.js.org/). You can use their [demo here](https://react-querybuilder.js.org/demo#addRuleToNewGroups=false\&autoSelectField=true\&autoSelectOperator=true\&debugMode=false\&disabled=false\&enableDragAndDrop=false\&independentCombinators=false\&justifiedLayout=false\&listsAsArrays=false\&parseNumbers=false\&resetOnFieldChange=true\&resetOnOperatorChange=false\&showBranches=false\&showCloneButtons=false\&showCombinatorsBetweenRules=false\&showLockButtons=false\&showNotToggle=false\&validateQuery=false) to get a feel for the query syntax.

Note that values for fields must be JSON-deserializable. If you're looking to filter against a string, then the value passed in should contain escaped double quotes.

### Example

Suppose you have two documents with the following metadata:

```json
// Document A
{
	"tags": [
		"customer-facing", "needs-triage", "bug"
	],
	"priority": "high"
}
// Document B
{
	"tags": [
		"needs-triage", "bug"
	],
	"priority": "low"
}
```

And you wanted to perform a search across all documents that are marked as high priority, customer-facing bugs, you would use the following query:

```json
{
		...,
		"options": {
			"filters": {
				"metadata": {
					"combinator": "AND",
					"rules": [
						{
							"field": "tags",
							"operator": "contains",
							"value": "\"customer-facing\""
						},
						{
							"field": "tags",
							"operator": "contains",
							"value": "\"bug\""
						},
						{
							"priority": "tags",
							"operator": "+",
							"value": "high"
						}
					],
					"negated": false
				}
			}
		}
}
```