The Sery SDK

The sery SDK lets any script or app query your data mesh by address — official clients for Python (pip install sery) and TypeScript / JavaScript (npm install @seryai/sdk). You write one SQL statement that references sources by a sery:// URI; Sery resolves each address to the machine that owns the data, runs the query there, and returns only the result rows. Raw files never move through Sery — the query goes to the data, not the other way around.

The walkthrough below is in Python; the JavaScript / TypeScript equivalent has its own section further down.

It talks to data.sery.ai, the public data API (separate from the dashboard's api.sery.ai). Requires a workspace API key and the Plus plan.

Install

# Python
pip install sery            # add sery[pandas] for .to_pandas()

# TypeScript / JavaScript (zero deps; Node >= 18, browser, edge, Deno, Bun)
npm install @seryai/sdk

Authenticate

Create a workspace API key at Settings → API Keys (keys start with sery_; the data API is a Plus feature). Pass it to the client:

import sery

client = sery.Client(api_key="sery_...")   # base_url defaults to https://data.sery.ai

Discover sources

catalog() lists every addressable source in your workspace, each with the sery:// address you pass to query() and its column schema:

for src in client.catalog():
    print(src.sery_uri, "—", src.machine, src.file_format)
# sery://sam-laptop/local/sales/orders.parquet — sam-laptop parquet

An address is sery://<machine>/<protocol>/<path> — the machine's name or stable id, a protocol (local, s3, https), and the file path. Anything from catalog() is guaranteed to resolve.

Run a query

df = client.query("""
    SELECT customer, SUM(amount) AS total
    FROM 'sery://sam-laptop/local/sales/orders.parquet'
    GROUP BY customer
    ORDER BY total DESC
""").to_pandas()

The result object also exposes raw access:

result = client.query("SELECT * FROM 'sery://my-mac/local/data.parquet' LIMIT 5")
result.columns          # ['id', 'name', ...]
result.rows             # [[1, 'a'], [2, 'b'], ...]
for row in result:      # iterate as dicts
    print(row["name"])
result.incomplete       # True if a machine didn't respond
result.warnings         # human-readable warnings to surface

When incomplete is True, a targeted machine was offline — check warnings before trusting an aggregate, since a SUM/COUNT may undercount.

JavaScript / TypeScript

The same API in TypeScript — zero dependencies (uses the global fetch), so it runs in Node, the browser, edge runtimes, Deno, and Bun.

import { Client } from "@seryai/sdk";

const client = new Client({ apiKey: "sery_..." }); // base: https://data.sery.ai

// Discover sources
for (const src of await client.catalog()) {
  console.log(src.seryUri, src.machine, src.fileFormat);
}

// Run a query
const result = await client.query(`
  SELECT customer, SUM(amount) AS total
  FROM 'sery://sam-laptop/local/sales/orders.parquet'
  GROUP BY customer
  ORDER BY total DESC
`);

console.table(result.toObjects()); // [{ customer, total }, ...]
result.incomplete;                 // true if a machine didn't respond
result.warnings;                   // surface these before trusting aggregates

Errors are typed classes mirroring the Python ones — catch with instanceof:

import { AmbiguousMachine } from "@seryai/sdk";

try {
  await client.query("SELECT * FROM 'sery://laptop/local/x.parquet'");
} catch (e) {
  if (e instanceof AmbiguousMachine) {
    for (const c of e.candidates) console.log(c.label, c.machine_id);
  }
}

Errors

Every failure is a typed exception (Python sery.errors / TypeScript named exports):

ExceptionWhen
AuthErrormissing / invalid API key (401)
QueryErrorno sery:// refs, bad address, unsupported protocol (400)
MachineNotFoundunknown machine (404)
AmbiguousMachinea name matched >1 machine — .candidates lists machine ids (409)
CrossMachineJoinUnsupportedthe query spans machines — .machines (422)
MachinesUnavailableevery targeted machine offline — .failures (503)
try:
    client.query("SELECT * FROM 'sery://laptop/local/x.parquet'")
except sery.AmbiguousMachine as e:
    for c in e.candidates:
        print(c["label"], c["machine_id"])   # re-issue with the machine_id

Limits

All sources in one query must live on the same machine — cross-machine joins raise CrossMachineJoinUnsupported for now. Routable protocols today: local, s3, https. The data API is rate-limited per workspace and requires the Plus plan.

Source on GitHub: seryai/sery-sdk-python.

Ready to get started?

Download Sery Link, pick a folder, ask your first question. No account required.

Download Sery Link