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 columnar — result.columns are QueryColumn(name, type) with type ∈ string | 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 methods (generated)

Datasets, uploads, viz metadata, API keys, etc. are resource-grouped on the same client, generated from the OpenAPI spec with flat, fully typed signatures: path params positional (in path order), then the request body, then the query params as keyword-only kwargs. No **kwargs, no bare Any; a non-2xx raises VibedasherApiError (status_code, detail, request_id).

from vibedasher import Vibedasher, VibedasherApiError
from vibedasher.models import DatasetUpdate

client = Vibedasher(api_key="sk_...", region="eu-central-1")
page = client.datasets.list(limit=50, folder_id=7)               # -> DatasetListResponse
ds = client.datasets.update(42, DatasetUpdate(name="renamed"))   # -> DatasetResponse
client.viz.delete(7)                                             # 204 -> None
try:
    client.viz.get(999999)
except VibedasherApiError as e:
    print(e.status_code, e.detail, e.request_id)

The low-level generated modules remain available for anything the facade does not expose (vibedasher.api.<tag>.<operation_id> with .sync(), .sync_detailed(), .asyncio(), .asyncio_detailed()), driven by create_client(api_key, region=...).

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/_facade.py — GENERATED resource classes with the flat signatures + VibedasherApiError.
  • tests/test_facade_surface.py — the blocking lint on _facade.py (no **kwargs, no bare Any, snake_case only).
  • vibedasher/api/<tag>/<operation>.py — one module per API operation.
  • vibedasher/models/ — request/response models (attrs classes).
  • vibedasher/factory.py — create_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.

Release files for vibedasher 4.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vibedasher 4.3.0
File Size Uploaded
vibedasher-4.3.0.tar.gz 464.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vibedasher 4.3.0
File Interpreter ABI Platform
vibedasher-4.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.5 MB

Release files / vibedasher-4.3.0.tar.gz

Download URL vibedasher-4.3.0.tar.gz
Size 464.5 kB
Tags Source
SHA-256 checksum
How to use checksums
03b9b27be51cf16bafffb40e6eb43d8f8ae9afc12605f8bdc80c48bb75ceec7e
BLAKE2b-256 checksum
How to use checksums
08e058382aff304412391fb0fdfdd408ff38babafe971a7009c872aadd132673
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / vibedasher-4.3.0-py3-none-any.whl

Download URL vibedasher-4.3.0-py3-none-any.whl
Size 1.1 MB
Tags Python 3
SHA-256 checksum
How to use checksums
be1674e4378d537cb45932321d9cf620e60b57149ce3e0248eb269683a3af434
BLAKE2b-256 checksum
How to use checksums
3e591d2fca9dbbab943c923c45d9a34f82e720c591f2c2f7baeec8b302a10512
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

4.3.1

2 release files

This release

4.3.0 This release

2 release files

4.2.0

2 release files

4.1.0

2 release files

4.0.0

2 release files

3.1.0

2 release files

3.0.0

2 release files

2.4.0

2 release files

2.3.0

2 release files

2.2.0

2 release 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