API reference

Documents

Write, update, delete, read, import.

Kept in this tab only and sent only to api.skimmerdb.com. Requests run against your account and count as usage. Make a key.

Write documents

POST /v1/namespaces/{name}/docs metered: write

Put (insert or replace) and delete in one request. Answers once the write is stored in object storage (about 250 ms); searchable on this server at once, everywhere within a second; wait: true answers once it's searchable here. A namespace that doesn't exist is made by the write (created: true in the answer): with vectors of the first vector's length and cosine when the documents have vectors, else text only (create it first for euclidean); past the plan's namespace count, 402 namespace_limit. Metered as documents written and bytes written (see Usage).

Parameters

namestring · pathrequiredThe namespace's name.

Body application/json

docsDocument[]Documents to put (insert or replace).
docs[].idstringrequiredAny string up to 512 bytes, one line (slashes allowed). Writing an id again replaces the document, so use a stable natural key (ticket-1042, docs/install.md#3, agent-1/run-42/step-3): retries are then safe.
docs[].textstringWhat q searches (exact text and regex): up to 2 MB. May be left out when the document has a vector.
docs[].metaobjectMetadata: a JSON object of strings, numbers and booleans you filter, group and sort by (up to 16 KB); its top-level keys are SQL columns. Keep it flat (nested objects and arrays can't be filtered), one type per key, times as integer seconds (ranges and DATE_TRUNC need numbers). Not searched by q: put searchable words in text.
docs[].vectornumber[]An embedding: as many numbers as the namespace's dims.
docs[].vector_b64stringThe vector as base64 of little-endian float32s (smaller than JSON numbers).
deletesstring[]Ids to delete.
waitbooleanAnswer only once the write is searchable on this server (it is durable either way). Default false.

Answer

createdbooleanThe namespace didn't exist: this write made it (with vectors of the first vector's length, cosine, when the documents have vectors; else text only). Check it to catch a mistyped name.
writteninteger
deletedinteger
versioninteger
stored_bytesinteger

Status codes

200OK
400Malformed: not the JSON expected.
401No key, or an unknown or revoked one.
402The free plan's included usage this period is used up (queries, query CPU, object-storage reads, bytes returned, writes or storage; the message says which), or its namespace count or daily namespace creations are reached (code namespace_limit): upgrade to pro.
403The key's scope or namespaces don't allow it, or the tenant is suspended.
404No such namespace, document, key or tenant (or one this key may not see: the same answer).
413Too big: a request's documents (64 MB), an import (2 GB), or what a SQL query would read.
422Invalid: the message says why (code invalid with fields: which input fields; query_timeout: past the plan's time limit; scan_refused: send scan: true).
429Over the tenant's or the key's rate, concurrency or cap, or writes arriving faster than the namespace commits them (code write_busy): retry after Retry-After seconds.
502Billing (Stripe) is unavailable right now.
503Object storage failed: retry (nothing was written, unless the code is write_unknown: then it may have been; retrying a write is safe).
507The tenant's storage quota is full.
curl -s https://api.skimmerdb.com/v1/namespaces/support/docs \
  -H "Authorization: Bearer $KEY" \
  -d '{"wait":true,"docs":[{"id":"t-1","text":"Refund requested for order ACME-1042","meta":{"status":"open","priority":3}},{"id":"t-2","text":"Login fails with error E401 on mobile","meta":{"status":"closed","priority":1}}]}'
from skimmerdb import SkimmerDB
from skimmerdb._api.api.documents import write_documents
from skimmerdb._api.models import WriteRequest

db = SkimmerDB()  # SKIMMERDB_KEY
answer = write_documents.sync("support", client=db.api, body=WriteRequest.from_dict({"wait":True,"docs":[{"id":"t-1","text":"Refund requested for order ACME-1042","meta":{"status":"open","priority":3}},{"id":"t-2","text":"Login fails with error E401 on mobile","meta":{"status":"closed","priority":1}}]}))
import { SkimmerDB } from "skimmerdb";
import { writeDocuments } from "skimmerdb/api";

const db = new SkimmerDB(); // SKIMMERDB_KEY
const { data } = await writeDocuments({ client: db.client, path: { name: "support" }, body: {"wait":true,"docs":[{"id":"t-1","text":"Refund requested for order ACME-1042","meta":{"status":"open","priority":3}},{"id":"t-2","text":"Login fails with error E401 on mobile","meta":{"status":"closed","priority":1}}]} });
200 answer
{
  "written": 2,
  "deleted": 0,
  "version": 3,
  "stored_bytes": 2817,
  "created": false
}

Update documents

PATCH /v1/namespaces/{name}/docs metered: write

Text replaced if given, meta merged key by key (null removes a key), the vector kept unless a new one is given. The documents must exist. Metered as documents written and bytes written (see Usage).

Parameters

namestring · pathrequiredThe namespace's name.

Body application/json

docsDocumentPatch[]required
docs[].idstringrequired
docs[].textstringReplaces the text (kept when left out).
docs[].metaobjectMerged key by key; a null value removes the key.
docs[].vectornumber[]An embedding: as many numbers as the namespace's dims.
waitbooleanDefault false.

Answer

createdbooleanThe namespace didn't exist: this write made it (with vectors of the first vector's length, cosine, when the documents have vectors; else text only). Check it to catch a mistyped name.
writteninteger
deletedinteger
versioninteger
stored_bytesinteger

Status codes

200OK
400Malformed: not the JSON expected.
401No key, or an unknown or revoked one.
402The free plan's included usage this period is used up (queries, query CPU, object-storage reads, bytes returned, writes or storage; the message says which), or its namespace count or daily namespace creations are reached (code namespace_limit): upgrade to pro.
403The key's scope or namespaces don't allow it, or the tenant is suspended.
404No such namespace, document, key or tenant (or one this key may not see: the same answer).
413Too big: a request's documents (64 MB), an import (2 GB), or what a SQL query would read.
422Invalid: the message says why (code invalid with fields: which input fields; query_timeout: past the plan's time limit; scan_refused: send scan: true).
429Over the tenant's or the key's rate, concurrency or cap, or writes arriving faster than the namespace commits them (code write_busy): retry after Retry-After seconds.
502Billing (Stripe) is unavailable right now.
503Object storage failed: retry (nothing was written, unless the code is write_unknown: then it may have been; retrying a write is safe).
507The tenant's storage quota is full.
curl -s -X PATCH https://api.skimmerdb.com/v1/namespaces/support/docs \
  -H "Authorization: Bearer $KEY" \
  -d '{"docs":[{"id":"t-1","meta":{"status":"closed","priority":null}}],"wait":true}'
from skimmerdb import SkimmerDB
from skimmerdb._api.api.documents import update_documents
from skimmerdb._api.models import UpdateRequest

db = SkimmerDB()  # SKIMMERDB_KEY
answer = update_documents.sync("support", client=db.api, body=UpdateRequest.from_dict({"docs":[{"id":"t-1","meta":{"status":"closed","priority":None}}],"wait":True}))
import { SkimmerDB } from "skimmerdb";
import { updateDocuments } from "skimmerdb/api";

const db = new SkimmerDB(); // SKIMMERDB_KEY
const { data } = await updateDocuments({ client: db.client, path: { name: "support" }, body: {"docs":[{"id":"t-1","meta":{"status":"closed","priority":null}}],"wait":true} });

Delete documents

POST /v1/namespaces/{name}/delete metered: write

Parameters

namestring · pathrequiredThe namespace's name.

Body application/json

idsstring[]required
waitbooleanDefault false.

Answer

createdbooleanThe namespace didn't exist: this write made it (with vectors of the first vector's length, cosine, when the documents have vectors; else text only). Check it to catch a mistyped name.
writteninteger
deletedinteger
versioninteger
stored_bytesinteger

Status codes

200OK
400Malformed: not the JSON expected.
401No key, or an unknown or revoked one.
402The free plan's included usage this period is used up (queries, query CPU, object-storage reads, bytes returned, writes or storage; the message says which), or its namespace count or daily namespace creations are reached (code namespace_limit): upgrade to pro.
403The key's scope or namespaces don't allow it, or the tenant is suspended.
404No such namespace, document, key or tenant (or one this key may not see: the same answer).
422Invalid: the message says why (code invalid with fields: which input fields; query_timeout: past the plan's time limit; scan_refused: send scan: true).
429Over the tenant's or the key's rate, concurrency or cap, or writes arriving faster than the namespace commits them (code write_busy): retry after Retry-After seconds.
503Object storage failed: retry (nothing was written, unless the code is write_unknown: then it may have been; retrying a write is safe).
curl -s https://api.skimmerdb.com/v1/namespaces/support/delete \
  -H "Authorization: Bearer $KEY" \
  -d '{"ids":["t-2"],"wait":true}'
from skimmerdb import SkimmerDB
from skimmerdb._api.api.documents import delete_documents
from skimmerdb._api.models import DeleteRequest

db = SkimmerDB()  # SKIMMERDB_KEY
answer = delete_documents.sync("support", client=db.api, body=DeleteRequest.from_dict({"ids":["t-2"],"wait":True}))
import { SkimmerDB } from "skimmerdb";
import { deleteDocuments } from "skimmerdb/api";

const db = new SkimmerDB(); // SKIMMERDB_KEY
const { data } = await deleteDocuments({ client: db.client, path: { name: "support" }, body: {"ids":["t-2"],"wait":true} });
200 answer
{
  "written": 0,
  "deleted": 1,
  "version": 4,
  "stored_bytes": 2817,
  "created": false
}

Get a document

GET /v1/namespaces/{name}/docs/{id} metered: query

One whole, with its vector. Ids may hold slashes (the rest of the path is the id). Counts as one query (metered; see Usage).

Parameters

namestring · pathrequiredThe namespace's name.
idstring · pathrequiredThe rest of the path (may hold slashes).

Answer

idstring
textstring
metaobject
vectornumber[]
written_atstring
temperaturestringHow warm the namespace was: warm (open), local (opened from the server's disk) or cold (opened from object storage).
store_getsintegerObject-storage reads this query made (0 when warm).
store_bytesintegerBytes those reads fetched.
cpu_msnumberCPU the query used (metered, not billed).
timed_outbooleanThe query ran past the plan's time limit (query_seconds): the answer is partial.

Status codes

200OK
400Malformed: not the JSON expected.
401No key, or an unknown or revoked one.
402The free plan's included usage this period is used up (queries, query CPU, object-storage reads, bytes returned, writes or storage; the message says which), or its namespace count or daily namespace creations are reached (code namespace_limit): upgrade to pro.
403The key's scope or namespaces don't allow it, or the tenant is suspended.
404No such namespace, document, key or tenant (or one this key may not see: the same answer).
422Invalid: the message says why (code invalid with fields: which input fields; query_timeout: past the plan's time limit; scan_refused: send scan: true).
429Over the tenant's or the key's rate, concurrency or cap, or writes arriving faster than the namespace commits them (code write_busy): retry after Retry-After seconds.
503Object storage failed: retry (nothing was written, unless the code is write_unknown: then it may have been; retrying a write is safe).
curl -s https://api.skimmerdb.com/v1/namespaces/support/docs/t-1 \
  -H "Authorization: Bearer $KEY"
from skimmerdb import SkimmerDB
from skimmerdb._api.api.documents import get_document

db = SkimmerDB()  # SKIMMERDB_KEY
answer = get_document.sync("support", "t-1", client=db.api)
import { SkimmerDB } from "skimmerdb";
import { getDocument } from "skimmerdb/api";

const db = new SkimmerDB(); // SKIMMERDB_KEY
const { data } = await getDocument({ client: db.client, path: { name: "support", id: "t-1" } });
200 answer
{
  "id": "t-1",
  "text": "Refund requested for order ACME-1042",
  "meta": {
    "priority": 3,
    "status": "open"
  },
  "written_at": "2026-10-05T01:30:12Z",
  "temperature": "warm",
  "store_gets": 0,
  "store_bytes": 0,
  "cpu_ms": 0.03,
  "timed_out": false
}

Bulk import a file

POST /v1/namespaces/{name}/import metered: write

CSV, NDJSON or Parquet as the request body (up to 2 GB and 1M rows), parsed straight into one segment: all rows become visible together, or none. A namespace that doesn't exist is made by the import (text only; created: true). Metered as a write of that many documents and the file's bytes.

Parameters

namestring · pathrequiredThe namespace's name.
formatstring · queryThe file's format.
idstring · queryThe column holding document ids (default: row numbers).
textstring · queryColumns to index as text, comma-separated.
schemastring · queryCSV: a CREATE TABLE giving the columns' types (default: from the header, kinds inferred).

Body application/octet-stream

Answer

writteninteger
deletedinteger
versioninteger
stored_bytesinteger
rowsinteger
took_msnumber
createdbooleanThe namespace didn't exist: this import made it (text only).

Status codes

200OK
400Malformed: not the JSON expected.
401No key, or an unknown or revoked one.
402The free plan's included usage this period is used up (queries, query CPU, object-storage reads, bytes returned, writes or storage; the message says which), or its namespace count or daily namespace creations are reached (code namespace_limit): upgrade to pro.
403The key's scope or namespaces don't allow it, or the tenant is suspended.
404No such namespace, document, key or tenant (or one this key may not see: the same answer).
413Too big: a request's documents (64 MB), an import (2 GB), or what a SQL query would read.
422Invalid: the message says why (code invalid with fields: which input fields; query_timeout: past the plan's time limit; scan_refused: send scan: true).
429Over the tenant's or the key's rate, concurrency or cap, or writes arriving faster than the namespace commits them (code write_busy): retry after Retry-After seconds.
503Object storage failed: retry (nothing was written, unless the code is write_unknown: then it may have been; retrying a write is safe).
507The tenant's storage quota is full.
curl
curl -s -X POST "https://api.skimmerdb.com/v1/namespaces/support/import?format=csv&id=ticket&text=subject,body" \
  -H "Authorization: Bearer $KEY" \
  --data-binary @tickets.csv
200 answer
{
  "written": 2,
  "deleted": 0,
  "version": 5,
  "stored_bytes": 4096,
  "rows": 2,
  "took_ms": 12,
  "created": false
}