chromadb
Chroma is an open source vector database for embedding-based retrieval, pitched as search infrastructure for AI. In Python it runs three ways: in-process for prototyping, as a local server via chroma run, or hosted on Chroma Cloud. You add documents to collections and Chroma handles tokenization, embedding, and indexing by default; you query by text or your own vectors, with metadata and document-content filters. The core API is four functions, which is most of its appeal.
The fastest path from zero to working vector search in Python, and fine to ship for modest corpora. Treat it as a component you may outgrow: the install is heavy, releases move weekly, and the scaling story points you toward their cloud.
Use it if
- You are building a RAG prototype and want add and query working in ten lines, with embedding handled for you
- You want local-first persistence in a single directory (PersistentClient) with no server process to babysit
- Your corpus is small to medium and vector similarity plus metadata filtering covers your retrieval needs
- You care about install weight: chromadb drags in onnxruntime, grpcio, opentelemetry, uvicorn, and typer even for embedded use; the thin chromadb-client package avoids that only in client-server mode
- You need self-hosted horizontal scaling, replication, or multi-tenant isolation; qdrant or milvus are engineered around those problems
- Your data already lives in Postgres and vectors are one column of a larger schema; pgvector keeps retrieval next to your relational queries and transactions
- You want a slow-moving dependency; the README states new tagged releases ship on Mondays with hotfixes at any time, so pins go stale fast
Setup reality
pip install chromadb brings a heavy dependency tree (pydantic, onnxruntime, grpcio, opentelemetry, uvicorn, typer), so installs are slow and version conflicts with an already-pinned stack are common. The first add() with the default embedding function downloads an ONNX MiniLM model. Local persistence is one line with PersistentClient(path=...). Client-server mode means running chroma run --path plus HttpClient, and only there does the lightweight chromadb-client package save you the full install.
Patterns
Spin up an in-memory client for prototypingephemeral-client
import chromadb
client = chromadb.Client()
collection = client.create_collection("docs")Everything vanishes when the process exits; switch to PersistentClient before you care about the data.
Persist collections to a local directorypersistent-client
import chromadb
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection("docs")get_or_create_collection is idempotent across restarts; create_collection raises if the name already exists.
Add documents with automatic embeddingadd-documents
collection.add(
documents=["This is document1", "This is document2"],
metadatas=[{"source": "notion"}, {"source": "google-docs"}],
ids=["doc1", "doc2"],
)ids are required and must be unique per collection; with no embeddings passed, the default embedding function downloads an ONNX model on first use.
Add precomputed embeddingsbring-own-embeddings
collection.add(
ids=["doc1", "doc2"],
embeddings=[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]],
documents=["text one", "text two"],
)All vectors in a collection must have the same dimensionality; mixing embedding models silently ruins similarity results.
Query the most similar documentsquery-similar
results = collection.query(
query_texts=["This is a query document"],
n_results=2,
)
top_docs = results["documents"][0]
top_ids = results["ids"][0]Results are lists of lists keyed by field, one inner list per query text, so index [0] even for a single query.
Filter a query by metadatametadata-filter
results = collection.query(
query_texts=["refund policy"],
n_results=5,
where={"$and": [
{"source": "notion"},
{"year": {"$gte": 2024}},
]},
)Supported operators include $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin combined with $and / $or.
Filter by document contentdocument-filter
results = collection.query(
query_texts=["how do refunds work"],
n_results=5,
where_document={"$contains": "refund"},
)where_document matches against the stored document text and can be combined with a metadata where filter.
Fetch records by id instead of similarityget-by-id
records = collection.get(
ids=["doc1"],
include=["documents", "metadatas"],
)get is exact lookup and query is similarity search; embeddings are excluded from results unless you ask for them in include.
Update or delete existing recordsupdate-delete
collection.update(
ids=["doc1"],
metadatas=[{"reviewed": True}],
)
collection.delete(ids=["doc2"])
collection.delete(where={"source": "google-docs"})Passing new documents to update re-embeds them; delete accepts ids, a where filter, or both.
Run Chroma as a server and connect over HTTPclient-server
# terminal:
# chroma run --path ./chroma_db
import chromadb
client = chromadb.HttpClient(host="localhost", port=8000)
collection = client.get_or_create_collection("docs")In this mode you can install the thin chromadb-client package instead of full chromadb and skip the heavy dependencies.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| qdrant-client | PyPI | You want a production-grade self-hosted vector database with filtering and scaling as core features |
| faiss-cpu | PyPI | You need raw nearest-neighbor speed as a library and will manage storage and metadata yourself |
| pgvector | PyPI | Your vectors belong next to relational data you already keep in Postgres |
| lancedb | PyPI | You want an embedded, file-based vector store built on columnar storage |