ChromaDB Query Cheat Sheet

Chroma is an open-source vector database you drive from a Python or JavaScript client. This sheet covers the clients, collections, add and upsert, get vs query, where and where_document filters, embedding functions and the chroma CLI, with one support_docs collection used in every example.

Updated Oct 12, 2026Tested against chromadb 1.5.9 (Python), chromadb 3.5.0 (JS)

Clients

Install with . Every example on this page uses a client named and a collection named .

TaskCode
Local database on disk
In-memory database for tests
Connect to a Chroma server
Async client for a server
Chroma Cloud
Turn off telemetry in code
Turn off telemetry with an environment variable
Use another tenant or database
Switch database on an open client
Check the server is up
Client package version
Largest batch for add, upsert and delete
Wipe every collection (needs )Destructive
JavaScript client (server only, no embedded mode)

comes from . The JavaScript client () uses camelCase for the same calls: , , , and it needs installed for the default embedding model.

Example: open a local database with telemetry off and confirm it responds.

python
import chromadb
from chromadb.config import Settings

client = chromadb.PersistentClient(
    path="./chroma",
    settings=Settings(anonymized_telemetry=False),
)
print(client.heartbeat(), chromadb.__version__)

Collections

A collection is Chroma's table: records with an ID, a document, metadata and an embedding.

TaskCode
Create (SQL )
Create, or open if it exists
Open an existing collection
Use cosine distance (set at creation)
List collection names
Page through collections
Number of collections
Number of records (SQL )
First few records
Show distance space and HNSW settings
Rename
Replace collection metadata
Raise search accuracy at query time
Delete (SQL )Destructive

The distance space is unless you pass or , and it cannot change later. Names need 3 to 512 characters from . raises for a missing name, and raises an "already exists" error for a taken one.

Example: create the collection used on the rest of this page, with cosine distance.

python
docs = client.get_or_create_collection(
    name="support_docs",
    configuration={"hnsw": {"space": "cosine"}},
    metadata={"team": "support"},
)
print(docs.configuration["hnsw"])

In 1bench: the Chroma GUI has a schema view per collection that shows the tenant and database, the embedding function, the distance space and every HNSW setting, so you do not have to print to check them.

Add and Upsert

TaskCode
Add records, embedded by the collection (SQL )
Add with your own vectors
Insert or replace (SQL upsert, )
Metadata value types, , , , or a list of one of these
List metadata for tags

skips an ID that already exists and raises no error, so the old record stays. Use when a record may already be there.

Example: load four support articles with source, category and year metadata.

python
docs.add(
    ids=["doc1", "doc2", "doc3", "doc4"],
    documents=[
        "How to reset your password from the login page",
        "Refunds are issued within 5 business days",
        "Enable two-factor authentication in account settings",
        "Export your invoices as PDF from the billing page",
    ],
    metadatas=[
        {"source": "help_center", "category": "account", "year": 2025},
        {"source": "faq", "category": "billing", "year": 2024},
        {"source": "help_center", "category": "security", "year": 2026},
        {"source": "faq", "category": "billing", "year": 2026, "tags": ["pdf", "invoice"]},
    ],
)
print(docs.count())

A call with more records than (5461 in our test) fails with "Batch size ... is greater than max batch size". Split big loads:

python
batch = client.get_max_batch_size()
for i in range(0, len(ids), batch):
    docs.upsert(
        ids=ids[i:i + batch],
        documents=texts[i:i + batch],
        metadatas=metas[i:i + batch],
    )

Get vs Query

reads records by ID or filter with no ranking. runs a similarity search and returns the nearest records first. Both take , and .

TaskCode
Fetch by ID (SQL )
Get all documents
Only the IDs
Page with and
Choose returned fields
Similarity search by text
Similarity search by vector
Several searches in one call
Search only within some IDs
Return distances (query only)

Defaults: returns metadatas and documents, adds distances. with raises an error. returns one list per query text, so read , not . Asking for more than there are records returns them all.

Example: the two closest articles to a user question, with their distances.

