# SDKs

Vibedasher ships official, fully-typed SDKs for **TypeScript** and **Python**, generated from the API's OpenAPI spec so they stay in lockstep with the platform. They're how your own code reaches the maintained platform: run SQL against your datasets, trigger refreshes, manage connections and access rights, wire a dashboard to live data, or automate anything the [MCP tools](/guides/build-with-your-agent.md) do from a script instead of an agent.

| Language   | Package                                                                  | Documented here |
| ---------- | ------------------------------------------------------------------------ | --------------- |
| TypeScript | [`@vibedasher/client`](https://www.npmjs.com/package/@vibedasher/client) | 4.1.5           |
| Python     | [`vibedasher`](https://pypi.org/project/vibedasher/)                     | 4.3.19          |

Both SDKs authenticate with an **API key** in the `X-Api-Key` header. Mint one in the app under **Settings → API keys** at [cloud.vibedasher.com](https://cloud.vibedasher.com) — see [Authentication](/api/authentication.md). (The SDKs are the *code* path; if your agent is driving Vibedasher over MCP, it signs in with `vibedasher login` instead and needs no key at all — see [Build with your agent](/guides/build-with-your-agent.md).)

Neither client needs to be told which region your account lives in. With no base URL and no `region`, both resolve to `https://api.vibedasher.com/api`, which routes each request by the credential it carries — see [One host, routed by your credential](/api/authentication.md#one-host-routed-by-your-credential). Passing `region` is still supported and pins the client to that region's own hostname, `https://api.{region}.vibedasher.com/api`; an explicit `baseUrl` / `base_url` overrides both. (`region` became optional in `@vibedasher/client` 4.1.5 and `vibedasher` 4.3.19.)

Looking for a specific endpoint? Browse the full **[API Reference](/reference/vibedasher-sdk-api.md)** — every operation grouped by resource, with curl / Python / TypeScript request samples generated from the same signatures the SDKs are built from.

## The shape of every call[​](#the-shape-of-every-call "Direct link to The shape of every call")

Both SDKs expose the same **resource-grouped fluent client**. Construct it once, then call methods grouped by resource. Signatures are **flat**: path parameters first (in path order), then the request body, then one optional trailing options object holding the query parameters. There are no `path` / `query` / `body` wrapper objects.

Resources are the same 23 in both languages, camelCase in TypeScript and snake\_case in Python:

`accounts`, `aiProviders`, `apiKeys`, `chats`, `connections`, `dashboardSubscriptions`, `datasets`, `dbt`, `embedTokens`, `folders`, `groups`, `images`, `questions`, `refreshes`, `skills`, `sql`, `subscriptions`, `tasks`, `templates`, `tools`, `upload`, `users`, `viz`.

A non-2xx response **raises** — `VibedasherApiError` in both languages. Methods never resolve to an error union, so you do not check a result before using it.

## TypeScript[​](#typescript "Direct link to TypeScript")

```
npm install @vibedasher/client
```

```
import { Vibedasher, VibedasherApiError } from '@vibedasher/client';



const client = new Vibedasher({

  apiKey: process.env.VIBEDASHER_API_KEY!,

});



// List datasets — query params go in the trailing options object

const page = await client.datasets.list({ limit: 50 });



// Create — the body is positional

const created = await client.datasets.create({ name: 'Sales', type: 'csv' });



// Fetch one by id — path params are positional

const one = await client.datasets.get(created.dataset.id);



// Update — path param, then body

const updated = await client.datasets.update(one.dataset.id, { name: 'Sales 2026' });
```

Every method is typed end to end: the argument types and the resolved response type come from the spec, and the per-resource interfaces (`DatasetsResource`, `DatasetsListOptions`, …) and model types (`DatasetResponse`, `DatasetUpdate`, …) are exported from the package root if you want to annotate a variable.

### Running SQL[​](#running-sql "Direct link to Running SQL")

`client.query` is the hand-written method the generated surface does not have: one call in, typed columnar results out.

```
const result = await client.query({

  sql: 'SELECT country, SUM(revenue) AS revenue FROM sales GROUP BY 1',

});

console.log(result.columns, result.rows);
```

`datasetIds` is optional — omit it and the server resolves the dataset set from the aliases your SQL references, applying exactly the same authorization.

### Uploading a CSV[​](#uploading-a-csv "Direct link to Uploading a CSV")

```
const uploaded = await client.datasets.upload({

  name: 'Sales',

  content: 'country,revenue\nFR,10\nDE,12\n',

});

console.log(uploaded.cleanSQLName); // the alias to use in client.query SQL
```

### Escape hatch[​](#escape-hatch "Direct link to Escape hatch")

The underlying `fetch` client is on the instance as `client.http` if you need interceptors, a custom `fetch`, or per-call overrides. There is no module-level singleton to configure: each `Vibedasher` instance owns its own client, so two clients in one process never clobber each other's base URL or key.

## Python[​](#python "Direct link to Python")

```
pip install vibedasher
```

```
from vibedasher import Vibedasher, VibedasherApiError

from vibedasher.models import DatasetCreate, DatasetUpdate



client = Vibedasher(api_key="<your-api-key>")



# List datasets — query params are keyword-only

page = client.datasets.list(limit=50)



# Create — the body is the first positional argument

created = client.datasets.create(DatasetCreate(name="Sales", type_="csv"))



# Fetch one by id — path params are positional

one = client.datasets.get(created.dataset.id)



# Update — path param, then body

updated = client.datasets.update(one.dataset.id, DatasetUpdate(name="Sales 2026"))
```

Request and response models live in `vibedasher.models`. Resource attributes are snake\_case (`client.datasets`, `client.api_keys`, `client.ai_providers`, `client.embed_tokens`, `client.viz`, …), and so are the methods.

### Running SQL[​](#running-sql-1 "Direct link to Running SQL")

```
result = client.query(sql="SELECT country, SUM(revenue) AS revenue FROM sales GROUP BY 1")

print(result.columns, result.rows)
```

### Uploading a CSV[​](#uploading-a-csv-1 "Direct link to Uploading a CSV")

```
uploaded = client.datasets.upload(name="Sales", content="country,revenue\nFR,10\nDE,12\n")

print(uploaded["cleanSQLName"])  # the alias to use in client.query SQL
```

### Escape hatch[​](#escape-hatch-1 "Direct link to Escape hatch")

The flat operation modules under `vibedasher.api.<tag>` are still shipped for anything the facade does not cover. Build a raw client with `create_client` and call `.sync()`, `.sync_detailed()`, `.asyncio()`, or `.asyncio_detailed()`:

```
from vibedasher import create_client

from vibedasher.api.datasets import read_datasets



raw = create_client(api_key="<your-api-key>")

datasets = read_datasets.sync(client=raw)
```

## Error handling[​](#error-handling "Direct link to Error handling")

Both SDKs raise on a non-2xx response, so the happy path reads straight through and errors are caught where you want to handle them.

**TypeScript:**

```
import { Vibedasher, VibedasherApiError } from '@vibedasher/client';



try {

  const page = await client.datasets.list({ limit: 50 });

  console.log(page.datasets);

} catch (err) {

  if (err instanceof VibedasherApiError) {

    console.error(`Request failed (${err.status})`, err.detail, err.requestId);

  } else {

    throw err;

  }

}
```

**Python:**

```
from vibedasher import VibedasherApiError



try:

    page = client.datasets.list(limit=50)

except VibedasherApiError as err:

    print(f"Request failed ({err.status_code})", err.detail, err.request_id)
```

Common status codes: **401/403** (missing or invalid API key, or a key used on a session-only route), **404** (not found, or a viz that isn't published), **422** (request validation error). Each endpoint's reference page lists the exact responses it can return.

## Pagination[​](#pagination "Direct link to Pagination")

List endpoints return a page of results plus a `nextKey` cursor. You rarely need to drive that cursor yourself: every listable resource carries `iterAll()` (TypeScript) / `iter_all()` (Python), which walks the pages and yields rows.

**TypeScript:**

```
for await (const ds of client.datasets.iterAll()) {

  handle(ds);

}
```

**Python:**

```
for ds in client.datasets.iter_all():

    handle(ds)
```

Both also expose the page-level form (`iterPages()` / `iter_pages()`) when you want the envelope rather than the rows. Driving the cursor by hand still works — pass `nextKey` back as `exclusiveStartKey` (`exclusive_start_key` in Python) and stop when it comes back `null`/`None` — but the iterators also detect a cursor that stops advancing and raise rather than looping forever.

The wire name — what the cURL tab and every reference page's parameter table show — is always `exclusiveStartKey`; the TypeScript SDK keeps that camelCase spelling as its option key, and the Python SDK exposes the same parameter as the `exclusive_start_key` keyword. It is one cursor, spelled per language — never both on one request.

## Response typing caveat[​](#response-typing-caveat "Direct link to Response typing caveat")

Some API routes currently return loosely-typed responses (`object` / `Any`) because they declare a generic response shape on the backend. Request bodies, query/path params, and an increasing set of responses are fully typed. Response types are being hardened module-by-module — upgrade the SDK to pick up improvements.

note

The SDKs are generated, never hand-edited. If you spot a gap, it's almost always upstream in the API's OpenAPI spec — reach out via the [contact page](https://vibedasher.com/contact).
