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

# Retrieve

> Rank a space's memories against a natural-language query, with type filters, tag filters, and latency modes.

Retrieve understands meaning rather than keywords. "Where does Alice work?", "Alice's
employer", and "What company is Alice at?" all surface the same memory. Your query is
embedded and compared against stored memory embeddings, then results are ranked by
`relevance_score`.

```http theme={null}
POST /v1/retrieve
Authorization: Bearer anona_live_YOUR_KEY
Content-Type: application/json
```

```json theme={null}
{
  "space_id": "customer-support-bot",
  "query": "Where does Alice work?",
  "top_k": 10,
  "memory_type": ["fact"],
  "tags": ["work"],
  "tags_match": "any",
  "min_score": 0.3,
  "mode": "accurate"
}
```

## Parameters

| Parameter             | Type      | Required | Description                                                                                                                                                             |
| --------------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `space_id`            | string    | Yes      | Which space to search.                                                                                                                                                  |
| `query`               | string    | Yes      | Natural-language query.                                                                                                                                                 |
| `limit`               | integer   | No       | Maximum results, 1 to 100. Defaults to 10.                                                                                                                              |
| `top_k`               | integer   | No       | Alias for `limit`, 1 to 100. Takes precedence when both are sent.                                                                                                       |
| `memory_type`         | string\[] | No       | Filter by type: `fact`, `note`, `experience`, or `summary`.                                                                                                             |
| `user_id`             | string    | No       | Only memories written under this end user. See [Scoping](#scoping-within-a-space).                                                                                      |
| `agent_id`            | string    | No       | Only memories written under this agent.                                                                                                                                 |
| `session_id`          | string    | No       | Only memories written under this conversation or run.                                                                                                                   |
| `tags`                | string\[] | No       | Return only memories carrying these tags.                                                                                                                               |
| `tags_match`          | string    | No       | How `tags` combine: `any` (default), `all`, `any_strict`, `all_strict`, or `exact`.                                                                                     |
| `min_score`           | number    | No       | Drop results whose `relevance_score` falls below this floor. Trades breadth for precision without shrinking `limit`.                                                    |
| `prefer_observations` | boolean   | No       | When `true` (default), returns a distilled `note` in place of the raw facts behind it. Set `false` to get both. See [Memory types](#memory-types).                      |
| `as_of`               | string    | No       | ISO 8601 instant. Point-in-time recall: drops every memory **recorded** after it, so you get what the space knew then. See [Searching over time](#searching-over-time). |
| `query_timestamp`     | string    | No       | ISO 8601 instant that recency scoring and relative dates in the query ("last June") are measured against, instead of now. Re-ranks only, and never removes a result.    |
| `mode`                | string    | No       | `accurate` (default) or `fast`. See [Latency modes](#latency-modes).                                                                                                    |
| `format`              | string    | No       | `results` (default) returns only the array. `block` also returns one prompt-ready string. See [Prompt-ready context](#prompt-ready-context).                            |
| `context_max_tokens`  | integer   | No       | Approximate token ceiling for that block.                                                                                                                               |

## Response

```json theme={null}
{
  "results": [
    {
      "memory_id": "mem_abc123def456",
      "content": "Alice works at Google and specializes in distributed systems",
      "relevance_score": 0.95,
      "memory_type": "fact",
      "entities": ["Alice", "Google"],
      "occurred_start": "2024-01-15T10:30:00Z",
      "occurred_end": "2024-01-15T10:30:00Z",
      "metadata": {"user_id": "alice_123"},
      "created_at": "2026-07-14T12:34:56Z"
    }
  ],
  "usage": {
    "input_tokens": 8
  }
}
```

| Field             | Type           | Description                                                                      |
| ----------------- | -------------- | -------------------------------------------------------------------------------- |
| `memory_id`       | string         | Stable id for the memory.                                                        |
| `content`         | string         | The memory text.                                                                 |
| `relevance_score` | number or null | Higher means more relevant. `null` for results returned outside a ranked recall. |
| `memory_type`     | string         | `fact`, `note`, `experience`, or `summary`.                                      |
| `entities`        | string\[]      | The people, places, and things this memory is about.                             |
| `occurred_start`  | string or null | ISO timestamp for when the event began, distinct from `created_at`.              |
| `occurred_end`    | string or null | ISO timestamp for when the event ended.                                          |
| `metadata`        | object         | Whatever you attached at write time.                                             |
| `created_at`      | string         | When the memory was recorded.                                                    |

```python theme={null}
results = client.retrieve(
    space_id="customer-support-bot",
    query="Where does Alice work?",
    limit=10,
)

for r in results:
    print(r["content"], r["relevance_score"])
```

`client.retrieve()` returns the `results` list directly, and an empty list when nothing
matches.

## Memory types

Every result carries a `memory_type`. The same content can exist at more than one level,
as raw evidence and as a distilled version of it, so filtering by type asks for exactly
the layer you want.

| Type         | What it is                                                                                                                                |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `fact`       | Raw evidence: a single piece of information as it was recorded. Append-only.                                                              |
| `note`       | A consolidated synthesis distilled from related facts, deduped and stated at a higher level. One note can stand in for several raw facts. |
| `experience` | An episodic memory: something that happened, from the recorder's point of view.                                                           |
| `summary`    | A broad pattern learned across many memories, rather than a single event.                                                                 |

By default (`prefer_observations: true`) a `note` is returned in place of the facts it
was built from, so you see one distilled result instead of the same content several
times. Set `prefer_observations: false` to receive the raw facts alongside their note.

## Entities and time

Each result carries structured signal beyond the raw text.

| Signal         | Detail                                                                                                                                                                                                                                          |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Entities**   | Retrieval extracts the people, places, and things a memory is about and returns them in `entities`. Because memories are linked through shared entities, a query about one entity can surface related memories about the others it connects to. |
| **Event time** | `occurred_start` and `occurred_end` capture when the event happened, separately from `created_at`, which is when you recorded it. Ranking is recency-aware, so more recent and more time-relevant memories score higher.                        |

The same entities are exposed as a browsable graph. See [Graph](/api-reference/graph).

## Searching over time

Every memory carries **two** independent times, and knowing which is which is
most of the work:

|                 | Set by                                           | Returned as                       | Meaning                    |
| --------------- | ------------------------------------------------ | --------------------------------- | -------------------------- |
| **Event time**  | `timestamp` on [record](/api-reference/memories) | `occurred_start` / `occurred_end` | When the thing happened.   |
| **Record time** | the system, at write                             | `created_at`                      | When you told us about it. |

They differ whenever you import history: a memory about last June that you
upload today has an event time of last June and a record time of today.

`as_of` and `query_timestamp` are not variants of each other: one filters, the
other ranks, and both work on **record** time.

<CodeGroup>
  ```python as_of: point-in-time recall theme={null}
  # What did the space know on June 1st? Anything recorded
  # after that instant is dropped, however relevant.
  results = client.retrieve(
      space_id="support",
      query="what plan is this customer on?",
      as_of="2026-06-01T00:00:00Z",
  )
  ```

  ```python query_timestamp: move "now" theme={null}
  # Rank as though it were June 1st, and read "last quarter"
  # in the query relative to that date. Nothing is removed.
  results = client.retrieve(
      space_id="support",
      query="what changed last quarter?",
      query_timestamp="2026-06-01T00:00:00Z",
  )
  ```
</CodeGroup>

Use `as_of` to reproduce a past answer or audit a decision: "why did the agent
say that on June 1st". Use `query_timestamp` when the query itself contains a
relative date, or when you want older-but-more-time-relevant memories to rank
higher without excluding anything.

Both take an ISO 8601 instant. A malformed one is rejected as `422` before the
search runs, so a typo costs you nothing.

<Warning>
  `as_of` filters on when a memory was **recorded**, not on when the event
  happened. A bulk import is recorded today no matter what `timestamp` each item
  carries, so `as_of` a year ago returns nothing from it. There is currently no
  filter on event time. `timestamp` and `occurred_start` affect ranking and are
  returned on results, but cannot be used as a range filter.
</Warning>

<Note>
  Under `as_of`, retrieval skips its graph-expansion pass and answers from
  semantic, keyword and temporal retrieval only. Expansion follows entity links
  without a time bound, so it could pull in a memory recorded after the cutoff:
  a wrong answer for point-in-time recall rather than a ranking artifact. The
  practical effect is that an `as_of` search is slightly narrower than the same
  search without it.
</Note>

## Prompt-ready context

Most callers take the results array and join it into a system prompt. Ask for
`format: "block"` and that is done for you, including the token budget, which
a hand-written loop usually skips.

```python theme={null}
context = client.get_context(space_id="support", query=user_message)

messages = [
    {"role": "system", "content": context},
    {"role": "user", "content": user_message},
]
```

Over the raw API:

```json theme={null}
{
  "space_id": "support",
  "query": "what plan are they on?",
  "format": "block",
  "context_max_tokens": 500
}
```

```json theme={null}
{
  "context": "1. Customer is on the Scale plan\n2. Their team has four seats",
  "token_estimate": 18,
  "results": [ "…unchanged…" ],
  "usage": { "input_tokens": 0, "output_tokens": 0 }
}
```

* `results` stays populated, so wanting both costs one round trip, not two.
* The block is numbered plain text with no header, so you compose your own
  prompt around it.
* `context_max_tokens` drops **whole memories**, lowest-ranked first. Text is
  never cut mid-sentence: half a fact still reads as a fact.
* `token_estimate` is an estimate, derived from a characters-per-token
  approximation rather than a tokenizer. Treat it as a guide when budgeting a
  context window, not an exact count.

Omitting `format` behaves exactly as before.

## Scoping within a space

A space is the coarse boundary. `user_id`, `agent_id` and `session_id` partition
it, so one space can serve many end users without their memories mixing, and you do
not need a space per user.

```python theme={null}
# Write under a user
client.record(
    space_id="support",
    content="Prefers email over phone.",
    user_id="alice",
)

# Only Alice's memories come back
results = client.retrieve(
    space_id="support",
    query="how should we contact them?",
    user_id="alice",
)
```

Scoping is **strict**, and that is the point:

* A scoped search returns only memories written under the *same* scope. Bob's
  memories can never appear in Alice's results.
* Memories stored **without** a scope are not returned to a scoped search
  either. If you turn scoping on for a space that already has history, that
  history stays visible to unscoped searches but not to scoped ones, so backfill
  the scope on those memories if you need them.
* Passing several keys ANDs them: `user_id` + `session_id` returns only that
  user's memories from that session.
* Consolidated memories are built **per user**, so a synthesis never mixes two
  users' facts. Sessions roll up into the user, which is what lets the memory
  improve across conversations.

Tags beginning with `anona:` are reserved for this and rejected with
`422 reserved_tag`, since otherwise a hand-written tag could impersonate another
user's scope. Use the scope fields instead.

<Note>
  Scoping costs nothing when unused. A request without these fields behaves
  exactly as it did before they existed.
</Note>

## Latency modes

`mode` trades relevance quality against speed.

| Mode       | Behavior                                                            | When to use                                                                                                                           |
| ---------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `accurate` | Neurally reranks candidates for best relevance. The default.        | Most calls, where result quality matters most.                                                                                        |
| `fast`     | Skips the neural rerank pass and ranks by retrieval fusion instead. | Latency-sensitive paths such as agent loops and autocomplete-style lookups, where slightly lower precision is an acceptable tradeoff. |

```python theme={null}
results = client.retrieve(
    space_id="customer-support-bot",
    query="Where does Alice work?",
    mode="fast",
)
```

## Error responses

| Status | Code           | Cause                                        |
| ------ | -------------- | -------------------------------------------- |
| 400    | `bad_request`  | Missing `query` or `space_id`.               |
| 401    | `unauthorized` | Missing or invalid API key.                  |
| 403    | `forbidden`    | The space is not owned by your organization. |
| 404    | `not_found`    | The space does not exist.                    |
| 429    | `rate_limited` | Credit quota or rate limit exceeded.         |

See the full [error reference](/api-reference/errors).

## Next steps

<CardGroup cols={2}>
  <Card title="Reason API" icon="lightbulb" href="/api-reference/reason">
    Get a synthesized answer instead of a ranked list.
  </Card>

  <Card title="Graph API" icon="chart-network" href="/api-reference/graph">
    Inspect the entities behind your results.
  </Card>
</CardGroup>
