Your collection is yours. Not just to query — to keep, to move, to walk away with.
One SDK call writes the whole collection to a directory of parquet files you can read with duckdb, pandas, or pyarrow. No row caps, no rate caps, no size caps. If you decide to leave, the door's already unlocked.
Setup
The examples below run end-to-end against a small collection built from the bundled sample corpus. If you already have a collection, skip this and use its name in place of my_collection.
from veep import VP, samples
df = samples.dataframe() # 5,000 films: id, title, year, genre, plot, vector
vp = VP.from_creds()
vp.collections.create("my_collection", tier="hot")
vp.vectors.upsert("my_collection", dataframe=df)
The call
from veep import VP
vp = VP.from_creds()
result = vp.collections.export("my_collection", "out/")
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_vectorpanda import VectorPandaStore
store = VectorPandaStore(
collection_name="my_collection",
embedding=HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2"),
)
result = store.export("out/")
That's it. The call blocks until every part is on disk. result.parts is the count, result.total_bytes is the size on disk, result.path is the directory you can point your reader at.
If you'd rather kick it off and walk away:
job = vp.collections.export("my_collection", "out/", wait=False)
# come back later, check the dashboard, or pass send_email=True
job = store.export("out/", wait=False)
# come back later, check the dashboard, or pass send_email=True
Reading it back
The output is a directory of part-NNNN.parquet files, plus a _manifest.json index and an _EXPORT_README.md summary. The sidecars are underscore-prefixed so parquet readers skip them — point any reader at the directory and it reads cleanly as one logical table:
import duckdb
df = duckdb.read_parquet("out/*.parquet").df()
import pandas as pd
df = pd.read_parquet("out/")
import pyarrow.parquet as pq
table = pq.read_table("out/")
Pick the one that's already in your stack. They all work.
What's inside
Each row carries three things:
id— the original string you upserted, exactly as you sent it. If your collection has formats that don't carry an id natively (barenpy,bvecs,fvecs,ivecs), you get the synthesized{filename}:{row_index}key that Vector Panda assigned at ingest. Drop the column if you don't want it.vector—list<float32>at your collection's original dimension. If we padded internally for SIMD alignment, the padding is stripped before export. You get your shape back.- metadata columns — every metadata field you wrote, typed the way you wrote them. Strings stay strings. Ints stay ints. Arrays stay arrays.
The export carries your data. It doesn't carry the index itself — the HNSW graph or PQ codebooks or Vamana edges we built to make your queries fast. Those are tied to specific strategy choices and don't port across vector databases cleanly.
What we do ship alongside the parquet parts is an _EXPORT_README.md. It records your collection's shape (dim, metric, vector count, the strategy that was active) and includes a short tour of every indexing strategy the platform tests — one sentence each, universal vector-search vocabulary. So wherever you take the data next, you're operating with the same names.
Snapshot semantics
The export reflects the collection at the moment the job started. Any writes that land after that point will be in the next export, not this one. Queries continue running normally throughout — the export doesn't lock the collection or interrupt traffic.
Practical version: kick off an export, then keep upserting. The export finishes cleanly. The new rows show up in queries immediately and in the next export.
Parts and sizes
Every export is chunked into parts of roughly 100 MB after compression. This is universal — even a small collection gets at least one part. The chunking keeps individual files well under filesystem caps and makes downloads resumable per part. The _manifest.json lists every part with its filename, byte size, and row count, so you can fan out a parallel download if you want.
There's no upper limit on collection size, vector count, or part count. If your collection is 50 GB, you get 50 GB back across however many parts that takes.
Email when it's ready
For larger collections you might not want to keep the SDK running. Pass send_email=True and we'll email the address on your account when the parts are staged and ready to download:
vp.collections.export("my_collection", "out/", wait=False, send_email=True)
store.export("out/", wait=False, send_email=True)
send_email also accepts a string. Pass an address instead of True — a shared inbox, a teammate, an alias that files the notice somewhere useful — and the email goes there rather than to your account address.
The email includes the part count, total bytes, and a link back to the collection page where every part is downloadable individually.
The seven-day window
After an export completes the parts stay available for download for seven days. After that they're swept off the staging disk. Starting a new export also clears the previous one — only one export per collection lives at a time, the most recent one always wins.
You can re-export any time, as often as you like.
In your dashboard
Every collection page has an Export your data section. You can start an export from there, watch parts roll in as the job runs, and download them individually when it's done. If you only ever interact with Vector Panda through the dashboard, you never need to touch the SDK to leave with your data.
A few things to do with it
Once you have the parquet directory in hand:
- Back it up — S3, your own NAS, cold storage, wherever makes sense
- Run offline analysis in duckdb or pandas without touching the live collection
- Spot-check it against your source-of-truth pipeline to catch drift
- Keep a snapshot for audit or compliance
Your data, in your hands, in a format every tool reads.
