API reference
Every database is an HTTP endpoint
The whole platform is plain HTTPS with JSON in and JSON out. No SDK, no connection string, no client library to keep in sync. Base URL https://api-repogrid.vercel.app.
Quick start
Three calls: create a database, mint a key, then run a statement. Create the database and the key from the dashboard, or with the session-authenticated endpoints.
- Create a database in the dashboard, or
POST /api/dbswith a dashboard session. You get an id likedb_c1c00d9e8fcb9c15. - Create an API key under API Keys. The full value is returned once; store it somewhere safe.
- Send a request with
Authorization: Bearer db_….
curl "https://api-repogrid.vercel.app/api/dbs/DATABASE_ID/sql" \
-H "Authorization: Bearer db_your_key_here" \
-H "Content-Type: application/json" \
-d '{"sql":"SELECT id, email FROM users LIMIT 20"}'const res = await fetch("https://api-repogrid.vercel.app/api/dbs/" + dbId + "/sql", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.REPOGRID_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
sql: "SELECT id, email FROM users WHERE active = ?",
params: [true],
}),
});
const { result } = await res.json(); // /sql wraps everything in "result"
const { kind, fields, rows, changes, durationMs } = result;
console.log(kind, fields.map((f) => f.name), rows, changes, durationMs + "ms");Authentication
One header, three levels of access. The `any` label below means a dashboard session or an API key both work.
Authorization: Bearer db_your_key_here
- any
Reads accept a session cookie or an API key, so the dashboard and your app can share endpoints.
- apikey
Every write needs a real API key. A session is not enough, so a stray tab cannot mutate your data.
- session
Management: create, rename and delete databases, and manage keys. Session only.
Discovering a database
Everything the platform knows about your account and your databases.
/api/health
Liveness probe. Returns ok, service name, time and supported types.
/api/dbs
List the databases in the signed-in account.
/api/dbs/:id/schema
Tables, columns and indexes for a relational database.
/api/dbs/:id/collections
List a JSON database's collections and their metadata (names, indexes, timestamps).
/api/dbs/:id/versions
Commit history, newest first, for any database.
/api/dbs/:id/stats
Versions, record counts, repository size, cache and rate-limit usage.
/api/keys
API keys for the account. Full key values are only returned once, at creation.
/api/dbs
Create a relational or JSON database and its backing repository.
Relational data
SQLite through POST /sql, plus paginated reads for table browsers. Statements accept bound params so you never concatenate user input.
/api/dbs/:id/tables
List tables with row counts.
/api/dbs/:id/tables/:table/rows
Read rows with limit, offset and optional version.
/api/dbs/:id/sql
Run one statement. DDL, DML and reads, with bound params. Accepts a session or an API key.
curl "https://api-repogrid.vercel.app/api/dbs/DATABASE_ID/sql" \
-H "Authorization: Bearer db_your_key_here" \
-H "Content-Type: application/json" \
-d '{"sql":"SELECT id, email FROM users LIMIT 20"}'curl -G "https://api-repogrid.vercel.app/api/dbs/DATABASE_ID/tables/users/rows" \
-H "Authorization: Bearer db_your_key_here" \
--data-urlencode "limit=20" \
--data-urlencode "offset=0"Response shape
{
"result": {
"kind": "select", // select | dml | ddl | other
"fields": [ { "name": "id", "declaredType": "INTEGER" } ],
"rows": [ { "id": 1, "email": "ada@example.com" } ],
"changes": 0, // rows modified (dml/ddl only)
"ddlChanged": false,
"lastInsertRowid": null,
"durationMs": 3
}
}
// POST /api/dbs/:id/tables/:name/rows returns positional arrays, not objects:
// fields: ["id", "email"], rows: [[1, "ada@example.com"]]result.fields is an array of { name, declaredType } objects; result.rows are objects keyed by column. The structured /rows endpoint returns columns as strings and each row as a value array.
JSON documents
Collections hold documents with stable ids and revisions. Reads work like the table browser; writes are commits.
/api/dbs/:id/collections/:name/docs
Query documents with filter, sort, fields, limit, offset or page.
/api/dbs/:id/collections/:name/docs/:docId
Read a single document by id.
/api/dbs/:id/collections/:name/docs
Insert one document or a batch of up to 1000.
/api/dbs/:id/collections/:name/docs/:docId
Update, upsert or unset fields on a document.
/api/dbs/:id/collections/:name/docs/:docId
Delete a document by id.
curl -G "https://api-repogrid.vercel.app/api/dbs/DATABASE_ID/collections/customers/docs" \
-H "Authorization: Bearer db_your_key_here" \
--data-urlencode 'filter={"plan":"pro","country":{"$in":["DE","AT"]}}' \
--data-urlencode 'sort={"createdAt":-1}' \
--data-urlencode 'fields=name,plan,country' \
--data-urlencode "limit=20"curl -X PATCH \
"https://api-repogrid.vercel.app/api/dbs/DATABASE_ID/collections/customers/docs/ada@example.com" \
-H "Authorization: Bearer db_your_key_here" \
-H "Content-Type: application/json" \
-d '{"$set":{"plan":"enterprise"},"$unset":["legacy_tag"]}'Filtering, sorting and paging
Pass filter and sort as URL-encoded JSON. Missing fields never match a condition, so a filter is also an existence check.
filter
URL-encoded JSON object of conditions, combined with $and by default.
sort
URL-encoded JSON, e.g. {"createdAt":-1}. No dash-prefix shorthand.
fields
Projection, comma separated or a JSON array of dot-paths.
limit / offset
Classic paging, limit up to 1000. Default 50 relational / 100 JSON.
page
1-based page index, as an alternative to offset.
version
Read a past commit: full sha or rev-N. Omit for the newest.
| Operator | Use |
|---|---|
| $eq / $ne | equality and inequality |
| $gt / $gte / $lt / $lte | range comparisons |
| $in / $nin | membership in a list |
| $regex | pattern matching on strings |
| $exists | field presence |
| $and / $or / $nor / $not | combine conditions |
Reading a past version
Every write is a commit, so any read can be pinned to a past state without restoring or branching anything.
curl "https://api-repogrid.vercel.app/api/dbs/DATABASE_ID/versions" \
-H "Authorization: Bearer db_your_key_here"
curl "https://api-repogrid.vercel.app/api/dbs/DATABASE_ID/tables/users/rows?version=COMMIT_SHA" \
-H "Authorization: Bearer db_your_key_here"Use the full 40-character sha returned by /versions. Short shas and the string latest are rejected ("400 VALIDATION_ERROR"); omit version to read the newest commit. Pinned versions are read-only — writes always land on the latest commit.
Errors and retries
Errors are JSON with a stable code, so you can branch on the code instead of the message.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The sql field is required."
}
}VALIDATION_ERROR / QUERY_ERROR / SCHEMA_ERROR / DOCUMENT_ERROR
Malformed body, filter or parameter; or a rejected statement.
UNAUTHORIZED / INVALID_API_KEY
Missing, expired, revoked or mis-scoped credential.
DATABASE_BLOCKED
The database's API kill-switch is on (dashboard only to unblock).
NOT_FOUND / DATABASE_VERSION_NOT_FOUND
Unknown database, collection, document, or commit sha.
CONCURRENT_MODIFICATION
Another write landed first. Re-read and retry.
PAYLOAD_TOO_LARGE
Request body over 4 MB. Batch or shrink.
RATE_LIMITED / GITHUB_RATE_LIMIT
GitHub API budget exhausted. Back off and retry.
GITHUB_API_ERROR / GITHUB_UPSTREAM_UNAVAILABLE
GitHub hiccup. Retry via exponential backoff.
Retry 429 and 502 with exponential backoff. Both mean GitHub was busy, not that your request was wrong.
Limits worth knowing
No surprises, just ceilings.
- Relational reads default to 50 rows per page, JSON reads to 100; both accept up to 1000 per request.
- Batches: document inserts accept up to 1000 documents per request, all in one commit.
- Request bodies and single documents are capped at roughly 4 MB.
- SQL is one statement per request. Use bound params instead of string interpolation.
- Every write is one commit, so a write request is not a bulk import and consumes GitHub rate-limit budget.
- Reads are cached for about a minute, namespaced per database, and invalidated on every write or restore.