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-2.4.0.tar.gz (257.7 kB view details)

Uploaded Source

Built Distribution

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

vibedasher-2.4.0-py3-none-any.whl (763.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: vibedasher-2.4.0.tar.gz
  • Upload date:
  • Size: 257.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.12.3 Linux/7.0.0-28-generic

File hashes

Hashes for vibedasher-2.4.0.tar.gz
Algorithm Hash digest
SHA256 be7f6b5791af37cbd9d0cdd0d44fd6cd2059174ecd248352ecf340664e30b25c
MD5 be5386cfdc404e9181aeda91003984cb
BLAKE2b-256 2e216eb9f32b1b857d6880a96c0bef8161ca0bcea07184de71e7948831b74dcf

See more details on using hashes here.

File details

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

File metadata

  • Download URL: vibedasher-2.4.0-py3-none-any.whl
  • Upload date:
  • Size: 763.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.12.3 Linux/7.0.0-28-generic

File hashes

Hashes for vibedasher-2.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a6ed97e49a4524ac45fccd66c6dae7cd1981c1192767a5fbf374758c9e6968ca
MD5 1170fe641e12273862153ae076514507
BLAKE2b-256 dc8bdfd355bafb0bfea7d62b2d599aaef7f62a1163596441d969a5ed16c63099

See more details on using hashes here.

Release history Release notifications | RSS feed

4.1.0

2 files

4.0.0

2 files

3.1.0

2 files

3.0.0

2 files

This release

2.4.0 This release

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