Search — MDDB API
Search and query operations
Search documents #
POST /v1/search
Search documents with metadata filtering, sorting, and pagination.
Filtering: AND logic between different metadata keys, OR logic between values of the same key.
Sorting: Sort by addedAt, updatedAt, or key.
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: SearchRequest
{
"asc": false,
"collection": "blog",
"filter": {
"kind": {
"not": [
"test"
]
},
"modified": {
"gte": "2026-09-01"
},
"path": {
"prefix": "internal/"
},
"size": {
"lt": 10000
},
"summary": {
"exists": true
},
"title": {
"contains": "hnsw"
}
},
"filterMeta": {
"category": [
"blog",
"tutorial"
],
"status": [
"published"
]
},
"limit": 10,
"offset": 0,
"sort": "updatedAt"
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
Search results | array of Document (application/json) |
400 |
Invalid request | ErrorResponse (application/json) |
Extension to language table for source files #
GET /v1/code-languages
The table the server classifies code by (AGT-001, v2.18.8), so a
client walking a directory — mddb-cli autoindex — marks files the
way the server would.
Authorization: BearerAuth or APIKeyAuth or none
Responses #
| Status | Description | Body |
|---|---|---|
200 |
The table | object (application/json) |
405 |
Method not allowed |
Where a symbol is defined, used, called and tested #
GET /v1/symbol
Definitions with their span, references with the definition each sits
in, callers, callees, tests and siblings of one symbol, as passages
(CODE-008, v2.18.8). Candidates come from the metadata index
(defines/uses) and the full-text index; spans are computed from the
documents at query time, so nothing is stored. Also the code_symbol
MCP tool and the codeSymbol GraphQL query. POST takes the same
fields as a JSON body.
Authorization: BearerAuth or APIKeyAuth or none
Parameters #
| Name | In | Type | Description |
|---|---|---|---|
collection (required) |
query | string |
|
name (required) |
query | string |
|
include |
query | string |
Comma-separated relations; default all. |
limit |
query | integer |
Default 20. Maximum 200. |
includeContent |
query | boolean |
Default false. |
Responses #
| Status | Description | Body |
|---|---|---|
200 |
The symbol's relations | SymbolResult (application/json) |
400 |
Missing collection or name, or an unreadable body | |
403 |
Forbidden | |
422 |
Unknown relation in include |
Symbol lookup (JSON body) #
POST /v1/symbol
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: SymbolRequest
{
"collection": "string",
"include": [
"definitions"
],
"includeContent": false,
"limit": 20,
"name": "string"
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
The symbol's relations | SymbolResult (application/json) |
400 |
Missing collection or name, or an unreadable body | |
422 |
Unknown relation in include |
Code connection graph for one document #
GET /v1/code-graph
Answers the relational questions full-text search cannot: what breaks if this selector changes, which pages load this script, which file defines a function this one calls, which manifest declares the ConfigMap a Deployment refers to, what does nothing reference any more. Edges are derived at query time from the defines/uses/imports metadata of code documents through the metadata index — nothing is stored, so a reindex reproduces the graph exactly. Resolution stays inside one collection.
Authorization: BearerAuth or APIKeyAuth or none
Parameters #
| Name | In | Type | Description |
|---|---|---|---|
collection (required) |
query | string |
Collection name |
key (required) |
query | string |
Document key, e.g. theme/style.css |
direction |
query | string |
in = what depends on this document, out = what it depends on. One of "in", "out", "both". Default "both". |
depth |
query | integer |
Hops to follow. Values above the maximum are clamped, not rejected. Default 1. Minimum 1. Maximum 3. |
maxDegree |
query | integer |
Maximum neighbours per node. A selector such as .title appears in nearly every template, so an unbounded walk returns the whole theme. Default 100. Minimum 1. Maximum 100. |
lines |
query | boolean |
Include the first line each edge's symbol appears on, both sides. Off by default: it is the only part of a traversal that reads document content. Default false. |
Responses #
| Status | Description | Body |
|---|---|---|
200 |
The graph around the requested document | CodeGraphResponse (application/json) |
400 |
Missing collection/key, or an unknown direction | |
404 |
No such document in the collection |
Code connection graph for one document (JSON body) #
POST /v1/code-graph
Identical to the GET form. Accepts a JSON body for keys containing characters awkward in a query string.
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: object
| Property | Type | Description |
|---|---|---|
collection (required) |
string |
|
key (required) |
string |
|
direction |
string |
One of "in", "out", "both". Default "both". |
depth |
integer |
Default 1. Minimum 1. Maximum 3. |
maxDegree |
integer |
Default 100. Minimum 1. Maximum 100. |
lines |
boolean |
Include first-occurrence line numbers for each edge. Default false. |
{
"collection": "theme",
"depth": 2,
"direction": "in",
"key": "theme/style.css"
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
The graph around the requested document | CodeGraphResponse (application/json) |
400 |
Missing collection/key, or an unknown direction | |
404 |
No such document in the collection |
List metadata keys and values for a collection #
GET /v1/meta-keys
Returns all unique metadata keys and their unique values for a given collection. Useful for building filter UIs.
Authorization: BearerAuth or APIKeyAuth or none
Parameters #
| Name | In | Type | Description |
|---|---|---|---|
collection (required) |
query | string |
Collection name |
Responses #
| Status | Description | Body |
|---|---|---|
200 |
Metadata keys with their unique values | object (application/json) |
400 |
Missing collection parameter |
Is what was just written searchable yet? #
GET /v1/indexing-status
Queued embedding and metadata work for one collection, or for the
whole server without collection (GO-042, v2.18.8). Counters come
from the queues, never from a scan. A write returns once its full
text and metadata are indexed; its embedding follows from a
background worker, so a semantic search sees the document only when
pendingEmbeddings reaches 0. To wait for that inside a search, pass
waitForIndex / waitForKeys; to be told, listen for the SSE event
collection.indexed.
Also available as the get_indexing_status MCP tool, the
GetIndexingStatus gRPC call and the GraphQL indexingStatus query.
Authorization: BearerAuth or APIKeyAuth or none
Parameters #
| Name | In | Type | Description |
|---|---|---|---|
collection |
query | string |
Responses #
| Status | Description | Body |
|---|---|---|
200 |
Indexing status | IndexingStatus (application/json) |
403 |
Forbidden | ErrorResponse (application/json) |
405 |
Method not allowed |
What retrieval saved over a window of days #
GET /v1/gain
Server-wide counts over a window of days (AGT-004, v2.18.8). They
cover searches by type, algorithm and hybrid fusion, tokens returned,
and tokens avoided. Tokens avoided are whole documents minus the
passages or chunks returned instead. Counts only: no query text,
keys or collection names. Kept per day in the database, so restarts
do not reset them. Definitions are in docs/TELEMETRY.md. Also the
get_gain MCP tool.
Authorization: BearerAuth or APIKeyAuth or none
Parameters #
| Name | In | Type | Description |
|---|---|---|---|
window |
query | string |
Days, as 7d or 7, from 1 to 365. Default "30d". |
daily |
query | boolean |
Add a per-day breakdown. Default false. |
Responses #
| Status | Description | Body |
|---|---|---|
200 |
The report | GainReport (application/json) |
400 |
Invalid window | ErrorResponse (application/json) |
403 |
Forbidden | ErrorResponse (application/json) |
405 |
Method not allowed |
Measure retrieval on an evaluation set #
POST /v1/eval
Runs the collection's evaluation set, or the inline queries, under
up to 8 search configurations. For each configuration it returns
recall@k, nDCG@k, MRR, p50/p99 latency and estimated returned tokens
(SRCH-019, v2.18.8). Read-only. A configuration that fails carries
error and does not stop the others. Also the run_eval MCP tool.
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: EvalRequest
{
"collection": "string",
"configs": [
{
"algorithm": "string",
"alpha": 0,
"bm25B": 0,
"bm25K1": 0,
"name": "string",
"queryDecomposition": "off",
"retrievalMode": "string",
"searchType": "fts",
"strategy": "string",
"summaryMode": "off",
"vectorAlgorithm": "string"
}
],
"details": false,
"k": 10,
"queries": [
{
"expectedKeys": [
"string"
],
"id": "string",
"query": "string"
}
]
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
Scores per configuration | EvalResult (application/json) |
400 |
No evaluation queries, an invalid query, or too many queries or configs | ErrorResponse (application/json) |
403 |
Forbidden | ErrorResponse (application/json) |
405 |
Method not allowed |
List a collection's evaluation set #
GET /v1/eval-queries
Authorization: BearerAuth or APIKeyAuth or none
Parameters #
| Name | In | Type | Description |
|---|---|---|---|
collection (required) |
query | string |
Responses #
| Status | Description | Body |
|---|---|---|
200 |
The stored queries | object (application/json) |
403 |
Forbidden | ErrorResponse (application/json) |
Add a query to the evaluation set #
POST /v1/eval-queries
Stores one query. An existing id is replaced, and a missing one is
generated. The set lives in the collection config as evalSet, but
PUT /v1/collection-config keeps it, so this is the only way to
change it. At most 200 queries. Also the add_eval_query MCP tool.
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: allOf EvalQuery & object
{
"expectedKeys": [
"string"
],
"id": "string",
"query": "string"
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
The stored query, with its id | EvalQuery (application/json) |
400 |
Invalid query, or the set is full | ErrorResponse (application/json) |
403 |
Forbidden, or read-only mode | ErrorResponse (application/json) |
Remove a query from the evaluation set #
DELETE /v1/eval-queries
Authorization: BearerAuth or APIKeyAuth or none
Parameters #
| Name | In | Type | Description |
|---|---|---|---|
collection (required) |
query | string |
|
id (required) |
query | string |
Responses #
| Status | Description | Body |
|---|---|---|
200 |
Whether a query was removed | object (application/json) |
403 |
Forbidden, or read-only mode | ErrorResponse (application/json) |
How should I search this collection? #
GET /v1/search-advisor
Measures a collection and recommends the search type, ranking algorithm, vector index, fusion weights and result shape — with a plain-language reason for every choice (SRCH-010).
Read-only by default. apply=true additionally writes the
recommendation into the collection's retrieval profile, which needs
write permission.
Also available as the search_advisor MCP tool, annotated read-only,
so an agent can ask before it searches instead of guessing from
algorithm names.
Authorization: BearerAuth or APIKeyAuth or none
Parameters #
| Name | In | Type | Description |
|---|---|---|---|
collection (required) |
query | string |
|
apply |
query | boolean |
Store the recommendation as the collection's retrieval profile. Default false. |
Responses #
| Status | Description | Body |
|---|---|---|
200 |
The recommendation | SearchRecommendation (application/json) |
403 |
Forbidden, or read-only mode when apply=true | ErrorResponse (application/json) |
How should I search this collection? (body form) #
POST /v1/search-advisor
Authorization: BearerAuth or APIKeyAuth or none
Request body #
Required.
Content type application/json: object
| Property | Type | Description |
|---|---|---|
collection (required) |
string |
|
apply |
boolean |
Default false. |
{
"apply": false,
"collection": "string"
}
Responses #
| Status | Description | Body |
|---|---|---|
200 |
The recommendation | SearchRecommendation (application/json) |
400 |
The request is malformed (a body that is not JSON, a missing or invalid field, an unreadable body) or names a subsystem that is not initialized. The message says which. | ErrorResponse (application/json) |
401 |
Auth is on (MDDB_AUTH_ENABLED=true) and the request carries no credentials, an invalid or expired JWT, or an unknown API key. | ErrorResponse (application/json) |
403 |
The caller lacks the permission the route needs (SEC-019 access gate, auth on: read, write or admin on the collections the request names), or a write meets read-only mode (MDDB_MODE=read or the protocol's own mode). | ErrorResponse (application/json) |
413 |
The body is over MDDB_MAX_BODY_BYTES (32 MiB by default). | ErrorResponse (application/json) |
429 |
The client is over the API rate limit (MDDB_RATE_LIMIT_ENABLED=true, MDDB_RATE_LIMIT_REQUESTS per MDDB_RATE_LIMIT_WINDOW seconds). A call that embeds can also be refused by the collection's daily embedding budget, with a QuotaExceeded body. | RateLimitError (application/json) |
400 example (application/json)
{
"error": "missing collection"
}
401 example (application/json)
{
"error": "missing authentication"
}
403 example (application/json)
{
"error": "forbidden"
}
413 example (application/json)
{
"error": "request body too large"
}