DS DevShelfHub Projects · AI tools
Cheatsheets / Chroma
Cheatsheet · AI frameworks

Chroma Cheatsheet: Collections, Queries and Filters Reference

By DevShelfHub

Persistent client, collections, embedding functions, filters, queries, server mode — the local-first vector DB as a one-page reference.

87 items 6 min Collections Filters Local

Start hereQuick start · 6 you’ll reach for daily

Persistent clientchromadb.PersistentClient("./d")
Collectionclient.get_or_create_collection("kb")
Upsert docscol.upsert(ids, documents, metadatas)
Querycol.query(query_texts, n_results=5)
Metadata filterwhere={"src": "docs"}
Connect to serverHttpClient(host, port=8000)

Target versions · paceVersions

Targets: chromadb ≥ 0.5 chromadb (js) ≥ 1.8 python ≥ 3.9

Chroma redesigned its API in 0.4 — the old Client(Settings(...)) pattern is gone, replaced by PersistentClient, HttpClient, and CloudClient. Telemetry is on by default — opt out with anonymized_telemetry=False. Index type is HNSW; only cosine / L2 / IP distances are supported. This sheet pins to 0.5+.

Install · serverSetup

bash
# Python client — covers both local and server modes
pip install "chromadb>=0.5"

# Docker — single-node server (HTTP API on :8000)
docker run -p 8000:8000 -v chroma:/data \
  -e IS_PERSISTENT=TRUE -e PERSIST_DIRECTORY=/data \
  chromadb/chroma:latest

# CLI — run the server from the package
chroma run --path ./chroma_data --host 0.0.0.0 --port 8000

# JS / TS client
npm install chromadb chromadb-default-embed

Where things liveCommon imports

import chromadbCore package — clients live on the top level.
from chromadb import PersistentClient, HttpClient, EphemeralClientThree client modes — local disk, remote server, in-memory.
from chromadb.config import SettingsTweak telemetry, allow_reset, auth.
from chromadb.utils import embedding_functionsBundled embedders (OpenAI, ST, Cohere, ...).
from chromadb.api.types import Document, EmbeddingFunctionTypes for custom embedders.
from chromadb.errors import IDAlreadyExistsError, NotFoundErrorCommon exceptions.

Local · server · cloudClients

chromadb.PersistentClient(path="./d")Local disk store. Preferred for dev / single-process apps.
chromadb.EphemeralClient()In-memory only. Tests + notebooks.
chromadb.HttpClient(host="h", port=8000, ssl=False)Talk to a Chroma server.
chromadb.AsyncHttpClient(host=...)Async variant of the HTTP client.
chromadb.CloudClient(api_key=..., tenant=..., database=...)Managed Chroma Cloud.
Client(Settings(anonymized_telemetry=False, allow_reset=True))Legacy — pre-0.4 pattern, still works for niche setups.
client.heartbeat()Server ping (ns since epoch).
client.reset()Wipe everything. Needs allow_reset=True.

Create · configure · inspectCollections

client.create_collection("kb")Create; errors if it exists.
client.get_collection("kb")Open existing; errors if missing.
client.get_or_create_collection("kb")Preferred idempotent variant.
client.list_collections()All collections on this client.
client.delete_collection("kb")Drop — destructive.
col.name · col.id · col.metadataIdentity + creation config.
col.count()Number of vectors.
col.modify(name="kb-v2", metadata={"hnsw:space": "cosine"})Rename / change distance + HNSW knobs.
metadata={"hnsw:space": "cosine"}Distance: cosine, l2, ip. cosine default for text embeddings.
metadata={"hnsw:construction_ef": 100, "hnsw:M": 16}Recall vs build-cost knobs.

Bundled · customEmbedding functions

DefaultEmbeddingFunction()Local all-MiniLM-L6-v2 (384-dim). No API key, no GPU. Decent starting point.
OpenAIEmbeddingFunction(api_key, model_name)OpenAI cloud. text-embedding-3-small is the modern default.
SentenceTransformerEmbeddingFunction(model_name)Any HF Sentence-Transformers model locally.
HuggingFaceEmbeddingServer(url)Hit a remote TEI / inference server.
CohereEmbeddingFunction / JinaEmbeddingFunction / VoyageAI...Other hosted vendors.
OllamaEmbeddingFunction(model_name, url)Local Ollama instance.
class MyEF(EmbeddingFunction): def __call__(self, input): ...Custom — must map list[str]list[list[float]].
col = get_or_create_collection("kb", embedding_function=ef)Once attached, Chroma embeds for you on add/query.
python
from chromadb.utils import embedding_functions

