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.

OperationHTTPgRPC
PutVectorPUT /v1/collections/{c}/vectors/{id} (or POST …/vectors with the id in the body)PutVector
PutVectors (batch)POST /v1/collections/{c}/vectors:batchPutVectors
GetVectorGET /v1/collections/{c}/vectors/{id}?typedGetVector
GetVectors (batch)POST /v1/collections/{c}/vectors:getGetVectors
ListVectorsGET /v1/collections/{c}/vectors?limit&offset&filter&typedListVectors
DeleteVectorDELETE /v1/collections/{c}/vectors/{id}DeleteVector
SearchVectorsPOST /v1/collections/{c}/querySearchVectors
Liveness probeGET /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"

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

StatusMeaning
400Invalid argument: malformed body, wrong vector dimension, unknown field, or fusion options on a single-leg query.
404Vector not found.
413Request body over 128 MiB.
421Wrong node in a placement-enabled fleet. The owner is named in the body and (when known) the X-Polign-Owner header; retry against it.
429Rate limit exceeded, on servers started with -rate-limit.
501Listing 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.
503Write backpressure: the unpersisted tail overlay is at its cap. Comes with Retry-After: 1; back off and retry.
500Anything 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.