Skip to content

Quickstart

Everything below is curl against the live API. If you do not have a token yet, see Authentication — an operator issues you one.

Terminal window
export SQLITED_TOKEN='dbk_live_key_…'
export SQLITED_BASE='https://sql.vreelo.xyz'
Terminal window
curl -sS "$SQLITED_BASE/v1/databases" \
-H "Authorization: Bearer $SQLITED_TOKEN" \
-H 'content-type: application/json' \
-d '{"name":"production"}'
{
"database": {
"id": "db_01a00b50-f7d7-74e8-be44-877297734dec",
"name": "production",
"status": "active",
"createdAt": "2026-08-16T09:14:02.118Z",
"deletedAt": null
}
}

The id is what every other request needs. Note the envelope — the record is under database, not at the top level:

Terminal window
export DB=db_01a00b50-f7d7-74e8-be44-877297734dec

This request needs the org:admin scope. If you were handed a narrower data-plane key, it will answer 403 here and that is correct; creating databases and querying them are different powers.

Terminal window
curl -sS "$SQLITED_BASE/v1/databases/$DB/query" \
-H "Authorization: Bearer $SQLITED_TOKEN" \
-H 'content-type: application/json' \
-d '{"sql":"CREATE TABLE events (id INTEGER PRIMARY KEY, kind TEXT NOT NULL, at TEXT NOT NULL)"}'
{
"columns": [],
"rows": [],
"rowsAffected": 0,
"lastInsertRowid": 0,
"epoch": 1,
"txid": 1,
"durability": "durable",
"waitedMs": 121
}

"durability": "durable" means the schema change is in object storage. There is no separate migration step or commit — DDL is a write like any other, and it is durable by the time you read this.

Always bind values rather than interpolating them into the SQL. ? is positional:

Terminal window
curl -sS "$SQLITED_BASE/v1/databases/$DB/query" \
-H "Authorization: Bearer $SQLITED_TOKEN" \
-H 'content-type: application/json' \
-d '{
"sql": "INSERT INTO events (kind, at) VALUES (?, ?)",
"params": ["signup", "2026-08-16T09:15:00Z"]
}'
{
"columns": [],
"rows": [],
"rowsAffected": 1,
"lastInsertRowid": 1,
"epoch": 1,
"txid": 2,
"durability": "durable",
"waitedMs": 117
}

Named parameters work too — pass an object and use :name in the SQL. See Query.

Terminal window
curl -sS "$SQLITED_BASE/v1/databases/$DB/query" \
-H "Authorization: Bearer $SQLITED_TOKEN" \
-H 'content-type: application/json' \
-d '{"sql":"SELECT id, kind, at FROM events ORDER BY id"}'
{
"columns": ["id", "kind", "at"],
"rows": [[1, "signup", "2026-08-16T09:15:00Z"]],
"rowsAffected": 0,
"lastInsertRowid": null,
"epoch": 1,
"txid": null,
"durability": "read",
"waitedMs": 0
}

Rows are arrays, not objects, positionally matching columns. That is not laziness: duplicate column names are legal SQL — SELECT * FROM a JOIN b where both tables have an id — and an object would silently lose one of them. Zip them yourself if you want names, and see Client examples for a three-line helper.

"durability": "read" and "txid": null are what a read looks like. Nothing needed to become durable, so nothing waited.

A batch is one transaction. Either all of it is in one durable segment, or none of it committed:

Terminal window
curl -sS "$SQLITED_BASE/v1/databases/$DB/batch" \
-H "Authorization: Bearer $SQLITED_TOKEN" \
-H 'content-type: application/json' \
-d '{
"statements": [
{"sql": "INSERT INTO events (kind, at) VALUES (?, ?)", "params": ["login", "2026-08-16T09:16:00Z"]},
{"sql": "INSERT INTO events (kind, at) VALUES (?, ?)", "params": ["view", "2026-08-16T09:16:01Z"]},
{"sql": "SELECT count(*) AS n FROM events"}
]
}'
{
"results": [
{ "columns": [], "rows": [], "rowsAffected": 1, "lastInsertRowid": 2 },
{ "columns": [], "rows": [], "rowsAffected": 1, "lastInsertRowid": 3 },
{ "columns": ["n"], "rows": [[3]], "rowsAffected": 0, "lastInsertRowid": null }
],
"epoch": 1,
"txid": 3,
"durability": "durable",
"waitedMs": 119
}

Note that three statements cost the same ~120 ms as one. This is the single most useful performance fact in these docs: the latency is one round trip to object storage per batch, not per statement. Ten inserts in a batch cost 120 ms; ten inserts in ten requests cost 1.2 seconds.

  • Durability — what durable promises, and the one case where the answer is “we do not know”.
  • Errors and retries — every error this API returns, and which ones to retry.
  • SQL support — what SQLite features are available and which statements are refused.
  • Client examples — TypeScript, Python, and Go, with retries wired in.