Querying Data¶
Working with an agent?
You don't have to write these scripts by hand. Claude has this page and the full schema reference in its workspace, so you can describe the question and let it pick the tables, push the filters into the query, and hand back a script you can keep and rerun. See Getting Started With Agents.
Interactive UI¶
The quickest way to explore your data is the DuckDB web UI. It connects to your storage, registers every table you have access to, and opens a local SQL editor in the browser. No Python beyond the launch command:
It opens http://localhost:4213, with all tables available immediately as views.
Press Enter in the terminal to stop the server.
Export to CSV¶
If you'd rather work in Excel or another spreadsheet tool, export every table you have access to as CSV files, one file per table:
The files land in an exports/ folder in your workspace, named <table>.csv.
Each run overwrites them with a fresh snapshot. To write somewhere else:
The directory is created if it doesn't exist. Coming from IndieBI DDA v1? See Migrating From v1 for how the table names map.
Querying From Python¶
The SDK exposes two clients for querying data in code. Put your scripts in the
projects/ folder of your workspace; projects/explore.py is there as a
starting point.
DuckDBClient¶
Run SQL against your data and get back a validated Polars DataFrame. The best choice for straightforward queries.
from dda_sdk import DuckDBClient, SalesPerSkuSchema
with DuckDBClient() as client:
df = client.query(
SalesPerSkuSchema,
"""
SELECT *
FROM sales_per_sku
WHERE date >= '2026-01-01'
AND date <= '2026-01-31'
""",
)
print(df["gross_sales"].sum())
The schema class you pass to query() validates the result. If a column is
missing or has the wrong type, you get an error immediately rather than a silent
wrong answer downstream.
PolarsClient¶
Returns a LazyFrame: nothing is fetched until you call .collect(). Better for
large date ranges, or when you want to chain several transformations before
pulling data.
import polars as pl
from dda_sdk import PolarsClient, SalesPerSkuSchema
with PolarsClient() as client:
df = (
client.scan_table(SalesPerSkuSchema)
.filter(pl.col("date").is_between(pl.date(2026, 1, 1), pl.date(2026, 3, 31)))
.filter(pl.col("portal_platform_region").str.starts_with("steam"))
.group_by(pl.col("date").dt.truncate("1mo"))
.agg(pl.col("gross_sales").sum())
.sort("date")
.collect()
)
PolarsClient takes SQL too:
with PolarsClient() as client:
df = client.query(
"SELECT * FROM sales_per_sku WHERE date >= '2026-01-01'"
).collect()
Passing Credentials Explicitly¶
Both clients read USER_ID and SAS_TOKEN from your .env by default. You can
also pass them directly through Config:
from dda_sdk.config import Config
cfg = Config(user_id="u-xxxxxxx", sas_token="<your-sas-token>")
with DuckDBClient(cfg) as client:
...
with PolarsClient(cfg) as client:
...
This is useful when you're running against a different storage account, or when
credentials come from a secret manager rather than a .env file.