Run the REST API¶
The HTTP server is an optional extra, so that the core install does not drag in a web stack:
Without it, importing the API tells you what to do:
>>> import vectrixdb.api
DependencyError: 'fastapi' is required for this feature.
Install it with: pip install vectrixdb[api]
Start it¶
Or from Python:
Interactive API docs are at /docs. The dashboard is at /dashboard/; /
returns a small JSON index, not the UI.
With the settings kept in a file, vectrixdb serve --env-file vectrixdb.env
reads it first, and vectrixdb check --env-file vectrixdb.env says beforehand
what a start would refuse. See Deploy the server.
create_app() builds an application you can mount or configure yourself, and
it can be called as many times as you like:
from vectrixdb.api.server import create_app
app = create_app(db_path="./vectrixdb_data", enable_dashboard=False)
Or point uvicorn at the import string for the shared singleton:
Query it¶
Add some text and let the server embed it:
curl -X POST http://127.0.0.1:8000/api/v1/collections/my_docs/text-upsert \
-H 'Content-Type: application/json' \
-d '{"points": [{"id": "d1", "text": "python is a programming language"}]}'
Then search by text:
curl -X POST http://127.0.0.1:8000/api/v1/collections/my_docs/text-search \
-H 'Content-Type: application/json' \
-d '{"query_text": "programming", "limit": 5}'
/text-search embeds the query for you, and "rerank": true re-ranks the
top candidates with the bundled cross-encoder. /search is the lower-level
route and takes a vector, not a string. Every route lives under /api/v1; the same
paths under /api are kept for the dashboard and are not documented. Every
documented route, with who may call it, is in the
REST API reference.
Read what a collection is¶
The dashboard's collection pages are built on read routes that describe a collection rather than search it:
| Route | What it answers |
|---|---|
GET /api/v1/collections/{name}/health |
count, deleted-vector ratio and whether a rebuild is advised, language, model, current build |
GET /api/v1/collections/{name}/policy |
the entitlement policy as data, with its fingerprint |
GET /api/v1/collections/{name}/builds |
every index build that still has chunks, read off the chunks: how many, when it first wrote, their mean quality and how many are under the line |
GET /api/v1/collections/{name}/growth?days=30 |
chunks written on each of the last days, today last, counted from the chunks that are here |
GET /api/v1/growth?days=14 |
the same summed across every collection, with low, the chunks of each day under the quality line |
GET /api/v1/collections/{name}/quality |
the extraction-quality histogram, the count below the line, the worst chunks; offset pages through them |
GET /api/v1/access/daily?days=14 |
searches a day and how long they took, for everyone; sign-ins, refusals and the searches a policy denied for whoever may read the access log |
GET /api/v1/collections/{name}/provenance/{id} |
where one chunk came from |
POST /api/v1/collections/{name}/rebuild |
rebuild the ANN index, dropping tombstones |
GET /api/v1/models |
every model the library knows and whether it is on this machine |
GET /api/v1/audit |
the audit trail, under the conditions below |
The evaluation runs the library saved are read with GET /api/v1/evaluations,
with offset for the next page, and GET /api/v1/evaluations/{run}, latest
for the newest. GET /api/v1/evaluations/{run}/golden is the golden file a run
read, for an admin signed in as a person and never a key; see
Evaluate every setup.
A collection that carries an entitlement policy answers health and policy
with configuration only and refuses the rest with the same 403 every content
route uses: this API resolves no principals, so it cannot say who is entitled
to a count.
Every write through the API mints an index build id and stamps it on the
chunks it lands, the same _vx_build the library writes, so builds and
provenance cover API-written chunks too.
/api/v1/audit is served only when the audit trail is kept, in
VECTRIXDB_AUDIT_STORE or the older VECTRIXDB_AUDIT_JSONL, and only to a
caller allowed to read it: with sign-in on, a role that holds audit.read;
without it, a request carrying VECTRIXDB_API_KEY itself. See
Audit trail. Records go out
without the principal snapshot, the result ids and the undisclosable count.
The audit log is more sensitive than the index it audits, which is why an open
server never serves it.
Writing an app against this server, in any language, is Build an app on it.
Before exposing it¶
Authentication is off until you turn it on
Nothing is required by default. Set VECTRIXDB_API_KEY and writes then need
an api-key header; without one they answer 401. Reads stay open either
way, which is deliberate and is the Qdrant convention, so a key alone does
not make a collection private.
export VECTRIXDB_API_KEY=$(python -c "import secrets; print(secrets.token_urlsafe(32))")
vectrixdb serve --host 127.0.0.1 --port 8000
curl -X POST http://127.0.0.1:8000/api/v1/collections \
-H "api-key: $VECTRIXDB_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"name": "my_docs", "dimension": 384}'
VECTRIXDB_READ_ONLY_API_KEY sets a second key that may read but not write,
and GET / reports auth_enabled so you can tell which mode a server is in.
VECTRIXDB_API_KEY_FILE and VECTRIXDB_READ_ONLY_API_KEY_FILE read the keys
from files, and VECTRIXDB_API_KEY_SHA256 and
VECTRIXDB_READ_ONLY_API_KEY_SHA256 take a key's SHA-256 in place of the key,
so the server's environment never holds one that works.
With a key and no sign-in, a read still needs no key: anyone who reaches the
port lists the collections and reads the points, text and vectors included.
VECTRIXDB_OPEN_READS=0 closes that, so every read, and the live feed at
/ws, asks for the full or the read-only key; only /, /health, /ready,
/auth/status, /openapi.json, the MCP sign-in description under
/.well-known/ and the dashboard's own files stay public.
With sign-in on, an admin can also make named keys for scripts, each with its
own role, shown once and revoked on its own: see
Sign people in.
Behind a gateway, the same routes are published by the team that owns it,
from the OpenAPI document this server serves at /openapi.json. See
Put it behind a gateway.
None of that is a substitute for a boundary. A named key can carry a
per-minute allowance (vectrixdb keys add --per-minute, or
VECTRIXDB_KEY_REQUESTS_PER_MINUTE for every key), and guests are limited,
but the server's own key never is and there is no tenancy, so keep it on a
private network or behind a proxy that terminates TLS. API keys are for machines: to let people in as
themselves, with roles and a record of who read what, see
Sign people in. The server listens on 127.0.0.1 by default and
refuses any other address with no key and no sign-in. Bind to 127.0.0.1 rather than 0.0.0.0 unless
something in front of it is doing access control.
Documents¶
Send a file and the server reads it, cuts it, embeds it and writes it, the way add_document() does: the same chunk ids, citations, lineage stamps and figure links, because both call the same function.
curl -X POST "http://localhost:7337/api/v1/collections/docs/documents?chunk=markdown&chunk_size=1000" -H "X-Filename: msa.pdf" -H "api-key: $KEY" --data-binary @msa.pdf
The file is the request body and its name travels in X-Filename, which needs no form parser on either side. A multipart form with a file field works too when python-multipart is installed on the server. The query parameters are doc_id (the file's name when left out), chunk (markdown, recursive or sentence), chunk_size, overlap, embed_heading, on_low_quality and metadata, a JSON object put on every chunk. Sending the same id again replaces the document.
{"ok": true, "doc_id": "msa.pdf", "replaced": 0, "chunks": 14, "quality": 0.93, "low_quality": false,
"extractor": "built-in", "pages": 6, "figures": 1,
"citations": ["msa.pdf#page=1", "msa.pdf#page=2"], "build": "build_5c1e0a97b2d44f10", "kept": true}
| Route | What it does |
|---|---|
POST /api/v1/collections/{name}/documents |
read, cut, embed and write one file |
GET /api/v1/collections/{name}/documents |
the documents the server has kept |
GET /api/v1/collections/{name}/documents/{doc_id} |
the Markdown a document was indexed from; ?raw=true keeps the front matter |
DELETE /api/v1/collections/{name}/documents/{doc_id} |
its chunks, and its kept copy moved aside |
GET /api/v1/extractors |
which file types this server reads, and who reads them |
Settings, all environment variables:
| Variable | Meaning |
|---|---|
VECTRIXDB_KEEP_SOURCE=1 |
keep the Markdown beside each collection; the two GET routes and the dashboard's Open document need it |
VECTRIXDB_EXTRACTOR_URL |
a service that reads file types for this server, as HttpExtractor does |
VECTRIXDB_EXTRACTOR_ROUTES |
JSON, suffix to path. Left out, .pdf .docx .doc go to /extract/... and .png .jpg .jpeg .wav .mp3 .m4a .mp4 to /transcribe/.... For VectrixDB's own extraction service, extraction_routes() is the whole table |
VECTRIXDB_EXTRACTOR_BODY |
raw, the default, or multipart |
VECTRIXDB_EXTRACTOR_KEY, VECTRIXDB_EXTRACTOR_KEY_HEADER |
a key for that service and the header it goes in, x-api-key by default |
VECTRIXDB_MAX_UPLOAD_BYTES |
the largest file read, 100 MiB by default; a request that says it is bigger is refused before its body is read |
A service that fails is a 502 naming the route and its status, and nothing is written. A file type nothing here can read is a 501 naming the extra to install. Like every data route, these refuse a collection that carries an entitlement policy, and the server never fetches an address a request names. What the route does not do is what needs a Vectrix: parent sections, deduplication and semantic chunking.
Listings and excerpts without the text¶
For a page that lists chunks, or any caller that should not be sent content it has not asked for:
| Request | What comes back |
|---|---|
GET /api/v1/collections/{name}/points?index=true |
rows: id, source, document, page and quality for each id. Never text. |
POST .../search?snippet=200 (also text-search, text-hybrid-search, keyword-search) |
Each result's text and highlights cut to 200 characters, with snipped: true when something was cut. |
GET .../quality?text=false |
The lowest chunks with reasons and below_line, and no excerpt. |
GET .../provenance/{id}?text=false |
Where the chunk came from, and no excerpt. |
Without the parameters every reply is what it always was. The text of one chunk
is GET /api/v1/collections/{name}/points/{id}.