GUIDE Getting Started

Getting started: upsert your first vectors

ManyVector Team 2026-07-01 8 min read

ManyVector stores all vector data in your own object store — your S3, GCS, R2, or MinIO bucket. This guide walks you through the complete setup: creating a namespace, connecting your bucket, and upserting your first vectors.

Prerequisites

  • A ManyVector account (sign up at manyvector.com/demo)
  • An AWS S3, GCS, Cloudflare R2, or MinIO bucket you control
  • Python 3.9+ and pip

Step 1: Install the SDK

pip install manyvector

Verify the installation:

python -c "import manyvector; print(manyvector.__version__)"

Step 2: Connect your object store

ManyVector never stores your data — it reads and writes only to your bucket. You provide credentials scoped to that bucket.

For S3, create an IAM policy with minimal permissions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket"],
      "Resource": ["arn:aws:s3:::my-vector-bucket", "arn:aws:s3:::my-vector-bucket/*"]
    }
  ]
}

Step 3: Create a namespace

A namespace is the top-level collection. Each namespace maps to a prefix in your bucket.

from manyvector import ManyVector

client = ManyVector(
    api_key="mv-...",
    backend={
        "provider": "s3",
        "bucket": "my-vector-bucket",
        "region": "us-east-1",
        "access_key_id": "AKIA...",
        "secret_access_key": "...",
    }
)

# Create a namespace for your documents
ns = client.create_namespace(
    name="my-docs",
    dimensions=1536,      # must match your embedding model
    metric="cosine",
)
print(f"Created namespace: {ns.name}")

The dimensions parameter must match the output dimension of your embedding model. For text-embedding-3-small (OpenAI) this is 1536. For all-MiniLM-L6-v2 (sentence-transformers) this is 384.

Step 4: Generate embeddings

You can use any embedding model. Here’s an example using the OpenAI SDK:

import openai

openai_client = openai.OpenAI(api_key="sk-...")

def embed(texts: list[str]) -> list[list[float]]:
    response = openai_client.embeddings.create(
        input=texts,
        model="text-embedding-3-small",
    )
    return [item.embedding for item in response.data]

Step 5: Upsert vectors

Each vector record has an id, a vector, and optional attributes (stored alongside the vector, searchable via BM25 or filterable):

documents = [
    {"id": "doc-1", "text": "ManyVector stores vectors in object storage."},
    {"id": "doc-2", "text": "HNSW enables approximate nearest-neighbour search."},
    {"id": "doc-3", "text": "BM25 provides full-text search ranking."},
]

# Embed all documents in a batch
embeddings = embed([d["text"] for d in documents])

# Upsert with attributes
ns = client.namespace("my-docs")
ns.upsert([
    {
        "id": doc["id"],
        "vector": emb,
        "text": doc["text"],
        "source": "guide",
    }
    for doc, emb in zip(documents, embeddings)
])
print(f"Upserted {len(documents)} vectors")

Step 6: Run your first query

query = "How does vector search work?"
query_embedding = embed([query])[0]

results = ns.query(
    vector=query_embedding,
    top_k=3,
)

for r in results:
    print(f"  {r.id}: score={r.score:.4f} | {r.attributes['text'][:60]}...")

What just happened?

When you upserted, ManyVector wrote:

  1. The raw vector data as objects in your bucket
  2. An HNSW graph index (also stored in your bucket)
  3. An inverted index for BM25 full-text search

When you queried, ManyVector:

  1. Loaded the HNSW graph from your bucket (or used the cached version)
  2. Traversed the graph to find approximate nearest neighbours
  3. Returned results ranked by cosine similarity

Next steps

Related guides

Start building with ManyVector today.