# Default — onnx all-MiniLM-L6-v2, ships with the package, no API key
default_ef = embedding_functions.DefaultEmbeddingFunction()

# OpenAI
openai_ef = embedding_functions.OpenAIEmbeddingFunction(
    api_key=os.environ["OPENAI_API_KEY"],
    model_name="text-embedding-3-small",
)

# Sentence-Transformers — runs locally on CPU/GPU
st_ef = embedding_functions.SentenceTransformerEmbeddingFunction(
    model_name="BAAI/bge-small-en-v1.5"
)

# Custom — any callable that maps list[str] -> list[list[float]]
class MyEF(embedding_functions.EmbeddingFunction):
    def __call__(self, input):
        return [[0.0] * 384 for _ in input]

# Attach to a collection — once set, you don't pass embeddings yourself
col = client.get_or_create_collection("docs", embedding_function=openai_ef)
The embedding function is not stored with the collection — you must re-attach the same function (or pass embeddings explicitly) every time you re-open the client. Mismatched dimensions throw on insert.

add · upsert · update · deleteAdding data

col.add(ids, documents, metadatas)Insert; errors on duplicate id.
col.upsert(ids, documents, metadatas)Preferred — replaces if id exists.
col.update(ids, documents=..., metadatas=...)Partial update; errors on missing id.
col.add(..., embeddings=[[...], [...]])Pass vectors directly — skips the embedding function.
col.delete(ids=["a","b"])Delete by id.
col.delete(where={"source": "stale"})Delete by metadata filter.
metadatas=[{"k": str|int|float|bool}, ...]Scalar metadata only — no nested objects or lists.
ids must be unique & strUUIDs or your own hash. Used for upsert + delete.
Batch ≤ ~5000 per callChunk huge corpora — the HTTP server has a default body cap.

Similarity · getQuerying

col.query(query_texts=["q"], n_results=5)k-NN by embedding text on the fly.
col.query(query_embeddings=[[...]], n_results=5)k-NN by raw vector.
include=["documents","metadatas","distances","embeddings"]Pick what to return. Distances are the picked space’s value.
where={"k": "v"}Metadata filter (see operators below).
where_document={"$contains": "vector"}Substring filter on the doc text.
col.get(ids=["a","b"])Random access by id (no similarity).
col.get(where={"source": "blog"}, limit=100, offset=0)Paginate by filter.
col.peek(limit=10)First N rows — quick sanity check.
res["distances"][0]Distances for the first query. Lower = closer (cosine: 0..2).

Mongo-style operatorsWhere filters

{"k": "v"}Equality shorthand.
{"k": {"$eq": v}} / {"$ne": v}Equality / inequality.
{"k": {"$gt": n, "$lte": m}}Numeric range.
{"k": {"$in": [a, b]}} / {"$nin": [...]}Set membership.
{"$and": [c1, c2]} / {"$or": [c1, c2]}Boolean composition.
where_document={"$contains": "text"}Substring on document body.
where_document={"$not_contains": "text"}Negation.
python
# Metadata filter — "where" mirrors Mongo-style operators
res = col.query(
    query_texts=["how does retrieval work?"],
    n_results=5,
    where={
        "$and": [
            {"source": {"$eq": "docs"}},
            {"year":   {"$gte": 2024}},
            {"tag":    {"$in": ["rag", "embeddings"]}}
        ]
    },
    where_document={"$contains": "vector"},   # full-text filter on the chunk
)

# Operators reference:
#   $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin   — value comparisons
#   $and, $or                                    — boolean composition
#   $contains, $not_contains                     — document text (where_document)

# Update / delete by filter — no need to pass ids
col.update(ids=["a", "b"], metadatas=[{"reviewed": True}, {"reviewed": True}])
col.delete(where={"year": {"$lt": 2020}})

HTTP API · auth · multi-tenancyServer mode

chroma run --path ./d --host 0.0.0.0 --port 8000Start the bundled server.
HttpClient(host, port, headers={"Authorization": "Bearer ..."})Send auth on every request.
CHROMA_SERVER_AUTHN_PROVIDER=...Pluggable token / basic auth (env).
CHROMA_SERVER_AUTHZ_PROVIDER=...Role-based access control.
client.tenant / client.databaseMulti-tenancy — isolated namespaces.
client.create_tenant("acme") / client.create_database("prod", tenant="acme")Provision tenants / dbs.
GET /api/v2/heartbeatREST health check.
POST /api/v2/.../collections/{name}/queryREST query endpoint — payload mirrors the SDK.
ALLOWED_ORIGINS envCORS allowlist for the browser SDK.

