← All docs
Raw

Quickstart

Unbase is a zero-config SQL database over HTTP. No signup, no provisioning — one POST gives you a live SQLite-backed project and a bearer token.

1. Create a project

curl -X POST https://api.unbase.dev/v1/projects
{
  "projectId": "unbase_7f3k9q2m1x8a",
  "url": "https://api.unbase.dev/v1/projects/unbase_7f3k9q2m1x8a",
  "token": "unbase_7f3k9q2m1x8a.AbCdEf123...",
  "anonKey": "unbase_7f3k9q2m1x8a.pk.AbCdEf123..."
}

Save the token. It's your project's secret key (a.k.a. "service role"): shown exactly once, it is your credential — there's no separate API key step. Anyone with it can read/write this project. Keep it server-side, never in a browser.

The anonKey is the publishable key — safe to embed in a client app. It doesn't touch your data; it only unlocks the project's Auth endpoints. More on that below.

2. Run some SQL

PROJECT=unbase_7f3k9q2m1x8a
TOKEN="unbase_7f3k9q2m1x8a.AbCdEf123..."

curl -X POST https://api.unbase.dev/v1/projects/$PROJECT/query \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"sql": "CREATE TABLE todos (id INTEGER PRIMARY KEY, title TEXT)"}'

curl -X POST https://api.unbase.dev/v1/projects/$PROJECT/query \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"sql": "INSERT INTO todos (title) VALUES (?)", "params": ["write docs"]}'

curl -X POST https://api.unbase.dev/v1/projects/$PROJECT/query \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"sql": "SELECT * FROM todos"}'
{ "rows": [{ "id": 1, "title": "write docs" }], "rowsRead": 1, "rowsWritten": 0 }

params are standard positional ? placeholders — always use them for user-supplied values instead of string-building SQL.

3. Same thing in JavaScript

const base = "https://api.unbase.dev/v1";

const created = await fetch(`${base}/projects`, { method: "POST" });
const { projectId, token } = await created.json();

async function query(sql, params) {
  const res = await fetch(`${base}/projects/${projectId}/query`, {
    method: "POST",
    headers: { authorization: `Bearer ${token}` },
    body: JSON.stringify({ sql, params }),
  });
  return res.json();
}

await query("CREATE TABLE todos (id INTEGER PRIMARY KEY, title TEXT)");
await query("INSERT INTO todos (title) VALUES (?)", ["write docs"]);
const { rows } = await query("SELECT * FROM todos");
console.log(rows); // [{ id: 1, title: "write docs" }]

4. Don't lose it

Projects created without an account are anonymous and expire in 7 days. Claim yours with an email to keep it forever, on the free plan, at no cost. Claiming is a two-step magic-link flow that proves you control the email address.

Step 1 — request the claim link:

curl -X POST https://api.unbase.dev/v1/claim \
  -d "{\"id\": \"$PROJECT\", \"email\": \"you@example.com\"}"
# => { "sent": true }

This emails a link to you@example.com. (In keyless dev mode, no email is sent and the response is { "sent": false, "devLink": "https://unbase.dev/claim/verify?token=..." } so you can complete the flow inline.)

Step 2 — open the link, which completes the claim via the token in the URL:

curl -X POST https://api.unbase.dev/v1/claim/verify \
  -d "{\"token\": \"<token from the emailed link>\"}"
# => { "projectId": "unbase_...", "accountId": "acct_...", "plan": "free" }

The existing token keeps working after claiming — nothing to rotate.

5. Authenticate your app's users (Auth)

Every project ships with a Supabase-style Auth service for the end users of your app — under ${url}/auth. It uses your project's two keys:

  • The anon key (unbase_....pk.<sig>) is safe to ship in a browser or mobile app. Send it in an apikey header to reach the Auth endpoints. It can't read or write your tables.
  • The secret key (the token from step 1) stays on your server and additionally unlocks the admin Auth endpoints.

Sign a user up (email + password, min 8 chars):

curl -X POST https://api.unbase.dev/v1/projects/$PROJECT/auth/signup \
  -H "apikey: unbase_7f3k9q2m1x8a.pk.AbCdEf123..." \
  -d '{"email": "user@example.com", "password": "hunter2!!"}'

Sign in returns a Session:

curl -X POST https://api.unbase.dev/v1/projects/$PROJECT/auth/signin \
  -H "apikey: unbase_7f3k9q2m1x8a.pk.AbCdEf123..." \
  -d '{"email": "user@example.com", "password": "hunter2!!"}'
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsIn...",
  "tokenType": "bearer",
  "expiresIn": 3600,
  "expiresAt": 1751824800,
  "refreshToken": "v1.MnhK...",
  "user": {
    "id": "user_3m1x8a7f3k9q",
    "email": "user@example.com",
    "emailConfirmedAt": null,
    "createdAt": "2026-07-06T12:00:00Z",
    "lastSignInAt": "2026-07-06T12:00:00Z"
  }
}

Prefer passwordless? POST .../auth/magiclink { "email", "redirectTo"? } emails the user a link; your app then calls POST .../auth/verify { "token" } to get a Session (the user is created on first verify). Refresh an expired access token with POST .../auth/token { "refreshToken" } (single-use rotation), and sign out with POST .../auth/logout { "refreshToken" }.

The accessToken is a plain HS256 JWT signed with your project's own JWT secret, so you can verify end-user tokens directly in your backend. Its claims are { sub: userId, iss: projectId, role: "authenticated", email, iat, exp }. Fetch the signing secret (admin, secret key only) from GET .../auth/settings, and list your users with GET .../auth/users.

6. Store files (Storage)

Every project also gets a private file store, backed by Cloudflare R2, under ${url}/storage/objects. There's no separate setup — upload with a PUT to a key of your choice (keys can contain / to namespace into folders):

curl -X PUT https://api.unbase.dev/v1/projects/$PROJECT/storage/objects/avatars/user.png \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: image/png" \
  --data-binary @avatar.png
{ "key": "avatars/user.png", "size": 48213, "contentType": "image/png", "createdAt": 1751824800000, "updatedAt": 1751824800000 }

Download it — the body is the raw bytes, with Content-Type set to whatever you uploaded it with:

curl https://api.unbase.dev/v1/projects/$PROJECT/storage/objects/avatars/user.png \
  -H "Authorization: Bearer $TOKEN" -o avatar.png

List everything in the project, or delete an object:

curl https://api.unbase.dev/v1/projects/$PROJECT/storage/objects -H "Authorization: Bearer $TOKEN"
curl -X DELETE https://api.unbase.dev/v1/projects/$PROJECT/storage/objects/avatars/user.png -H "Authorization: Bearer $TOKEN"

Uploads and deletes require the secret key; downloads and listing also accept the anon key (as apikey header or Authorization: Bearer), so a client app can fetch files directly without a server round-trip. There's no public/anonymous URL — every read is authenticated. Objects are capped at 100 MB each and count against your plan's file-storage quota, separate from your SQL database's own storage limit.

Next steps

  • migrating.md — moving from Neon, Supabase, or another database to Unbase.
  • llms.txt — dense API reference, ideal for feeding to an AI agent.
  • openapi.yaml — full OpenAPI spec (import into Postman/Swagger).
  • Batch multiple statements atomically with /v1/projects/:id/batch.
  • Check /v1/projects/:id/usage for current-month row counts and plan status.
  • Back up anytime with /v1/projects/:id/export (plain-SQL dump, replayable with sqlite3).
  • Store files with /v1/projects/:id/storage/objects (see Storage above).