Each collection is its own vector index. The default is 768 dimensions — what Harakumo's default embedding model (bge-base) produces, so text you send fits as-is. Choose 384 (bge-small) or 1,024 (bge-large) to match those models, or 1,536 for vectors you make elsewhere; larger is refused.
In plain words
Search by meaning instead of exact words. You add your texts, and it finds the ones closest in meaning to what someone asks.
For example: Someone types "how do I get my money back" into a help center and finds the article called "Refunds".
Embed, upsert, query
SDK
const { collection } = await hk.vectors.create(project.id, {
name: 'docs', dimensions: 768, metric: 'cosine',
metadataIndexes: [{ propertyName: 'lang', indexType: 'string' }],
});
// Send text and let Harakumo embed it (metered on AI credits), or send your own "values".
await hk.vectors.upsert(collection.id, [
{ id: 'doc-1', text: 'Peaches are in season in July.', metadata: { lang: 'en' } },
{ id: 'doc-2', text: 'Mangoes ripen best at room temperature.', metadata: { lang: 'en' } },
]);
const { matches } = await hk.vectors.query(collection.id, {
text: 'when can I buy peaches?', topK: 3, filter: { lang: 'en' }, returnMetadata: 'all',
});Namespaces and filters
- Give each vector a
namespace(one per end user, say); a query that names the namespace sees only its vectors. - Filters work on metadata fields made filterable with
metadataIndexesat create time, or later withPOST /api/vectors/:id { action: "index-metadata", propertyName, indexType }. Only vectors written after a field is indexed are filterable. Filtering on a field that is not indexed is a 400 that says so. - Delete vectors with
POST /api/vectors/:id { action: "delete", ids: [...] }.
Limits
- Upsert up to 1,000 vectors per request (100 when sending text); metadata up to 10 KB per vector.
- topK up to 100, or 50 when returning full metadata or values.
- Upserts are applied in the background: new vectors become searchable in about a minute, and the upsert answer says so.
GET /api/vectors/:idreports the index's own count, which trails new writes by as much. - Collections per plan: 1 hobby, 10 pro, 25 team, 100 scale. Vectors per collection: 10,000 hobby, 1,000,000 pro, 1,000,000 team, 1,000,000 scale; a write that would pass it answers 402
plan_limitand writes nothing. Near the limit every vector sent counts as new until the index has processed it, even one that replaces an existing id, and a delete makes room once the index has processed it. - Keys: a key limited to the project works for everything here; a read-only key can query but not upsert or delete.
Reference
SDK
const { embeddings } = await hk.ai.embed(['fresh mango', 'ripe banana'], 'bge-base'); // 768 dims
await hk.vectors.upsert(collectionId, [{ id: 'a', values: embeddings[0], metadata: { title: 'Mango' } }]);
const { matches } = await hk.vectors.query(collectionId, { vector: embeddings[1], topK: 5 });REST
POST /api/projects/:id/vectors { "name": "docs", "dimensions"?: 768, "metric"?: "cosine", "metadataIndexes"? }
POST /api/vectors/:id/upsert { "vectors": [{ "id": "doc-1", "values"?: [...], "text"?: "…", "metadata"?: {}, "namespace"? }] }
POST /api/vectors/:id/query { "vector"?: [...], "text"?: "…", "topK"?: 5, "filter"?, "namespace"?, "returnMetadata"?: "all"|"indexed"|"none" }
GET /api/vectors/:id # the collection and its vector count
POST /api/vectors/:id { "action": "delete", "ids": [...] } | { "action": "index-metadata", "propertyName", "indexType" }
DELETE /api/vectors/:id