Skip to main content

vibedasher — Python SDK

Official Python SDK for the Vibedasher headless data engine. The method you'll use most is client.query(...) — it runs SQL across your managed datasets and returns typed rows, so you can render a dashboard natively from your own backend.

Install

pip install vibedasher

Headless / eject — start here

Vibedasher hosts the data engine (ETL, datasets, query); you own the frontend/app. You author a dashboard in Vibedasher, eject its code, and wire it to the engine with a single call:

from vibedasher import Vibedasher

# Server-side (API key held on your server, never shipped to a browser).
client = Vibedasher(api_key="sk_...", region="eu-central-1")

result = client.query(
    sql="SELECT region, AVG(lead_price_usd) AS avg_price FROM sales GROUP BY region",
    params={"region": "Caribe"},     # bound/escaped server-side, never interpolated
)

for col in result.columns:
    print(col.name, col.type)        # e.g. region string / avg_price number
for row in result.rows:
    print(row["region"], row["avg_price"])
  • dataset_ids is optional (plural). Omit it and the server infers the dataset set from the aliases your sql references. Pass it to pin an exact set explicitly: client.query(sql=..., dataset_ids=[12, 47], params=...) — each id resolves server-side to that dataset's alias under RLS, and a dataset not in the set is rejected (deny-don't-drop). A mistyped/unknown alias raises 400 query_dataset_unresolved.
  • sql is inline and alias-only — it references dataset aliases, never a physical table.
  • Results are typed columnarresult.columns are QueryColumn(name, type) with typestring | number | boolean | date | timestamp | json, and result.rows are dicts keyed by column name.
  • Transport is hidden — inline vs presigned object storage, MessagePack decoding, and transient retries all happen inside query(). One call in, typed rows out.

query() is the one hand-written method (_query.py); everything else is generated (see below).

Where the query runs: type="wasm"

By default the server executes your SQL and returns rows. Pass type="wasm" to get a plan instead — the injected SQL plus one presigned Parquet per dataset — and execute it yourself (in DuckDB-WASM in a browser, or plain DuckDB locally):

result = client.query(sql="SELECT region, COUNT(*) FROM sales GROUP BY region", type="wasm")

if result.mode == "wasm":
    for src in result.plan.sources:
        print(src.alias, src.url, src.bytes)   # register each under `alias`
    print(result.plan.sql)                     # then run this
else:
    print(result.rows, result.fallback_reason) # ran server-side, and why

Authorization does not move. The server still authorizes, validates alias-only SQL, and injects row filters — in that order, before the capability check. Asking for wasm skips no gate; you get a plan only for data you were already allowed to read.

Asking for wasm does not guarantee getting it. If any participating dataset isn't wasm-capable — no current extract, an extract over the size cap, or a mandatory row-level filter — the query runs server-side and the response says so via mode: "backend" and a fallback_reason. It is never a silent downgrade, so always branch on result.mode, never on what you requested.

VibedasherEmbedClient (scoped-token) has no wasm lane and raises ValueError rather than quietly serving rows.

Embed vs eject

  • Embed (iframe, zero-code): paste a snippet; we host and render. Nothing to build.
  • Eject (this SDK, own-your-code): pull the dashboard's code into your stack and feed it data via client.query(...). Your app owns the rendering; only the data crosses the wire. See a runnable host app in examples/nextjs-embed (TS), the same contract as here.

Client-side (no backend) auth

For a pure-frontend app, mint a short-TTL scoped token and use the same method via the token-pinned client:

from vibedasher import VibedasherEmbedClient

client = VibedasherEmbedClient(token=scoped_token, region="eu-central-1")
result = client.query(sql=sql, params=params)
# The token pins the viz + datasets — dataset_ids are accepted for symmetry but the
# server enforces the token's bound set.

Concurrency: a 503 is a limit, not an outage

The query engine has a finite, shared concurrency ceiling. A 503 Service Unavailable from query() means the request was throttled, not that the service is down — the same SQL run serially succeeds. query() retries throttled requests (429/502/503/504) with jittered backoff and honours Retry-After.

httpx.Client is synchronous, so one client is already one query at a time and there is no in-flight gate here — that lives in the TypeScript SDK, where a browser dashboard fans out on mount. If you thread this client yourself, keep the width modest.

