# Find notes by meaning

> Turn on semantic search with a local or hosted embedding model, and search your notes by what they say.

Semantic search finds notes by meaning instead of exact words. The engine splits each note into passages, sends them to an embedding model you choose and stores the resulting vectors in the vault. Agents and programs then search with the [`semantic_search`](https://datanotes.dev/reference/operations/queries/#semantic_search) operation. It’s off by default, because note text goes to the model’s provider.

## Turn on semantic search

Turn it on in the engine’s settings file, `settings.json` in its store (`datanotes store path` shows where):

1. Stop the engine.
2. Pick a provider and a model from the [embedding providers](#choose-an-embedding-provider) table.
3. Set `semanticEnabled`, `semanticProvider` and `semanticModel` in the settings file.
4. If the provider needs an API key, [pass the key to the engine](#pass-the-api-key-to-the-engine).
5. Start the engine.

The settings below use Ollama, which keeps your notes on your computer:

```json
{
  "semanticEnabled": true,
  "semanticProvider": "ollama",
  "semanticModel": "nomic-embed-text"
}
```

Leave `semanticBaseUrl` empty to use the provider’s default address. The [settings reference](https://datanotes.dev/reference/settings/) lists every key and its default.

## Choose an embedding provider

The provider decides where your note text goes. Local providers keep it on your computer; hosted ones send it over the network:

| `semanticProvider` | Where notes go                                         | Default address                | Example model                                                |
| ------------------ | ------------------------------------------------------ | ------------------------------ | ------------------------------------------------------------ |
| `ollama`           | Stays on this computer                                 | `http://127.0.0.1:11434`       | `nomic-embed-text`, `qwen3-embedding:0.6b`                   |
| `lmstudio`         | Stays on this computer                                 | `http://127.0.0.1:1234/v1`     | `text-embedding-nomic-embed-text-v1.5`                       |
| `openrouter`       | OpenRouter and the model’s provider (needs an API key) | `https://openrouter.ai/api/v1` | `openai/text-embedding-3-small`, `qwen/qwen3-embedding-0.6b` |
| `openai`           | OpenAI (needs an API key)                              | `https://api.openai.com/v1`    | `text-embedding-3-small`                                     |
| `custom`           | Any OpenAI-compatible `/embeddings` API                | None: set `semanticBaseUrl`    | The model your API serves                                    |

## Pass the API key to the engine

The engine never stores API keys in the settings file. Set the `DATANOTES_EMBEDDING_KEY` environment variable before you start the engine, or pass a file that holds the key:

```bash
npx -y datanotes serve --database your_database_folder \
  --embedding-key-file your_key_file
```

## Choose which notes to index

By default the engine indexes every note in the vault. Two settings narrow the scope:

* **`semanticScope`**: `"vault"` (the default) indexes every note; `"tables"` indexes only table rows
* **`semanticExclude`**: folders to leave out

The engine always skips `_archive` folders.

## How the engine builds the index

The engine splits each note into passages of at most about 1,500 characters. The splitting follows these rules:

* **First passage**: holds the title and the front matter, up to 800 characters, without the keys `id` and `table`
* **Heading sections**: each section starts a new passage
* **Long sections**: the engine splits them at paragraph boundaries
* **Limit**: 40 passages per note

The engine sends the passages to the model and stores the vectors as int8 values in `semantic/`, in its store. It updates the index 10s after edits stop, and 5s after it starts. It embeds again only the notes whose content changed. If you change the provider, the address or the model, the engine starts a new index.

## Search from agents and programs

Agents and programs use three operations. All three work through the [HTTP API](https://datanotes.dev/develop/http-api/), the [JavaScript client](https://datanotes.dev/develop/client/) and the [Model Context Protocol (MCP) server](https://datanotes.dev/start/connect-agents/):

* **[`semantic_search`](https://datanotes.dev/reference/operations/queries/#semantic_search)**: takes a `query`, with optional `tables`, `folder`, `min_score` and `limit`; returns one result per note, its best passage, best first
* **[`describe_semantic_index`](https://datanotes.dev/reference/operations/auxiliary/#describe_semantic_index)**: shows the index status
* **[`sync_semantic_index`](https://datanotes.dev/reference/operations/auxiliary/#sync_semantic_index)**: brings the index up to date, or rebuilds it with `rebuild: true`

This example asks for the five notes closest in meaning to a question. `db` is a [client](https://datanotes.dev/develop/client/) connected to the engine:

```ts
const r = await db.call("semantic_search", {
  query: "films that scare without a single spoken word",
  limit: 5,
});
```

## Search these docs by meaning

With semantic search on, the engine also embeds the sections of these docs, in an index of their own. `search_docs` then merges its ranking by words with the engine’s ranking by meaning. [Search by words and by meaning](https://datanotes.dev/guides/agents/docs-offline/#search-by-words-and-by-meaning) explains how.

## Search notes by meaning

`semantic_search` finds notes by meaning rather than by words. It’s off by default, because it sends note text to an embedding provider. Until you turn it on in the engine’s settings, the call answers that it’s off; [Turn on semantic search](https://datanotes.dev/guides/search/semantic/#turn-on-semantic-search) explains how. `describe_semantic_index` reports the state of the index, and `sync_semantic_index` brings it up to date once it’s on.

**`semantic_search`**

```json
{ "query": "the Friday we watched the Cary Grant film" }
```

Refused:

```text
semantic search is off: set semanticEnabled, semanticProvider and semanticModel in the engine settings
Hint: Check the arguments of semantic_search: query (string, required), limit (integer), tables (list of strings), folder (string), min_score (number).
Docs: https://datanotes.dev/reference/operations/queries.md#semantic_search (offline: read_docs("reference/operations/queries#semantic_search"))
```

**`describe_semantic_index`** (no arguments)

Answer:

```json
{
  "enabled": false,
  "provider": "ollama",
  "model": "",
  "files": 0,
  "passages": 0,
  "pending": 6,
  "running": false
}
```

**`sync_semantic_index`** (no arguments)

Refused:

```text
semantic search is off: set semanticEnabled, semanticProvider and semanticModel in the engine settings
Hint: Check the arguments of sync_semantic_index: rebuild (boolean).
Docs: https://datanotes.dev/reference/operations/auxiliary.md#sync_semantic_index (offline: read_docs("reference/operations/auxiliary#sync_semantic_index"))
```
