Skip to content
Browse docs

REST API reference

The Layerbase Cloud API lets you create, list, and query databases over HTTPS, and manage the personal API keys that authenticate every request. This page documents each public endpoint: the auth header it expects, the fields it accepts, the shape it returns, and the status codes it can send back. The Docs overview has the quickstart if you are starting from scratch.

Base URL

Each database uses the HTTPS API URL of the server hosting it. Copy the endpoint from that database's Connect panel under HTTP API key or Snippets. Keep its server URL, database UUID, and API key together; a key sent to a different server can return 401. The examples below store your server's base URL in $LAYERBASE_API_URL. The original shared server's URL below is only correct for databases hosted there.

original shared server
https://cloud.layerbase.dev

Authentication

All /v1/* endpoints except GET /v1/engines require a personal API key as a Bearer token. Keys are prefixed sk_ and the full secret is shown exactly once, at creation. A key never expires unless you ask for one that does. Create and manage keys under Personal API keys in cloud settings, or with the key endpoints below.

Authorization header
Authorization: Bearer sk_<your-key>

Keys have one of two scopes. An account key (the default) can reach every endpoint for your account. A database key is pinned to one database: it may only call /v1/databases/<that-id> and its sub-paths, and returns 403 with code: "scoped_key_out_of_scope" anywhere else, including GET /v1/databases. The id in that path is the database's UUID, the id field returned by GET /v1/databases and shown in the dashboard, not its hostname: a scoped key sent to /v1/databases/my-db-fox-hedge/query is refused because that path is outside the key's scope. The per-database key in the Connect panel is a database key; keys created under Personal API keys are account keys.

An sk_ key is the only credential the authenticated /v1/* endpoints accept (the unauthenticated ones noted above take no credential at all). In particular, the session token the CLI stores after the browser login flow, lbase login with no flags, is a browser-issued JWT that works only against the layerbase.com/api/cli/* proxy routes, and sending it here returns 401 Invalid API key. Storing a key with lbase login --api-key sk_... is a different flow and does leave the CLI holding a valid sk_ credential. If you are debugging that error while signed in to the CLI, that mismatch is the usual cause: create a key under Personal API keys and send it instead. See the CLI guide for how the two credentials divide up.

Expiring keys

A key can be given a lifetime at creation. Send ttlSeconds on POST /v1/api-keys as a whole number of seconds between 600 (10 minutes) and 2592000 (30 days), and the key stops working at that moment on its own. This is what a CI pipeline wants: the job mints a credential for one run, and the credential dies whether or not the job reaches the step that would have revoked it. Omit ttlSeconds and the key never expires, which is how every key behaved before this option existed, so nothing already issued changed. The endpoint reference below carries the request shape and a worked example.

Every key the API returns carries expiresAt, an absolute UTC instant or null for a key that never expires: on the key lists, on the create and rotate responses, and in the apiKey block of GET /v1/me, which describes the key that made the call. A ttlSeconds outside the bounds is refused with 400 and code: "invalid_ttl" rather than clamped, so a job that asked for five seconds is never handed a ten-minute key it thinks is already dead.

A request made with an expired key returns 401 with code: "api_key_expired" and the expiry, which is deliberately distinct from the generic invalid-key 401 so a pipeline can tell an aged-out key from a wrong one. Rotation carries the expiry forward: the replacement key inherits the old expiresAt unchanged, so rotating is not a way to extend a short-lived key. Pass ttlSeconds in the rotate body to set a fresh expiry measured from now. Rotating a key that has already expired, without one, returns 410 and code: "api_key_expired" instead of minting a replacement that is dead on arrival.

Request and response format

Requests and responses are JSON, and body fields use camelCase. Send Content-Type: application/json on any request with a body. Errors return { "error": "message" } with a relevant HTTP status; some also add a machine-readable code (for example database_limit_reached or pool_block_required).

200

Success.

201

Created (database, API key).

400

Invalid input: bad engine, name, or body field.

401

Missing, malformed, or unknown API key.

402

Account is suspended for a failed payment. Mutating calls are blocked; GET calls still work.

403

Not allowed: a database-scoped key outside /v1/databases/<its own UUID> (code scoped_key_out_of_scope), an engine your plan cannot create, or an owner-only action.

404

Database or key not found (or not yours).

409

Conflict: name already taken, pool capacity exhausted, or a pool block is required.

423

Database is locked, or an operation is in progress.

429

Your plan's database limit or monthly programmatic-create limit is reached.

503

Temporarily unavailable: engine binary not ready, database waking or archived, or account migrating.

Health and engines

GET/health

Unauthenticated liveness probe. Returns { "status": "ok", "service": "layerbase-cloud" }, always alongside the reconcile and bootReconcile fields that report reconciler progress. Returns 503 with draining or overloaded when the server is shutting down or under memory pressure.

GET/v1/engines

The public engine registry: every engine, its display name, versions, and defaults. No auth required, and the response is a bare JSON array rather than an object wrapper. This is the same data the dashboard create flow reads, so you can use it to build a valid engine and version for a create call.

Databases

GET/v1/databases

Lists the databases you own plus any shared with you through a team. Returns { "limits": { ... }, "counts": { ... }, "databases": [ ... ] }: counts splits primaries from branches, and limits reports your branch allowances. Each entry carries more fields than an ordinary client needs; the stable core is id, name, engine, version, status, host, port, connectionString, an access field of owner or member, the placement fields server / serverPool (the canonical server slug and either shared or dedicated; both null when placement is not reported), and the transient fields expiresAt / transient (see transient databases). App workloads are not databases and never appear here.

list databases
curl $LAYERBASE_API_URL/v1/databases \
  -H "Authorization: Bearer $LAYERBASE_API_KEY"
POST/v1/databases

Provisions a new database. Only engine is required; the rest have defaults.

engine

Required. e.g. postgresql, mysql, redis, valkey, mariadb, mongodb, clickhouse.

version

Optional. Defaults to the engine default from /v1/engines.

name

Optional. Lowercase letter first, then letters/numbers/hyphens/underscores, up to 63 chars. Auto-generated if omitted.

database

Optional. Initial database/schema name. Defaults per engine.

backupPolicy

Optional. 'none' (default), '7d', or '30d'.

keepAlive

Optional boolean. Always-on (skip hibernation). Paid plans only.

storageBlocks

Optional integer 0-20. Extra storage blocks.

ttlHours

Optional integer 1-72. Makes the database transient: an expires_at is stamped and the database is destroyed outright at expiry with no final backup. Ideal for CI. See Transient databases below.

teamId

Optional. Owning team. Defaults to your primary team.

Returns 201 with the new database, including its connection details and a status. Fast engines come back running; slow-start engines (MySQL, MariaDB, ClickHouse, QuestDB, libSQL) come back provisioning and finish in the background, so poll GET /v1/databases/:id until status is running. Redis and Valkey also return restUrl / restToken; MySQL and MariaDB also return psUrl / psUsername / psPassword for their HTTP drivers (see serverless and edge access).

The response also carries the placement fields server (the canonical server slug of the box hosting the database) and serverPool (shared or dedicated). Either may be null while a slow-start engine is still provisioning, or when the host does not report placement.

create a database
curl -X POST $LAYERBASE_API_URL/v1/databases \
  -H "Authorization: Bearer $LAYERBASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"engine": "postgresql", "name": "my-app"}'

Common failures: 400 for an invalid engine or name, 403 for an engine your plan cannot create, 409 when the name is taken or the pool is exhausted, and 429 when you hit your plan's database limit or your monthly programmatic-create limit (see programmatic-create limits).

Transient databases (TTL)

Pass ttlHours (a whole number from 1 to 72) on create to make the database transient. Layerbase stamps an expires_at deadline and a reaper destroys the database outright at expiry: there is no final backup and no archive-warning window, because you asked for a throwaway. This is the recommended primitive for a fresh, disposable database per CI run: a crashed job cannot strand a database against your quota, because it self-destructs at the deadline. Every database in the response carries two extra fields: expiresAt (an ISO timestamp, or null for a durable database) and transient (a boolean).

A transient database still counts against your plan's database quota while it is alive, and it cannot be branched. An out-of-range ttlHours returns 400 with { "code": "invalid_ttl" } (the value is never silently clamped), and branching a transient database returns 400 with { "code": "branch_on_transient_forbidden" }.

create a 2-hour transient database
curl -X POST $LAYERBASE_API_URL/v1/databases \
  -H "Authorization: Bearer $LAYERBASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"engine": "postgresql", "ttlHours": 2}'
GET/v1/databases/:id

Returns one database with the same fields as the list entry plus teamName and stopped_at. Returns 404 if the database does not exist or is not yours. Poll this after a create to watch status settle.

POST/v1/databases/:id/query

Runs a query over HTTP against the database, no driver or open socket required, which makes it the easiest path from serverless and edge runtimes. Send { "query": "SELECT 1" } for SQL engines; the query is capped at 10240 characters. A hibernated database wakes automatically (you may get a 503 with retry_after while it does); an archived one returns 503 with a restore path, and a locked one returns 423. The full request shapes for non-SQL engines and edge examples are in serverless and edge access.

query over HTTP
curl -X POST $LAYERBASE_API_URL/v1/databases/<id>/query \
  -H "Authorization: Bearer $LAYERBASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "SELECT 1"}'

Identity and usage

GET/v1/me

Returns who the key belongs to and your current programmatic-create usage, with no side effects. Use it for a whoami check or to let an agent report your standing before it provisions anything. The apiKey block describes the calling key itself, including its expiresAt (null when it never expires), so a CI job can fail early on a credential that is about to age out.

GET /v1/me response
{
  "user": { "id": "...", "email": "you@example.com", "plan": "pro" },
  "apiKey": {
    "id": "...",
    "name": "ci",
    "prefix": "sk_a1b2",
    "scopeType": "account",
    "scopeId": null,
    "expiresAt": "2026-10-01T00:00:00.000Z"
  },
  "usage": {
    "programmaticCreates": {
      "used": 12,
      "limit": 500,
      "resetsAt": "2026-08-01T00:00:00.000Z"
    }
  }
}

limit is null when your plan is unmetered (a Custom plan on your own servers), and resetsAt is the first instant of next month in UTC.

Programmatic-create limits

Every database create authenticated by an API key counts toward a monthly programmatic-create quota, tracked per calendar month in UTC. This is separate from your plan's database-count quota: it caps how many databases automation may spin up over a month (transient or durable), so a CI loop cannot exhaust shared capacity. Creates you make in the dashboard do not count. The per-plan limits are:

Free5 / month
Solo30 / month
Pro500 / month
Team1,000 / month
Custom (dedicated servers)Unmetered

When you exceed the limit, the create returns 429 before any provisioning work, with a machine-readable body so a script or agent can back off gracefully:

429 over the monthly limit
{
  "code": "programmatic_create_limit_reached",
  "error": "Programmatic create limit reached (5/5 this month). Upgrade your plan for a higher limit: https://layerbase.com/pricing",
  "used": 5,
  "limit": 5,
  "resetsAt": "2026-08-01T00:00:00.000Z"
}

API keys

Manage the personal keys that authenticate the API. The raw secret is returned exactly once, on create and rotate; store it immediately.

GET/v1/api-keys

Lists your active keys. Returns { "keys": [ ... ] } with each key's id, name, prefix, scopeType, createdAt, lastUsedAt, and expiresAt (null for a key that never expires). The secret is never included.

POST/v1/api-keys

Creates a key. Body is optional: name (defaults to default; anything past 80 characters is truncated rather than rejected, and the reserved name web-app returns 400), scopeType (account or database), and scopeId (required when scopeType is database, naming a database you own; one you cannot reach returns 404). Returns 201 with { "key": { ..., "secret": "sk_..." } }. The secret appears only in this response. Key names are unique per scope while the key is active: creating a second key with a name you are already using returns 409 with code: "api_key_name_exists" and the existing key, so retrying a request that timed out cannot silently leave you with two keys under one name. Since the secret is only ever shown once, rotate the existing key to get a new secret, or pick a different name. Revoking a key frees its name. Pass ttlSeconds to mint a key that expires on its own, in whole seconds from now, between 600 (10 minutes) and 2592000 (30 days). This is what a CI job wants: mint a credential at the start of the run and let it die by itself, whether or not the job reaches its cleanup step. The absolute expiry comes back as expiresAt, and a request past it fails 401 with code: "api_key_expired". Omit ttlSeconds and the key never expires, which is how every key behaved before this option existed; a value outside the bounds returns 400 with code: "invalid_ttl" rather than being clamped.

create an API key
curl -X POST $LAYERBASE_API_URL/v1/api-keys \
  -H "Authorization: Bearer $LAYERBASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "ci"}'

# a short-lived key for one CI run: dies on its own after an hour
curl -X POST $LAYERBASE_API_URL/v1/api-keys \
  -H "Authorization: Bearer $LAYERBASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "ci-run-482", "ttlSeconds": 3600}'
POST/v1/api-keys/:id/rotate

Mints a replacement key with the same name and scope and revokes the old one after a 24-hour grace window, so long-running callers can pick up the new secret without an outage. The old secret keeps authenticating for that full window, reported as graceSeconds in the response. Returns 201 with the new key and its one-time secret. Returns 404 if the key is not yours and 410 if it is already revoked. Expiry carries forward: the replacement inherits the old key's expiresAt unchanged, so rotating a short-lived key is not a way to extend it and rotating a key that never expires still gives one that never expires. Send ttlSeconds in the body to set a fresh expiry measured from now. Rotating a key that has ALREADY expired, with no ttlSeconds, returns 410 with code: "api_key_expired" rather than minting a replacement that is dead on arrival.

DELETE/v1/api-keys/:id

Revokes a key immediately. Returns { "ok": true } alongside the revokedKeyHash of the key it retired, and is idempotent (revoking an already-revoked key still returns 200). Any request using that key fails 401 afterward.

The dashboard exposes many more per-database operations (stop, start, backup, restore, branch, firewall, client certificates) over the same /v1/databases/:id/* surface. Those are covered in their own guides, linked below.