Errors raise QueryHttpError (a httpx.HTTPStatusError subclass, so existing except clauses keep working) carrying status_code, the API's own message, and request_id — quote that in a bug report.

Everything else: resource operations (generated)

Datasets, uploads, viz metadata, API keys, etc. are generated from the OpenAPI spec:

from vibedasher.factory import create_client
from vibedasher.api.datasets import read_datasets_v1_datasets_get

client = create_client(api_key="sk_...", region="eu-central-1")
# Each operation lives under vibedasher.api.<tag>.<operation_id> and exposes
# .sync(), .sync_detailed(), .asyncio(), .asyncio_detailed().
datasets = read_datasets_v1_datasets_get.sync(client=client)

Auth is an API key sent as X-Api-Key (mint one via POST /v1/api-keys).

Paging a list endpoint: iter_all()

Every cursor-paginated list endpoint returns one page plus a nextKey:

{ "datasets": [ ... ], "nextKey": 812 }

nextKey: null means that was the last page — it is the only end-of-list signal. Rather than hand-rolling that loop, iterate:

for dataset in client.datasets.iter_all():
    print(dataset.name)                    # pages transparently, rows streamed

for dataset in client.datasets.iter_all(page_size=50, max_pages=10, folder_id=7):
    ...                                    # cap the work; extra kwargs are forwarded

for page in client.datasets.iter_pages():
    print(page.next_key)                   # page-at-a-time, e.g. to checkpoint

Nested collections take the method name:

for upload in client.datasets.iter_all(method="list_uploads", dataset_id=4):
    ...

Notes:

  • Bounded memory — rows are yielded as each page arrives, and break stops the requests immediately. Nothing is prefetched or accumulated.
  • max_pages caps the work: iterating an unbounded table to exhaustion is a footgun, so the ceiling is one keyword away.
  • A repeated cursor raises CursorNotAdvancingError instead of looping forever. That is a server bug in the endpoint's nextKey; the SDK surfaces it rather than absorbing it as a short read.
  • For a paginated endpoint that is not on the resource, the standalone form works on any list callable: from vibedasher import iter_all.

Layout

  • vibedasher/_query.py — the hand-written query() (transport, decode, retry, typed rows).
  • vibedasher/_pagination.py — the hand-written iter_all() / iter_pages() cursor loop.
  • vibedasher/api/<tag>/<operation>.py — one module per API operation.
  • vibedasher/models/ — request/response models (attrs classes).
  • vibedasher/factory.pycreate_client(api_key, region=..., base_url=...).

Generated code — do not edit vibedasher/ by hand. Regenerate with make py from packages/sdk/. Source of truth: ../openapi.json. query() and the pagination iterators are the deliberate exceptions — hand-written in packages/sdk/py/{_query,_pagination}.py and re-copied after each generation.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vibedasher-3.1.0.tar.gz (294.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vibedasher-3.1.0-py3-none-any.whl (814.1 kB view details)

Uploaded Python 3

File details

Details for the file vibedasher-3.1.0.tar.gz.

File metadata

  • Download URL: vibedasher-3.1.0.tar.gz
  • Upload date:
  • Size: 294.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for vibedasher-3.1.0.tar.gz
Algorithm Hash digest
SHA256 e844e8840f475c2c649b7d9cd8540435354c38a81ef0236593b59d7d5a1393a3
MD5 6abf02594fd36b9fb35f2702be5135bd
BLAKE2b-256 7d96a16447addfe3d8b4f5577b62d571daa847375f360d9b311a42a73a374d7a

See more details on using hashes here.

File details

Details for the file vibedasher-3.1.0-py3-none-any.whl.

File metadata

  • Download URL: vibedasher-3.1.0-py3-none-any.whl
  • Upload date:
  • Size: 814.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for vibedasher-3.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1768932a53cfd309b1c06934bd504930adf1d0e9f0ab3964e4d5cf650e14eaa1
MD5 293da47ee515eede3a6a598326fe6009
BLAKE2b-256 7cd42d7970321a253c2ea76b0e507c4cc664a9954279166b6753cbec6b1c5684

See more details on using hashes here.

Release history Release notifications | RSS feed

4.1.0

2 files

4.0.0

2 files

This release

3.1.0 This release

2 files

3.0.0

2 files

2.4.0

2 files

2.3.0

2 files

2.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page