Skip to content
Docs / REST API

Documentation

REST API

Create, query and manage DuckHouse databases over HTTP with an API key: run SQL, read schemas and set up Connections.

Everything the dashboard does is available over HTTPS: create and delete databases, run SQL, read schemas, and manage Connections. Requests and responses are JSON.

Base URLhttps://duckhouse.co/api/v1
AuthenticationAuthorization: Bearer dhk_…, an API key
Content typeapplication/json
Terminal
export DUCKHOUSE_API_KEY=dhk_your_key

curl https://duckhouse.co/api/v1/databases \
  -H "Authorization: Bearer $DUCKHOUSE_API_KEY"

Errors

Failures return a matching HTTP status and a body of the form { "error": "what went wrong" }.

StatusMeaning
400The request is malformed, or the SQL failed. The message says which.
401The API key is missing, wrong, expired or revoked.
403The key is valid but not allowed to do this, for example a write with a read-scoped key.
404No such database or Connection in your organization.
409The database is not in a state that allows this, for example it is still provisioning.

Databases

GET/databases

List your organization’s databases, newest first.

POST/databases

Create a database. Requires a full-scope key.

FieldTypeDefault
namestringrequired1 to 50 characters.
regionstringiadiad, lax, fra or sin. DuckLake runs in iad only; for DuckDB iad is recommended, since the dashboard, API and syncs run there.
vmSizestringshared-cpu-1xSee sizes.
memoryMbinteger1024256 to 32768.
ttlHoursnumbernoneDelete the database automatically after this many hours.
autoStopbooleanfalseStop when idle. See stop when idle.
Request
curl -X POST https://duckhouse.co/api/v1/databases \
  -H "Authorization: Bearer $DUCKHOUSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "q3-analysis", "ttlHours": 72, "autoStop": true }'
201 Created
{
  "database": {
    "id": "cmfk2x9q10001",
    "name": "q3-analysis",
    "engine": "duckdb",
    "engineVersion": "1.5.5",
    "status": "provisioning",
    "region": "iad",
    "vmSize": "shared-cpu-1x",
    "memoryMb": 1024,
    "autoStop": true,
    "expiresAt": "2026-09-23T18:00:00.000Z",
    "connection": { "protocol": "quack", "uri": "quack:duckdb-a1b2c3d4e5.fly.dev:443" }
  },
  "token": "dh_…",
  "connection": {
    "uri": "quack:duckdb-a1b2c3d4e5.fly.dev:443",
    "sql": "ATTACH 'quack:duckdb-a1b2c3d4e5.fly.dev:443' AS q3_analysis (TOKEN 'dh_…');"
  }
}

The token is returned by this call and never again. A new database starts as provisioning; poll GET /databases/:id until its status is running, usually about a minute.

GET/databases/:id

One database, including its current status and connection address.

DELETE/databases/:id

Delete a database and everything in it, permanently. Requires a full-scope key.

GET/engines

The database types that can be created right now.

Run a query

POST/databases/:id/query

Run one SQL statement and return its rows.

FieldTypeDefault
sqlstringrequiredOne statement, up to 100 KB.
readOnlybooleanfalseRefuse anything that is not a plain query. Always on for read-scoped keys.
maxRowsinteger10000Up to 100000. Extra rows are dropped and reported.
Request
curl -X POST https://duckhouse.co/api/v1/databases/$DB/query \
  -H "Authorization: Bearer $DUCKHOUSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sql": "SELECT status, count(*) AS n FROM stripe.invoices GROUP BY status" }'
200 OK
{
  "columns": ["status", "n"],
  "types": ["VARCHAR", "BIGINT"],
  "rows": [
    { "status": "paid", "n": "1284" },
    { "status": "open", "n": "37" }
  ],
  "rowCount": 2,
  "truncated": false
}
  • The query runs on the server, so schema names, joins, UPDATE and DELETE all work as written. None of the attachment limits apply here.
  • Large integers, decimals, dates and timestamps are returned as strings, so that no precision is lost in JSON.
  • When truncated is true, the result was cut at maxRows. Aggregate or filter in SQL rather than paging through a large table; for bulk export, attach with DuckDB and COPY to a file.
GET/databases/:id/schema

Every table and view, with column names, types and nullability. Useful before generating SQL.

Connections

The same operations as the Connections section of the dashboard. Creating, changing and deleting require a full-scope key.

GET/connectors

The available sources and the fields each one needs.

POST/databases/:id/connections/test

Check that a source can be reached, and list what could be synced, without saving anything.

GET/databases/:id/connections

The Connections feeding a database.

POST/databases/:id/connections

Create a Connection. The source is tested first, and the first sync starts immediately. Returns 409 if the database already has a Connection for a source that allows only one (every source except PostgreSQL, MySQL, Google Sheets and SFTP / FTP).

Request
curl -X POST https://duckhouse.co/api/v1/databases/$DB/connections \
  -H "Authorization: Bearer $DUCKHOUSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "postgres",
    "name": "Production replica",
    "scheduleMinutes": 60,
    "config":  { "host": "db.example.com", "port": 5432, "database": "shop", "user": "duckhouse_reader" },
    "secrets": { "password": "…" }
  }'
GET/connections/:id

One Connection and the state of its last run. Secrets are never returned.

PATCH/connections/:id

Rename, pause or resume (status: paused or active), reschedule, or replace the configuration and secrets.

GET/connections/:id/streams

What the source has now, and the per-table choices this Connection has saved. Change them with PATCH and config.streams, which replaces the saved list: enabled: false turns a table off, and excludeColumns (PostgreSQL and MySQL) lists columns never read.

DELETE/connections/:id

Stop syncing. Tables already in the database are left in place.

POST/connections/:id/sync

Start a sync now. Returns immediately; follow progress through the runs.

GET/connections/:id/runs

Run history, most recent first, with rows synced per table and any error.

A note on API keys