Page through each collection with collection.get(), asking for embeddings, documents and metadata, 300 records per request. Write each collection to its own file and record its dimension and distance metric.
Pause writes before you start. Export works until December 22, 2026, when Chroma Cloud stops serving reads.
What do you need to export?
For every collection, four things per record and two per collection:
| Per record | Per collection |
|---|---|
id | Vector dimension |
embedding | Distance metric (l2, cosine or ip) |
document (the text) | |
metadata |
The metric matters as much as the vectors. Chroma defaults to l2 (squared Euclidean) if none was set, and loading l2 vectors into a cosine index silently changes your rankings. The embedding function is worth noting too, if your app queries with text.
How does the export work?
get() returns records in pages. Chroma Cloud caps each request at 300 records, so loop with limit and offset:
import chromadb, json, os
client = chromadb.CloudClient(
tenant=os.environ["CHROMA_TENANT"],
database=os.environ["CHROMA_DATABASE"],
api_key=os.environ["CHROMA_API_KEY"],
)
for col in client.list_collections():
with open(f"{col.name}.jsonl", "w") as out:
offset = 0
while True:
page = col.get(
include=["embeddings", "documents", "metadatas"],
limit=300,
offset=offset,
)
if not page["ids"]:
break
for i, id_ in enumerate(page["ids"]):
out.write(json.dumps({
"id": id_,
"document": page["documents"][i],
"embedding": list(map(float, page["embeddings"][i])),
"metadata": page["metadatas"][i],
}) + "\n")
offset += len(page["ids"])
For self-hosted Chroma, swap CloudClient for chromadb.HttpClient(host=..., port=...).
The TopK migration guide has the complete script. It also writes an import spec per collection with the dimension, metric and a typed field for each metadata key, so the import is one topk import command.
What can go wrong?
- Records written mid-export get skipped or duplicated. Pagination is by offset, so a write shifts every later page. Pause writes, or export again just before you switch.
- List metadata. Lists and nested values need a field type that supports them in the new database. The TopK script exports strings, numbers and booleans and skips the rest, so ask us if you filter on lists.
- Mixed types. A key that holds numbers in some records and strings in others has to become one type. Check any filters that use it.
- Sparse vectors. Chroma Cloud's hosted SPLADE vectors are not part of a dense export. Use a keyword index for BM25 instead, or re-encode them.
How do you know the export is complete?
Compare the line count of each file with collection.count(). Then check that search results match after import; see how to validate a migration.
Want it done for you?
Paste "Migrate my Chroma collections to TopK: docs.topk.io/migrate-chroma" into Claude Code, Cursor or Codex. The agent guide lists your collections, shows a plan, then exports, imports and checks each one, reading from Chroma only. Or book a migration call and a TopK engineer will help.