HTTP API
Every operation over plain HTTP/JSON on port 23000. It is the
same surface the CLI and Python SDK use
underneath. The identical operations over gRPC are in the
gRPC API reference.
Operations
Collections are auto-created on first PutVector, inferring their dimension from the vector. Batch puts and batch gets take up to 5,000 vectors per request; a batch put is validated as a whole before anything is applied. Listings page at 100 records by default, up to a limit of 1,000. The data-plane routes below need no credentials.
| Operation | HTTP | gRPC |
|---|---|---|
| PutVector | PUT /v1/collections/{c}/vectors/{id} (or POST …/vectors with the id in the body) | PutVector |
| PutVectors (batch) | POST /v1/collections/{c}/vectors:batch | PutVectors |
| GetVector | GET /v1/collections/{c}/vectors/{id}?typed | GetVector |
| GetVectors (batch) | POST /v1/collections/{c}/vectors:get | GetVectors |
| ListVectors | GET /v1/collections/{c}/vectors?limit&offset&filter&typed | ListVectors |
| DeleteVector | DELETE /v1/collections/{c}/vectors/{id} | DeleteVector |
| SearchVectors | POST /v1/collections/{c}/query | SearchVectors |
| Liveness probe | GET /healthz | — |
Servers running the collections registry (-byo-store) additionally expose
the collection-management routes — POST/GET/DELETE /v1/collections/{c},
GET /v1/collections, POST /v1/collections/{c}/verify, and
GET /v1/setup?uri=… — plus the key-guarded /v1/admin surface. The
operations guide walks that whole
lifecycle; on other servers the collection routes return "not enabled" (501).
Typed metadata
Metadata values are typed scalars — strings, numbers, and booleans — written as plain
JSON (server v0.3.0+). null, arrays, and nested objects are a 400. Responses
render every value as a string unless the request opts in: "typed_metadata": true
in a query or vectors:get body, ?typed=true on GET
and list. Filters compare typed values by kind (numbers numerically, so 5
matches 5.0); two legacy rules keep string data working (a string record value
matches a typed operand by its literal form, and numeric range bounds match string values
that parse as floats), but a value stored typed no longer matches a string operand.
# Put (auto-creates the "docs" collection as 3-dimensional) curl -X PUT localhost:23000/v1/collections/docs/vectors/a \ -d '{"values":[1,0,0],"metadata":{"label":"first","score":0.85,"published":true,"text":"a friendly hello"}}' # Nearest-neighbour search; filters take operators ($in, $gte, $exists, …) # composed with $and/$or/$not curl -X POST localhost:23000/v1/collections/docs/query \ -d '{"values":[0.9,0.1,0],"k":5,"filter":{"label":"first"}}' # Filtered listing: the same filter language, URL-encoded into the query # string; offset and the returned total count matching records only curl "localhost:23000/v1/collections/docs/vectors?filter=%7B%22label%22%3A%22first%22%7D"
Text & hybrid search
The same query endpoint also runs BM25 keyword search and hybrid search. Send text alone for pure BM25, or text plus values to run both legs concurrently and get one fused ranking (Reciprocal Rank Fusion by default, or a weighted linear blend). Each hit carries a score (larger is better). The text index is built by the persistor over a metadata field (default "text", set with the persistor's -text-field flag), so text search needs the server started with -segment-stores:
# pure BM25 keyword search curl -X POST localhost:23000/v1/collections/docs/query \ -d '{"text":"friendly hello","k":5}' # hybrid: vector + text, fused server-side (RRF is the default) curl -X POST localhost:23000/v1/collections/docs/query \ -d '{"values":[0.9,0.1,0],"text":"friendly hello","k":5, "fusion":{"method":"linear","alpha":0.7}}'
The full query body is values, k, ef (search beam
width override, 0 = server default), filter, text,
fusion, cold (serve straight from object-storage segments),
nprobe (IVF probe-count override for cold queries), rescore
(accuracy/speed dial for IVF-PQ collections), and typed_metadata. Fusion
options are only valid when both legs are present, so asking for them on a single-leg query
is a 400. Unknown fields are rejected, so an older server answers an unrecognized
text/fusion/typed_metadata field with a 400 rather
than silently ignoring it.
Errors
| Status | Meaning |
|---|---|
400 | Invalid argument: malformed body, wrong vector dimension, unknown field, or fusion options on a single-leg query. |
404 | Vector not found. |
413 | Request body over 128 MiB. |
421 | Wrong node in a placement-enabled fleet. The owner is named in the body and (when known) the X-Polign-Owner header; retry against it. |
429 | Rate limit exceeded, on servers started with -rate-limit. |
501 | Listing a cold-served resource this node keeps no in-memory index for — its records live in segments; search or batch-get by id instead. Also returned by the collection routes when -byo-store is off. |
503 | Write backpressure: the unpersisted tail overlay is at its cap. Comes with Retry-After: 1; back off and retry. |
500 | Anything else. The body carries a generic message; the detail is in the server log. |
The collection-management and admin routes add auth-related statuses
(401/403/409), and the full table is in the
operations guide.