python
r = docs.query(
    query_texts=["I forgot my password"],
    n_results=2,
    include=["documents", "metadatas", "distances"],
)
for doc_id, text, dist in zip(r["ids"][0], r["documents"][0], r["distances"][0]):
    print(doc_id, round(dist, 3), text)

Metadata Filters

filters on metadata in both and . Each dict holds exactly one field or one operator.

TaskFilter
Equal (SQL )
Equal, explicit form
Not equal (SQL )
Greater than
Greater than or equal
Less than
Less than or equal
Any of several values (SQL )
None of several values (SQL )
Both conditions (SQL )
Either condition (SQL )
List metadata contains a value
List metadata lacks a value

Two keys in one dict, such as , raise "Expected where to have exactly one operator". Wrap them in . Comparison operators check types too: fails because the value is a string.

Example: FAQ billing articles, nearest to a refund question.

python
r = docs.query(
    query_texts=["how long does a refund take"],
    n_results=3,
    where={"$and": [{"source": "faq"}, {"category": "billing"}]},
)
print(r["ids"][0])

In 1bench: the Chroma GUI has a query builder with Query and Get modes, an IDs box, a where JSON field and a where_document field with a negate switch, and it shows the request body it will send.

Document Filters

filters on the document text. Use it alone with , or next to in a .

TaskFilter
Text contains (SQL )
Text does not contain (SQL )
Regular expression
Case-insensitive regex
Does not match a regex
Either phrase (SQL )
Both phrases (SQL )

is case-sensitive: does not match "password". Use with when case should not matter.

Example: billing articles that mention PDF, ranked against an invoice question.

python
r = docs.query(
    query_texts=["download my invoice"],
    n_results=2,
    where={"category": "billing"},
    where_document={"$contains": "PDF"},
    include=["documents", "distances"],
)
print(r["documents"][0], r["distances"][0])

Update and Delete

TaskCode
Change metadata fields (merges with existing keys)
Remove one metadata key
Change document text (re-embeds it)
Delete by ID (SQL )Destructive
Delete by metadata filterDestructive
Delete by document textDestructive

on an ID that does not exist raises no error and changes nothing. with no arguments raises an error instead of emptying the collection.

Example: retire every pre-2025 FAQ article and bump the year on one that was reviewed.

python
docs.delete(where={"$and": [{"source": "faq"}, {"year": {"$lt": 2025}}]})
docs.update(ids=["doc1"], metadatas=[{"year": 2026}])
print(docs.count(), docs.get(ids=["doc1"])["metadatas"])

In 1bench: the Chroma GUI exports a whole collection to a JSON file, with or without embeddings, so you can keep a copy before a bulk delete.

Embedding Functions

A collection embeds and with its embedding function. Pass one at creation; Chroma saves the choice in the collection configuration.

TaskCode
Import the built-in functions
Default model (all-MiniLM-L6-v2, ONNX, 384 dimensions)
OpenAI ()
sentence-transformers ()
Ollama (, Ollama running)
Attach to a new collection
Embed text yourself
See which one a collection uses

The default model downloads about 80 MB to on first use. The OpenAI function reads its key from unless you set , and only the variable name is saved, never the key. restores a built-in function from the saved configuration, and passing a different one raises "Embedding function conflict".

Example: a collection embedded with OpenAI, reopened later without passing the function again.

python
from chromadb.utils import embedding_functions

openai_ef = embedding_functions.OpenAIEmbeddingFunction(
    api_key_env_var="OPENAI_API_KEY",
    model_name="text-embedding-3-small",
)
kb = client.get_or_create_collection("support_docs_openai", embedding_function=openai_ef)
kb.add(ids=["doc1"], documents=["How to reset your password from the login page"])

kb = client.get_collection("support_docs_openai")
print(kb.query(query_texts=["lost password"], n_results=1)["ids"])
TaskCode
Keyword or phrase filter, any deployment
Pattern filter, any deployment
Vector search narrowed by keywords
BM25 sparse index (Chroma Cloud only) with
Ranked search expression (Chroma Cloud only)
Fuse dense and sparse rankings (Chroma Cloud only) inside the Search API

