REST API
JSON over HTTP, on port 6900 by default. Request bodies must be sent with content-type: application/json (except snapshot uploads), and may not contain unknown fields. With API keys configured, every route except / and /health needs one, in Authorization: Bearer <key> or x-api-key: <key>: read routes take any key, write routes a read-write key. See security.
Server
| Method and route | Role | Answers |
|---|---|---|
GET / | public | {"name", "version", "docs"} |
GET /health | public | {"status": "ok", "version"} |
GET /info | read | {"version", "storage", "snapshots_enabled", "collections", "points"} |
GET /metrics | read | Prometheus text format; see monitoring |
curl localhost:6900/health
curl localhost:6900/info{"status":"ok","version":"0.1.0"}
{"collections":0,"points":0,"snapshots_enabled":true,"storage":"file","version":"0.1.0"}Collections
| Method and route | Role | Body | Answers |
|---|---|---|---|
GET /collections | read | {"collections": [CollectionInfo]} | |
POST /collections | write | {"name", "dimension", "metric"?, "index"?, "quantization"?, "on_disk"?, "vectors"?, "sparse_vectors"?, "field_indexes"?} | 201 CollectionInfo |
GET /collections/{name} | read | CollectionInfo | |
PATCH /collections/{name} | write | {"index"?, "quantization"?, "on_disk"?, "vectors"?: {name: {"index"?, "quantization"?, "on_disk"?}}, "sparse_vectors"?: {name: {"modifier"}}} | CollectionInfo, after rebuilding what changed |
POST /collections/{name}/rename | write | {"name"} | CollectionInfo under the new name |
DELETE /collections/{name} | write | 204; also removes its aliases | |
POST /collections/{name}/flush | write | 204: checkpoint now | |
POST /collections/{name}/compact | write | CollectionInfo, after reclaiming deleted slots | |
POST /collections/{name}/indexes | write | {"field"} | CollectionInfo; indexing an indexed field does nothing |
DELETE /collections/{name}/indexes/{field} | write | CollectionInfo, or 404 not_found if the field has no index |
A CollectionInfo is {"name", "dimension", "metric", "index", "quantization"?, "on_disk"?, "vectors"?, "sparse_vectors"?, "count", "tombstones", "field_indexes"}, where index is {"type": "hnsw", "m", "ef_construction", "ef_search", "seed"}, {"type": "flat"} or {"type": "kd_tree", "balance_factor"}, and quantization is "scalar" or left out. vectors maps names to {"dimension", "metric", "index", "quantization"?, "on_disk"?}, and sparse_vectors names to {"modifier"?: "idf"}; both, and on_disk, are left out when unset. In PATCH, index is merged into the current settings as a JSON Merge Patch. See collections & points, indexes & metrics and field indexes.
Aliases
| Method and route | Role | Body | Answers |
|---|---|---|---|
GET /aliases | read | {"aliases": [{"alias", "collection"}]} | |
PUT /aliases/{alias} | write | {"collection"} | {"alias", "collection"}; creates or atomically re-points |
DELETE /aliases/{alias} | write | 204, or 404 not_found |
An alias works in place of a collection name on every /collections/{name}/… route except DELETE and rename. See aliases.
Points
| Method and route | Role | Body or query | Answers |
|---|---|---|---|
POST /collections/{name}/points | write | {"points": [{"id"?, "vector", "vectors"?, "sparse_vectors"?, "metadata"?}]} | {"upserted", "ids"} |
GET /collections/{name}/points | read | ?cursor=&limit=100&with_vectors=false | {"points", "next_cursor"} |
GET /collections/{name}/points/{id} | read | ?with_vectors=true | a point |
POST /collections/{name}/points/lookup | read | {"ids", "with_vectors"?: true} | {"points"}: the ids that exist, in request order |
POST /collections/{name}/points/scroll | read | {"cursor"?, "limit"?: 100, "filter"?, "order_by"?: {"field", "direction"?: "asc"}, "with_vectors"?: false} | {"points", "next_cursor"} |
POST /collections/{name}/points/count | read | {"filter"?} | {"count"} |
POST /collections/{name}/points/facet | read | {"field", "filter"?, "limit"?: 10} | {"values": [{"value", "count"}]} |
POST /collections/{name}/points/metadata | write | {"ids" or "filter", "metadata", "mode"?: "merge"} | {"updated"} |
POST /collections/{name}/points/delete | write | {"ids" or "filter"} | {"deleted"} |
DELETE /collections/{name}/points/{id} | write | 204, or 404 point_not_found |
Points whose id is lookup, count, scroll, facet, metadata or delete cannot be read with GET /points/{id}, which those routes shadow; use points/lookup. A point is {"id", "vector", "vectors"?, "sparse_vectors"?, "metadata"}; vectors maps names to dense vectors and sparse_vectors names to {"indices", "values"}. metadata is left out when the point has none, and the vectors when with_vectors is false. Lookups and deletes take at most 10,000 ids, and limit is at most 10,000. See data operations and listing points.
curl -X POST localhost:6900/collections -H 'content-type: application/json' \
-d '{"name": "places", "dimension": 2, "metric": "euclidean"}' > /dev/null
curl -X POST localhost:6900/collections/places/points -H 'content-type: application/json' \
-d '{"points": [{"id": "brussels", "vector": [4.35, 50.85]}, {"id": "lille", "vector": [3.06, 50.63]}]}'
curl -X POST localhost:6900/collections/places/points/lookup -H 'content-type: application/json' \
-d '{"ids": ["lille", "paris", "brussels"], "with_vectors": false}'{"ids":["brussels","lille"],"upserted":2}
{"points":[{"id":"lille"},{"id":"brussels"}]}Search
| Method and route | Role | Body | Answers |
|---|---|---|---|
POST /collections/{name}/search | read | {"vector", "id" or "sparse", "using"?, "k"?: 10, "offset"?: 0, "filter"?, "ef"?, "max_distance"?, "with_vectors"?: false, "with_metadata"?: true} | {"results": [hit]} |
POST /collections/{name}/search/batch | read | {"searches": [search body]}, at most 256 | {"results": [[hit]]} |
POST /collections/{name}/search/groups | read | {"vector", "id" or "sparse", "group_by", "groups"?: 10, "group_size"?: 3, "using"?, "filter"?, "ef"?, "max_distance"?, "with_vectors"?, "with_metadata"?} | {"groups": [{"key", "hits": [hit]}]} |
POST /collections/{name}/recommend | read | {"positive", "negative"?, "using"?, "k"?, "offset"?, "filter"?, "ef"?, "max_distance"?, "with_vectors"?, "with_metadata"?} | {"results": [hit]} |
POST /collections/{name}/discover | read | {"target"?, "context"?: [{"positive", "negative"}], "using"?, "k"?, "offset"?, "filter"?, "ef"?, "max_distance"?, "with_vectors"?, "with_metadata"?}; examples are ids or vectors | {"results": [hit]} |
POST /collections/{name}/query | read | {"prefetch": [{"vector" or "sparse", "using"?, "limit"?, "weight"?, "ef"?, "max_distance"?}], "fusion"?: {"type": "rrf", "k": 60} or {"type": "relative_score"} or {"type": "dbsf"}, "rescore"?: {"vector", "using"?}, "k"?, "offset"?, "filter"?, "with_vectors"?, "with_metadata"?} | {"results": [scored hit]} |
A hit is {"id", "distance", "metadata"?, "vector"?, "vectors"?, "sparse_vectors"?}, closest first; for a sparse search, distance is the negated score. A scored hit has a score instead, highest first. using names a named vector; sparse needs it. See searching.
curl -X POST localhost:6900/collections/places/search -H 'content-type: application/json' \
-d '{"vector": [5.5, 50.5], "k": 1}'{"results":[{"distance":1.2020812034606934,"id":"brussels"}]}Snapshots
| Method and route | Role | Body or query | Answers |
|---|---|---|---|
POST /collections/{name}/snapshots | write | 201 SnapshotInfo | |
GET /collections/{name}/snapshots | read | {"snapshots": [SnapshotInfo]}, oldest first | |
GET /snapshots | read | ?collection= | {"snapshots": [SnapshotInfo]} |
GET /snapshots/{id} | read | SnapshotInfo | |
GET /snapshots/{id}/download | read | the archive, application/gzip | |
POST /snapshots | write | the raw archive, up to 16 GiB | 201 SnapshotInfo |
POST /snapshots/{id}/restore | write | {"collection"?, "replace"?: false}, or empty | CollectionInfo |
DELETE /snapshots/{id} | write | 204 |
A SnapshotInfo is {"format_version", "id", "collection", "created_at_ms", "quiver_version", "config": {"dimension", "metric", "index", "quantization"?, "on_disk"?, "vectors"?, "sparse_vectors"?}, "points", "data_bytes", "data_sha256", "archive_bytes", "field_indexes"?}. See snapshots & backups.
Errors
Every error has the same shape:
{"error": {"code": "not_found", "message": "collection `nope` not found"}}| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_argument | a value is out of range or the request is inconsistent: a bad name or dimension, k out of range, not exactly one of vector/id/sparse or of ids/filter, an unknown using, an empty delete filter, a point that fails validation, a settings change that is not allowed, dropping or renaming by alias, a bad query string |
| 400 | invalid_body | malformed JSON, a wrongly typed or unknown field, a missing required field |
| 400 | invalid_filter | the filter does not parse |
| 400 | dimension_mismatch | a query vector of the wrong length |
| 400 | invalid_snapshot | an uploaded archive that cannot be read or fails its checks |
| 401 | unauthorized | no API key, or an unknown one |
| 403 | forbidden | a read-only key on a write route |
| 404 | not_found | no such collection, alias, snapshot or route |
| 404 | point_not_found | no such point (a get, a single delete, search by id, a recommend example) |
| 409 | already_exists | the name is taken by a collection or an alias (create, rename, alias, or restore without replace) |
| 413 | invalid_body | a JSON body over --max-body-mb |
| 413 | payload_too_large | a snapshot upload over 16 GiB |
| 415 | invalid_body | a JSON route without content-type: application/json |
| 500 | internal | an I/O or storage failure; the server logs it |
| 501 | unsupported | snapshots on a server without a snapshot directory |
A route that exists but not for that method answers 405 with an empty body.
curl localhost:6900/collections/nope
curl -X POST localhost:6900/collections/places/search -H 'content-type: application/json' -d '{"vector": [1.0]}'
curl -X POST localhost:6900/collections/places/search -H 'content-type: application/json' -d '{"vector": [1.0, 2.0], "top_k": 3}'{"error":{"code":"not_found","message":"collection `nope` not found"}}
{"error":{"code":"dimension_mismatch","message":"dimension mismatch: expected 2, got 1"}}
{"error":{"code":"invalid_body","message":"Failed to deserialize the JSON body into the target type: top_k: unknown field `top_k`, expected one of `vector`, `id`, `sparse`, `using`, `k`, `filter`, `ef`, `max_distance`, `with_vectors`, `with_metadata` at line 1 column 30"}}Limits
| Limit | Value |
|---|---|
| JSON request body | 64 MiB, --max-body-mb |
| Snapshot upload | 16 GiB |
k | 1–10,000, and k + offset at most 10,000 |
groups × group_size | 10,000 |
| Context pairs per discovery | 64 |
| Values per facet | 1,000 |
| Searches per batch | 256 |
| Searches fused per query | 16 |
| Non-zero values per sparse vector | 65,536 |
| Named dense vectors, sparse vectors per collection | 16 each |
| Ids per lookup or delete, examples per recommend | 10,000 |
limit when listing | 10,000 |
--cors allows cross-origin requests from any origin; leave it off unless a browser application on another origin needs the API.