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

# Query Vectors

Retrieve the nearest neighbors for a given query vector. Passing `text` alongside `query_vectors` turns this into a **hybrid** query that fuses BM25 full-text ranking with the vector ranking (see [Full-Text and Hybrid Search](../../guides/data-operations/bm25-search)). Adding `filters` on top composes with either form — including a **three-way** query (vector + BM25 + metadata); see [Combining search legs](#combining-search-legs).

## Authentication

Required - API key via `X-API-Key` header:

```http theme={null}
X-API-Key: cyborg_your_api_key_here
```

Any valid credential is accepted — the root key or a per-user `cdbk_` token. When RBAC is not enabled (`CYBORGDB_SERVICE_ROOT_KEY` unset), no header is required.

## Request Body

### Single Query Request

```json theme={null}
{
  "index_name": "my_index",
  "index_key": "64_character_hex_string_representing_32_bytes",
  "query_vectors": [0.1, 0.2, 0.3, 0.4],
  "top_k": 10,
  "n_probes": 1,
  "greedy": false,
  "filters": {"category": "greeting"},
  "include": ["distance", "metadata"]
}
```

### Batch Query Request

```json theme={null}
{
  "index_name": "my_index",
  "index_key": "64_character_hex_string_representing_32_bytes",
  "query_vectors": [
    [0.1, 0.2, 0.3, 0.4],
    [0.5, 0.6, 0.7, 0.8]
  ],
  "top_k": 10,
  "n_probes": 1,
  "greedy": false,
  "filters": {"category": "greeting"},
  "include": ["distance", "metadata"]
}
```

### Semantic Search Request (with embedding model)

```json theme={null}
{
  "index_name": "semantic_index",
  "index_key": "64_character_hex_string_representing_32_bytes",
  "query_contents": "Find documents about machine learning",
  "top_k": 10,
  "filters": {"type": "research"},
  "include": ["distance", "metadata"]
}
```

<Expandable title="parameters">
  <ParamField body="index_name" type="string" required="true">
    Name of the target index
  </ParamField>

  <ParamField body="index_key" type="string">
    32-byte encryption key as a hex string. Required for indexes created with the SDK-supplied KEK path; omit for KMS-backed indexes (the service resolves the key from the index's stored key envelope).
  </ParamField>

  <ParamField body="query_vectors" type="array[number] | array[array[number]]">
    Query vector(s) for similarity search:

    * Single query: `[0.1, 0.2, 0.3, ...]` (1D array)
    * Batch query: `[[0.1, 0.2, ...], [0.4, 0.5, ...]]` (2D array)
      Required unless `query_contents` is provided
  </ParamField>

  <ParamField body="query_contents" type="string">
    Text content for semantic search (requires index with `embedding_model`).
    Alternative to `query_vectors` for automatic embedding generation
  </ParamField>

  <ParamField body="top_k" type="integer" default={100}>
    Number of nearest neighbors to return
  </ParamField>

  <ParamField body="n_probes" type="integer" default="auto">
    Number of index lists to probe. When set to 0 or omitted, automatically determines optimal value based on index size and query requirements
  </ParamField>

  <ParamField body="greedy" type="boolean" default="false">
    Use greedy search for higher recall
  </ParamField>

  <ParamField body="rerank_mult" type="integer" default={50}>
    Multiplier for stage 1 retrieval in reranking indexes. Stage 1 returns `top_k * rerank_mult` candidates before the rerank pass narrows the result down to `top_k`. Higher values trade query latency for recall. Ignored by indexes that do not rerank. `top_k * rerank_mult` must not exceed 10,000; hybrid (vector + `text`) queries multiply by an additional fusion window, so their budget is tighter.
  </ParamField>

  <ParamField body="filters" type="object">
    MongoDB-style metadata filters
  </ParamField>

  <ParamField body="include" type="array[string]" default={[]}>
    Fields to include in response. `id` is always returned. `contents` and `vector` are **not** valid values for query — to retrieve stored vectors or contents, call `POST /v1/vectors/get` (or the SDK's `get`) with the matching IDs afterwards.

    <Expandable title="include options">
      <ParamField body="distance" type="string">
        Include distance/similarity score to query vector (pure vector query only)
      </ParamField>

      <ParamField body="metadata" type="string">
        Include item metadata key-value pairs
      </ParamField>
    </Expandable>
  </ParamField>

  <ParamField body="text" type="string">
    Query text for a BM25 full-text leg. Setting it makes the query **hybrid**: the BM25 ranking is fused with the vector ranking by weighted RRF and each result carries `score` instead of `distance`. Requires an index with at least one `full_text` field. A `query_vectors` value is still required even with `text` set — it fixes the batch shape.
  </ParamField>

  <ParamField body="text_fields" type="array[string]">
    Which `full_text` fields the text leg searches. Omitted means all designated fields. Naming a non-`full_text` field returns 400.
  </ParamField>

  <ParamField body="text_field_weights" type="array[number]">
    Per-field weights on the summed per-field BM25 scores, parallel to the searched fields. Omitted means 1.0 each.
  </ParamField>

  <ParamField body="require_all_terms" type="boolean" default="false">
    Require every query term to match (AND) instead of any (OR, the default).
  </ParamField>

  <ParamField body="alpha" type="number" default={0.5}>
    Leg blend in `[0, 1]`: `0` = pure BM25, `1` = pure vector. An exact endpoint skips the dead leg's retrieval.
  </ParamField>

  <ParamField body="rrf_k" type="number" default={60}>
    RRF rank-smoothing constant (must be `> 0`). Higher rewards agreement further down each leg's list.
  </ParamField>

  <ParamField body="window_mult" type="integer" default={3}>
    Per-leg candidate depth as a **multiple of `top_k`** (must be `>= 1`), so each leg looks deeper than the final cut.
  </ParamField>
</Expandable>

<Note>Hybrid queries accept fewer filters than pure vector queries. Filtering on a `full_text` or `filterable: false` field, or using `$regex`/`$contains` on a non-`pattern` field, works in a pure query but is rejected with 400 when `text` is set. To keep such a filter, run the query without `text`.</Note>

## Response

### Single Query Response

When `query_vectors` is a 1D array or `query_contents` is used:

```json theme={null}
{
  "results": [
    {
      "id": "item_1",
      "distance": 0.123,
      "metadata": {"category": "greeting", "language": "en"}
    },
    {
      "id": "item_3",
      "distance": 0.245,
      "metadata": {"category": "greeting", "language": "es"}
    }
  ]
}
```

### Batch Query Response

When `query_vectors` is a 2D array (array of arrays):

```json theme={null}
{
  "results": [
    [
      {"id": "item_1", "distance": 0.123, "metadata": {"category": "greeting"}},
      {"id": "item_2", "distance": 0.245, "metadata": {"category": "farewell"}}
    ],
    [
      {"id": "item_3", "distance": 0.156, "metadata": {"category": "question"}},
      {"id": "item_4", "distance": 0.298, "metadata": {"category": "answer"}}
    ]
  ]
}
```

<Note>
  The response format automatically matches the request format:

  * Single query → flat array of results
  * Batch query → nested array with results for each query
</Note>

### Hybrid Query Response

When `text` is set alongside `query_vectors`, each result carries a `score` (fused BM25 + vector relevance, higher = more relevant) instead of `distance` — a text-only match has no vector distance to report:

```json theme={null}
{
  "results": [
    {"id": "a1", "score": 0.0161, "metadata": {"title": "Encrypted vector search"}},
    {"id": "a7", "score": 0.0134, "metadata": {"title": "Query fusion"}}
  ]
}
```

## Metadata Filtering

Use MongoDB-style query operators:

```json theme={null}
{
  "filters": {
    "$and": [
      {"category": "greeting"},
      {"confidence": {"$gte": 0.9}}
    ]
  }
}
```

**Supported operators:** `$eq`, `$ne`, `$in`, `$nin`, `$exists`, `$gt`, `$gte`, `$lt`, `$lte`, `$regex`, `$contains`, combined with `$and`, `$or`, `$nor`, `$not`

Filtering works on any metadata field; the index's `metadata_schema` only decides *how* a filter is resolved — fields marked `filterable` (the default) are answered from the inverted index, and `$regex` / `$contains` are answered from the regex dictionary on `pattern` fields. Anything else falls back to a post-filter over the decrypted metadata, which is correct but slower. Dates have no native type: store and filter them as epoch milliseconds.

## Combining Search Legs

Vector similarity, BM25 full-text, and metadata filtering are independent legs that compose in one request:

| Legs | Request shape | Endpoint |
| - | - | - |
| Vector + metadata | `query_vectors` + `filters` | this endpoint |
| Vector + BM25 (hybrid) | `query_vectors` + `text` | this endpoint |
| Vector + BM25 + metadata (three-way) | `query_vectors` + `text` + `filters` | this endpoint |
| BM25 + metadata (no vector) | `text` + `filters` | [`/v1/vectors/query_metadata`](./query-metadata) |
| Metadata only | `filters` | [`/v1/vectors/query_metadata`](./query-metadata) |

On a three-way query the filter is resolved **once**, before retrieval, and the surviving IDs constrain both the vector and BM25 legs — so both rankings are computed over the same filtered candidate set, then fused by RRF. A filter that matches nothing short-circuits to an empty result without running either leg.

`alpha` still applies: `0` reduces the query to BM25 + metadata, `1` to vector + metadata, and either exact endpoint skips the dead leg's retrieval entirely.

<Note>Any query with `text` set accepts only filters the metadata index can resolve exactly (see the Note above) — this includes the three-way form.</Note>

## Exceptions

<AccordionGroup>
  <Accordion title="400 Bad Request">
    Invalid values (e.g. vector dimension mismatch with the index), or a hybrid-parameter violation — a filter disallowed on a hybrid (`text`) query (see the Note above), `text` on an index with no `full_text` field, a `text_fields` entry that is not a `full_text` field, a text knob (`text_fields` / `text_field_weights` / `require_all_terms`) without `text`, `alpha` outside `[0, 1]`, `rrf_k <= 0`, or `window_mult < 1`
  </Accordion>

  <Accordion title="401 Unauthorized">
    Authentication failed (invalid API key) **or** wrong `index_key` on SDK-supplied indexes — see [error model](../introduction#error-model-api-keys-index-keys-and-kms)
  </Accordion>

  <Accordion title="404 Not Found">
    Index not found
  </Accordion>

  <Accordion title="422 Unprocessable Entity">
    Request body failed validation (missing/mistyped fields)
  </Accordion>

  <Accordion title="500 Internal Server Error">
    Internal server error
  </Accordion>

  <Accordion title="503 Service Unavailable">
    Embedding model unavailable (semantic search via `query_contents`)
  </Accordion>
</AccordionGroup>

## Example Usage

**Basic Query:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/vectors/query" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "my_index",
       "index_key": "your_64_character_hex_key_here",
       "query_vectors": [0.1, 0.2, 0.3, 0.4],
       "top_k": 5
     }'
```

**With Filters:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/vectors/query" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "my_index",
       "index_key": "your_64_character_hex_key_here",
       "query_vectors": [0.1, 0.2, 0.3, 0.4],
       "top_k": 10,
       "filters": {
         "$and": [
           {"category": "document"},
           {"priority": {"$gte": 3}}
         ]
       },
       "include": ["distance", "metadata"]
     }'
```

**Semantic Search:**

```bash theme={null}
# For indexes with embedding_model
curl -X POST "http://localhost:8000/v1/vectors/query" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "semantic_index",
       "index_key": "your_64_character_hex_key_here",
       "query_contents": "Find documents about machine learning",
       "top_k": 10,
       "filters": {"type": "research"}
     }'
```

**Hybrid Query (BM25 + Vector):**

```bash theme={null}
# Requires an index created with at least one full_text field.
# Each result carries `score` instead of `distance`.
curl -X POST "http://localhost:8000/v1/vectors/query" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "articles",
       "index_key": "your_64_character_hex_key_here",
       "query_vectors": [0.1, 0.2, 0.3, 0.4],
       "text": "encrypted search",
       "top_k": 10,
       "alpha": 0.5,
       "include": ["metadata"]
     }'
```

**Three-Way Query (Vector + BM25 + Metadata):**

```bash theme={null}
# Filters are resolved once and constrain both legs before retrieval.
# Every filtered field must be resolvable from the metadata index
# (filterable, or pattern for $regex) — see the hybrid filter Note.
curl -X POST "http://localhost:8000/v1/vectors/query" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "articles",
       "index_key": "your_64_character_hex_key_here",
       "query_vectors": [0.1, 0.2, 0.3, 0.4],
       "text": "encrypted search",
       "filters": {
         "$and": [
           {"category": "tech"},
           {"published": {"$gte": 1735689600000}}
         ]
       },
       "top_k": 10,
       "alpha": 0.5,
       "include": ["metadata"]
     }'
```

**Batch Query:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/vectors/query" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "my_index",
       "index_key": "your_64_character_hex_key_here",
       "query_vectors": [
         [0.1, 0.2, 0.3, 0.4],
         [0.5, 0.6, 0.7, 0.8],
         [0.9, 0.1, 0.2, 0.3]
       ],
       "top_k": 5,
       "include": ["distance"]
     }'
```

**High Recall Query:**

```bash theme={null}
curl -X POST "http://localhost:8000/v1/vectors/query" \
     -H "X-API-Key: cyborg_your_api_key_here" \
     -H "Content-Type: application/json" \
     -d '{
       "index_name": "my_index",
       "index_key": "your_64_character_hex_key_here",
       "query_vectors": [0.1, 0.2, 0.3, 0.4],
       "top_k": 20,
       "n_probes": 10,
       "rerank_mult": 20,
       "greedy": true
     }'
```

<Note>If `embedding_model` is configured for the index, you can use either `query_vectors` for direct vector search or `query_contents` for text-based semantic search.</Note>

<Tip>Higher `n_probes`, `rerank_mult`, and `greedy=true` all increase recall but reduce query performance. Start with defaults and adjust based on your recall requirements.</Tip>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.