In chromadb 1.5.9, a local or self-hosted server rejects sparse indexes and . The Search API below runs on Chroma Cloud:

python
from chromadb import Search, K, Knn

s = (
    Search()
    .where(K("category") == "billing")
    .rank(Knn(query="get my money back"))
    .limit(2)
    .select(K.DOCUMENT, K.SCORE, "year")
)
results = docs.search(s)

Example: a local stand-in for hybrid search, ranking by meaning and keeping only articles that mention a password.

python
r = docs.query(
    query_texts=["I cannot log in"],
    n_results=3,
    where_document={"$regex": "(?i)password"},
)
print(r["ids"][0], r["distances"][0])

CLI, Server and REST API

The command ships with the Python package.

TaskCommand
Start a local server (default port 8000)
Pick the port
Listen on all interfaces
Run in Docker with a data volume
Shrink the SQLite file of a local database
Browse a collection in the terminal
Copy local collections to Chroma Cloud
Health check
List collections
Interactive API docsopen

REST calls under take the collection UUID, not its name, for , , , , , and . The server does not embed text, so a REST needs .

Example: start a server on port 8000 and fetch billing articles over REST.

bash
chroma run --path ./chroma-data --port 8000

B=http://localhost:8000/api/v2/tenants/default_tenant/databases/default_database
ID=$(curl -s "$B/collections/support_docs" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")

curl -s -X POST "$B/collections/$ID/get" \
  -H 'Content-Type: application/json' \
  -d '{"where": {"category": "billing"}, "include": ["documents", "metadatas"]}'

Gotchas

  • with an existing ID keeps the old record and raises no error, and on a missing ID does nothing silently. Use when you are not sure the record exists.
  • A dict with two fields raises "Expected where to have exactly one operator". Write , not .
  • on an existing collection ignores the you pass, so a collection first created with stays . The space cannot be changed later; create a new collection and copy the records.
  • , and fail above records per call. Split large loads into batches.
  • is case-sensitive. Use with for a case-insensitive match.
  • Embeddings come back as a NumPy array, so raises "truth value of an array ... is ambiguous". Check or instead.

Work with Chroma without memorizing it

1bench is a desktop GUI for Chroma. Connect, browse your data and run queries in one window, with the query DSL still there when you want it.

Open Chroma in 1bench

Chroma FAQ

Where does ChromaDB store data?
PersistentClient writes to the folder you pass as path, ./chroma by default. Inside it, chroma.sqlite3 holds collections, metadata and documents, and one folder per collection (named by its UUID) holds the vector index files. chroma run uses --path the same way, and the Docker image stores data in /data, so mount a volume there. EphemeralClient keeps everything in memory and loses it when the process exits.
How do I check which ChromaDB version I have?
Run python -c "import chromadb; print(chromadb.__version__)" or pip show chromadb. client.get_version() gives the same answer on a PersistentClient. Over HTTP it returns what the server reports: in our test a server started from the 1.5.9 package answered 1.0.0 on /api/v2/version, so check the package version when you need the release number.
Does ChromaDB use cosine similarity?
Not by default. A new collection uses squared L2 distance. Pass configuration={"hnsw": {"space": "cosine"}} when you create the collection to get cosine, or "ip" for inner product. The space is fixed after creation: modify() rejects it, and get_or_create_collection on an existing name keeps the old space without an error. Query results are distances, so for cosine a smaller number means closer, and similarity is 1 minus the distance.
Does ChromaDB support hybrid search and BM25?
On Chroma Cloud, yes: the Search API ranks with Knn and fuses dense and sparse (BM25 or SPLADE) results with Rrf. A local or self-hosted Chroma 1.5.9 rejects both: a sparse index fails with "Sparse vector indexing is not enabled in local", and collection.search() raises a not implemented error. Locally, the closest thing is a vector query narrowed by where_document $contains or $regex.