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.
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
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).
| Measure | arxiv-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, tuned | 0.982 at 1.27 ms | 0.968 at 1.65 ms |
| Queries / s, 8 at a time | 5,600 | 5,560 |
| With a filter passing 10% | 0.989 at 4.38 ms | 0.989 at 4.49 ms |
| Hybrid: text + filter (1.5% pass) | 0.982 at 50 ms | 0.976 at 58 ms |
| Write to searchable (p50) | 22 ms | 23 ms |
| Cold query (p50), +20 ms a request | 63 ms | 60 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.