Python SDK
rho-store reads and writes tables from Python: the scripts, notebooks and jobs around your agents.
pip install "rho-store[pandas]" # or [polars]export RHO_API_KEY=rho_sk_… # an API tokenMake the token on the API tokens page of the rho app: see API tokens.
import rho
rho.put(df, "research/competitors") # a new version; columns keep their typesrho.put(new, "research/competitors", mode="upsert", key="company", message="weekly refresh")df = rho.get("research/competitors") # the latest; version=3 for an earlier onedf = rho.query('SELECT category, count(*) AS n FROM "research/competitors" GROUP BY 1')rho.history("research/competitors") # who wrote what, whenrho.restore("research/competitors", version=3) # as a new versionurl = rho.share("research/competitors") # makes it public: a link anyone can openData in, data out
Section titled “Data in, data out”put takes a pandas or Polars DataFrame, an Arrow table or a list of dicts. get and query give pandas by default; pass as_="polars", "arrow" or "rows" for another.
Data travels as Parquet, never JSON, so types come back as they went: whole numbers stay whole (nullable ones become Int64), timestamps keep nanoseconds, decimals their scale. A few things are kept differently:
- a time zone becomes UTC, the same instant;
- a category becomes text;
- a DataFrame’s default index isn’t kept, and a named one becomes a column;
- durations are refused, with a hint.
replace, the default: the data becomes the table.append: adds the rows.upsert: updates the rows whosekeymatches, and adds the rest.create: fails if the table exists.
Pass run="nightly-2026-10-29" to label a job’s writes in the history.
Async, and other servers
Section titled “Async, and other servers”async with rho.AsyncClient() as client: await client.put(df, "runs/evals", mode="append", run=run_id) df = await client.query('SELECT model, avg(score) FROM "runs/evals" GROUP BY 1')rho.Client and rho.AsyncClient have the same methods. Pass api_key= and url= to choose them yourself; RHO_URL points the module’s functions at another server.
Warnings and errors
Section titled “Warnings and errors”What rho decides about data it’s given comes as a rho.RhoWarning, and the write is made. Column names are compared without case, so A and a in one frame are refused.
Errors are rho.RhoError, with kind (not_found, exists, schema_conflict, …) and a hint saying what to do.