Writes are queryable immediately
No reindexing step, no maintenance window. A vector inserted at 14:02 is a candidate for the query at 14:02.
You send embeddings. The engine builds the index, keeps it current while you write to it, and answers similarity queries up to a billion vectors.
No credit card · Free tier never expires
There is no console to learn before you can evaluate this. The whole product is a request and what comes back from it.
Below: a k-NN query with a structured filter, and the response it returns. The filter is applied inside the graph traversal, which is why the score of the last result is still high.
POST /v1/indexes/handbook/query
{
"vector": [0.021, -0.118, 0.334, ...],
"k": 3,
"filter": {
"lang": "en",
"updated_at": { "$gte": "2026-01-01" }
}
} {
"matches": [
{ "id": "doc_9f2c", "score": 0.918 },
{ "id": "doc_41ab", "score": 0.902 },
{ "id": "doc_7d30", "score": 0.887 }
],
"took_ms": 11.4
} It starts well. A library, a graph in memory, good results. Then the corpus grows, filtered queries begin returning almost nothing, reindexing needs a window nobody can schedule, and the p99 drifts while the median looks fine.
None of that is a search problem. It is an operations problem that search handed you — and it lands on the person who least wanted it.
No reindexing step, no maintenance window. A vector inserted at 14:02 is a candidate for the query at 14:02.
Metadata predicates are evaluated during graph traversal, not after it. On a filter matching 1 % of the corpus, recall@20 stays at 0.97 instead of collapsing to 0.61.
p50 and p99 are measured continuously and published. A latency budget can be set per query, so a slow neighbourhood degrades the result rather than the response time.
Reindex in place, behind the same alias, while the old graph keeps serving. Dimension is declared at creation, not baked into your application.
Batch upload from any embedding provider, up to 4096 dimensions. Namespaces keep datasets apart inside one index.
A multi-layer HNSW graph, sharded automatically, compacted in the background. Nothing to schedule and nothing to tune before you have a reason to.
k nearest neighbours with cosine, dot product or Euclidean distance, a score on every result, and structured filters applied inside the traversal.
Ingestion, HNSW construction and continuous updates
k-NN search and control over the recall/latency trade-off
Structured constraints inside the vector search
Measured latency, capacity and scaling
Regions, security and integration
Filtering inside the index instead of after it was the whole difference. Our recall stopped depending on how narrow the filter was.
Head of engineering, Corpus Legal — read the case study
Any of them. The engine stores and compares vectors; it never produces them. You declare the dimension when you create the index, and you can change model later with an in-place reindex.
No. The defaults are sized for a general corpus and most indexes never move off them. When you do need to, m and efConstruction are exposed on the Team tier, and efSearch can be set per query.
They keep being served. Writes are merged into the graph continuously, and compaction runs in the background. There is no read-only phase.
That is exactly what metadata filters are. The difference from bolting a filter on afterwards is that the predicate steers the traversal, so a narrow filter no longer destroys recall.
Bring a validation set of known-correct neighbours and the console reports recall@k against it. Recall you have not measured is a guess, whoever is hosting the index.
Often not. Below roughly a million vectors with light filtering, pgvector inside the Postgres you already run is the pragmatic answer, and we say so on the comparison page.
A million vectors, one index, no card and no expiry. If your corpus outgrows it, nothing has to be rebuilt.