Documents
Write, update, delete, read, import.
Write documents
/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}}]} });{
"written": 2,
"deleted": 0,
"version": 3,
"stored_bytes": 2817,
"created": false
}Update documents
/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
/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} });{
"written": 0,
"deleted": 1,
"version": 4,
"stored_bytes": 2817,
"created": false
}Get a document
/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" } });{
"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
/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 -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{
"written": 2,
"deleted": 0,
"version": 5,
"stored_bytes": 4096,
"rows": 2,
"took_ms": 12,
"created": false
}