← Code Recipes
Embeddings & Search

Chroma Free code examples in Python and JavaScript

The simplest way to start: an embedded vector store, no server.

C# Python Java JavaScript

WHAT THIS PAGE COVERS

A working RAG loop in about forty lines.

Model all-MiniLM-L6-v2
Package chromadb
Checked

Model ids change. If a sample returns a 404 on the model, that is the first thing to check against the official docs.

Install

One package and nothing else. The default embedding model is downloaded on first use and then runs locally — no account, no key, no network after that.

pip install chromadb

# Nothing to configure. The bundled all-MiniLM-L6-v2 model is
# fetched once on the first call and cached.
A working retrieval loop

This is the whole thing. No vectors are computed by you, no schema is declared, and the query takes text because the collection owns its embedding function.

import chromadb

client = chromadb.Client()          # in-memory; see the next step

collection = client.get_or_create_collection(name="documents")

collection.upsert(
    documents=[
        "Error code E-4471 means the pump stalled.",
        "Quarterly maintenance is due every thirteen weeks.",
    ],
    ids=["doc-1", "doc-2"],
)

results = collection.query(
    query_texts=["the pump has stopped working"],
    n_results=2,
)

print(results["documents"][0])
Which client you actually want

chromadb.Client() IS IN-MEMORY. Everything disappears when the process exits, which is fine for a notebook and a nasty surprise in a service that re-embeds the corpus on every deploy.

import chromadb

# Notebook / test: gone when the process ends.
ephemeral = chromadb.Client()

# A real local app: survives a restart.
persistent = chromadb.PersistentClient(path="./chroma")

# Several processes sharing one store — run `chroma run --path ./chroma`
# first, then point at it.
remote = chromadb.HttpClient(host="localhost", port=8000)
Metadata, and filtering on it

where filters on metadata, where_document on the text itself. Both run inside the search, so n_results=3 with a filter still returns three if three match.

collection.upsert(
    documents=["Error code E-4471 means the pump stalled."],
    ids=["doc-1"],
    metadatas=[{"source": "handbook.pdf", "page": 4}],
)

results = collection.query(
    query_texts=["the pump has stopped working"],
    n_results=3,
    where={"source": "handbook.pdf"},          # metadata
    where_document={"$contains": "E-4471"},    # the text itself
)

for document, distance, metadata in zip(
    results["documents"][0],
    results["distances"][0],
    results["metadatas"][0],
):
    print(round(distance, 4), metadata["page"], document)
One collection, one model — for good

The bundled all-MiniLM-L6-v2 is 384 dimensions and enough to prototype with. Swapping it is one argument, but it must be a NEW COLLECTION: 384-wide vectors and a 768-wide query are not comparable, and Chroma rejects the mismatch rather than quietly mis-ranking.

from chromadb.utils import embedding_functions

stronger = embedding_functions.SentenceTransformerEmbeddingFunction(
    model_name="all-mpnet-base-v2",      # 768 dimensions, still local
)

# A NEW collection, not a re-pointed one. The embedding function is
# recorded when the collection is created and cannot be changed.
upgraded = client.get_or_create_collection(
    name="documents-v2",
    embedding_function=stronger,
)

# Re-add everything: the old vectors are the wrong width and there
# is no migration that avoids re-embedding.
upgraded.upsert(
    documents=["Error code E-4471 means the pump stalled."],
    ids=["doc-1"],
)
Install

THE JS CLIENT NEEDS A RUNNING SERVER. Unlike Python there is no embedded mode — `chroma run` is not optional, and forgetting it produces a connection error rather than an in-process store.

npm install chromadb @chroma-core/default-embed

# Required — there is no embedded mode in Node.
pip install chromadb
chroma run --path ./chroma

# @chroma-core/default-embed carries all-MiniLM-L6-v2, the same
# bundled model the Python client uses. No key, no account.
A working retrieval loop

Everything is async, including getOrCreateCollection. The default embedding function comes from @chroma-core/default-embed, which is why it is a separate install.

import { ChromaClient } from "chromadb";

const client = new ChromaClient();     // defaults to localhost:8000

const collection = await client.getOrCreateCollection({
  name: "documents",
});

await collection.upsert({
  documents: [
    "Error code E-4471 means the pump stalled.",
    "Quarterly maintenance is due every thirteen weeks.",
  ],
  ids: ["doc-1", "doc-2"],
});

const results = await collection.query({
  queryTexts: ["the pump has stopped working"],
  nResults: 2,
});

console.log(results.documents[0]);
Metadata, and filtering on it

Camel case in JavaScript, snake case in the filter operators — queryTexts and nResults, but $contains and the raw metadata keys. That split catches everyone once.

await collection.upsert({
  documents: ["Error code E-4471 means the pump stalled."],
  ids: ["doc-1"],
  metadatas: [{ source: "handbook.pdf", page: 4 }],
});

const results = await collection.query({
  queryTexts: ["the pump has stopped working"],
  nResults: 3,
  where: { source: "handbook.pdf" },          // metadata
  whereDocument: { $contains: "E-4471" },     // the text itself
});

results.documents[0].forEach((document, index) => {
  console.log(
    results.distances[0][index].toFixed(4),
    results.metadatas[0][index].page,
    document,
  );
});
Knowing when to move on

Chroma is for finding out whether retrieval helps. Once it does, these are the signals that you have outgrown it — and none of them are reasons not to have started here.

const stats = {
  count: await collection.count(),
};

// Rough thresholds, not rules:
//  * past ~1M vectors, a dedicated database indexes and filters better
//  * if several services need the same store, you want a real server
//    with auth rather than `chroma run` on a box
//  * if you need hybrid search, Chroma has no sparse vectors — that
//    is Qdrant or Weaviate territory
if (stats.count > 1_000_000) {
  console.warn(
    `${stats.count} vectors — worth pricing a managed database now.`,
  );
}

// The upside: the retrieval loop above is the same four calls
// everywhere, so the migration is the client, not the design.

QUESTIONS

Which languages are the Chroma examples in?

Python and JavaScript. Every sample runs as pasted and reads its credentials from an environment variable — no key is ever written into the code.

Which package do I install for Chroma?

The samples use chromadb. The install step on each language tab gives the exact command.

Which Chroma model do these examples use?

all-MiniLM-L6-v2, checked against the official documentation on 26 September 2026. Model ids change often; if a call returns a model-not-found error, check the current list in the official docs.

Where is the official Chroma documentation?

https://docs.trychroma.com/ — it is linked at the bottom of this page, and it is the authority whenever it disagrees with a sample here.

ALSO IN EMBEDDINGS & SEARCH

The authority

Official Chroma documentation

Model names, pricing and rate limits change faster than any page here can. When this page and the official docs disagree, the official docs are right.

Read the docs ↗

https://docs.trychroma.com/

New drops, every week

Free tools & Claude templates, landing regularly.

We keep shipping new free tools, code recipes and Claude Code templates. Follow @freeyantra on Instagram or join the newsletter so you never miss one.

Follow @freeyantra

or get them by email

Double opt-in. Unsubscribe any time. Privacy