LangChain · LlamaIndexFramework integrations

from langchain_chroma import ChromaModern LangChain wrapper package.
Chroma.from_documents(docs, embedding, persist_directory="./d")Build a store from LangChain Documents.
store.as_retriever(search_kwargs={"k": 4})Drop straight into a LangChain chain.
from langchain_community.vectorstores import ChromaLegacy import — superseded by langchain-chroma.
from llama_index.vector_stores.chroma import ChromaVectorStoreLlamaIndex adapter.
VectorStoreIndex.from_vector_store(ChromaVectorStore(col))Wire a LlamaIndex query engine on top.

Index · query in 25 linesEnd-to-end · Minimal RAG store

Persistent client + Sentence-Transformers embedder + a filtered query. Swap in your own corpus — the shape stays.

python
# Minimal RAG store — persistent client, one collection, query with a filter
import chromadb
from chromadb.utils import embedding_functions

client = chromadb.PersistentClient(path="./chroma_data")

ef = embedding_functions.SentenceTransformerEmbeddingFunction(
    model_name="BAAI/bge-small-en-v1.5"
)

col = client.get_or_create_collection(
    name="kb",
    embedding_function=ef,
    metadata={"hnsw:space": "cosine"},   # cosine ∈ [0,2], lower is closer
)

# Index — Chroma embeds documents for you because ef is set
col.upsert(
    ids        = ["d1", "d2", "d3"],
    documents  = ["Vectors are dense embeddings.",
                  "RAG joins retrieval with generation.",
                  "Chroma is local-first."],
    metadatas  = [{"source": "blog"},
                  {"source": "docs"},
                  {"source": "docs"}],
)

# Retrieve top-2 docs from "docs" only
hits = col.query(
    query_texts=["what is RAG?"],
    n_results=2,
    where={"source": "docs"},
)
for d, m, dist in zip(hits["documents"][0], hits["metadatas"][0], hits["distances"][0]):
    print(f"{dist:.3f}  [{m['source']}]  {d}")

Best practiceGood to know

Default distance is L2, not cosine. For text embeddings (which are usually normalized), set {"hnsw:space": "cosine"} at create time. Distances become directly interpretable: 0 = identical, 1 = orthogonal, 2 = opposite.
Use upsert instead of add by default. Most pipelines re-index the same content multiple times during development. add errors on duplicates; upsert is idempotent and just as fast.
Re-attach the same embedding function every time. Chroma stores vectors but not the function that produced them. Open a collection with the wrong embedder and you’ll silently get nonsense rankings — or a dimension-mismatch error on first insert.

Common trapsWatch out for

Metadata values can’t be lists or nested objects. Only str | int | float | bool | None. Stash JSON-encoded payloads in a string column or model lists as separate boolean tags (tag_rag: True).
Telemetry is on by default. The default client pings posthog.com. Pass Settings(anonymized_telemetry=False) in regulated environments — otherwise air-gapped deploys silently fail health checks.
PersistentClient isn’t multi-process safe. Two Python processes opening the same path will corrupt the SQLite store. Use the HTTP server with HttpClient the moment you have more than one writer.

Go deeperSee also

Chroma FAQ

What is Chroma used for?

Chroma is an open-source vector database designed to store and query embeddings alongside their metadata. It is commonly used in LLM applications to implement semantic search, RAG (retrieval-augmented generation) pipelines, and memory stores.

What is the difference between PersistentClient and EphemeralClient in Chroma?

PersistentClient stores data on disk and survives restarts, making it the preferred choice for development and single-process apps. EphemeralClient keeps everything in memory and is discarded when the process exits — ideal for tests and notebooks.

How do metadata filters work in Chroma queries?

Chroma supports a where dict of metadata conditions, e.g. where={"source": "docs"}. You can combine conditions with $and/$or operators and use comparison operators like $gte and $lt. Filters apply before the vector similarity search, narrowing the candidate set.

Can Chroma run as a server?

Yes. Run chroma run --path ./db to start the HTTP server, then connect with chromadb.HttpClient(host="localhost", port=8000). An async variant, AsyncHttpClient, is available for non-blocking workflows.

How do I use custom embedding functions in Chroma?

Implement a class with a __call__(input: Documents) -> Embeddings method and pass it as embedding_function when creating or getting a collection. Chroma ships bundled functions for OpenAI, Sentence Transformers, Cohere, and others in chromadb.utils.embedding_functions.

Is Chroma free and open source?

Yes, Chroma is Apache-2.0 licensed. The local PersistentClient and self-hosted server are free. Chroma Cloud is a managed hosted option with its own pricing.