Vector search

Vector search on S3, narrowed by exact text and filters.

IVF with RaBitQ codes, stored in object storage and cached where queries land: recall@10 of 0.97 on 1.34M vectors in under a millisecond warm, two parallel round trips cold, and nothing running while nobody asks.

0.97recall@10, 1.34M vectors, defaults
0.8msp50 warm query at that recall
5,600queries / s, 8 threads
2parallel round trips cold

Two round trips cold

A cold query reads the manifest and a small map of the indexes side by side, then only the clusters it probes. No warm-up download of the whole namespace before the first answer.

Hybrid without a second system

Exact text, regex and metadata filters narrow the candidates before ranking, on the same copy of the data. No vector store and search engine to keep in sync.

Complete or says so

A top-k query returns k hits whenever k documents qualify; otherwise it widens its probes, and answers partial: true only if it still can’t. Ties are broken by id.

One query for meaning, words and metadata

vectors
NS=https://api.skimmerdb.com/v1/namespaces/notes

# write: a vector with each document (its length sets the namespace's dims)
curl $NS/docs -H "Authorization: Bearer $KEY" -d '{"docs": [
  {"id": "n-17", "text": "Retry with backoff on 429 from the billing API",
   "vector": [0.12, -0.03, ...], "meta": {"team": "api"}}]}'

# nearest neighbours that also contain a word and pass a filter
curl $NS/search -H "Authorization: Bearer $KEY" -d '{
  "vector": [0.11, -0.02, ...], "q": "billing",
  "filter": {"team": "api"}, "limit": 10}'
# -> {"hits": [{"id": "n-17", "score": 0.912, ...}], "took_ms": 0.73,
#     "temperature": "warm", "store_gets": 0, ...}

Measured

Against each dataset’s brute-force ground truth. Defaults: nprobe 48, rerank 64 (int8).

Measurearxiv-nomic-768 (1.34M, cosine)GIST-960 (1M, euclidean)
Recall@10, defaults (p50 latency)0.970 (0.80 ms)0.913 (0.86 ms)
Recall@10, tuned0.982 at 1.27 ms0.968 at 1.65 ms
Queries / s, 8 at a time5,6005,560
With a filter passing 10%0.989 at 4.38 ms0.989 at 4.49 ms
Hybrid: text + filter (1.5% pass)0.982 at 50 ms0.976 at 58 ms
Write to searchable (p50)22 ms23 ms
Cold query (p50), +20 ms a request63 ms60 ms

In-process, 8 threads on a 14-core laptop. Cold rows add 20 ms to every object-store request to model S3. More, including runs on real S3, on the benchmarks page.

Questions

How does vector search on S3 work?

Every namespace is stored in object storage. Recent writes are searched exactly; compaction builds an IVF index per segment over RaBitQ 1-bit codes, with int8 rows for reranking and the float32 rows kept in object storage. Servers cache what queries read in RAM and NVMe, so warm queries make no object-store reads at all.

What recall should I expect?

With the defaults (nprobe 48, rerank 64), recall@10 was 0.970 on 1.34M 768-dimension arxiv embeddings and 0.913 on GIST-960, measured against brute-force ground truth. Raising nprobe and rerank, or full: true to rerank with the float32 vectors, trades latency for recall.

Can I combine vector search with keyword search and filters?

Yes, in one query: q (literal, or a regex) and filter narrow the candidates first, and the answer is the nearest among those that pass. A common word is checked on the nearest vectors instead of every document.

Which dimensions and metrics are supported?

1 to 2,048 dimensions, cosine (vectors are normalised when stored) or euclidean, set when the namespace is created. The first write with vectors creates a cosine namespace of that length.

What does an idle vector namespace cost?

Only its storage, $0.10 per GB-month on pro. No server is reserved for it; its first query after a sleep reads what it needs from S3.

Store your first vectors

Free up to 1 GB stored and 100k queries a month. No card to start.