REST API

Every collection has an API the moment it exists. The browser client (sluurp.js) is a thin layer over it, so anything the client does, a script or another program can do with plain HTTP.

A request is made as whoever its token says: Authorization: Bearer <token>, from signing in. Without one it is made as a visitor. Either way, the collection’s rules decide what it may read and write.

Records

Method and pathWhat it does
GET /api/collections/{name}/recordsA page of records
POST /api/collections/{name}/recordsCreate one; answers 201 and the record
GET /api/collections/{name}/records/{id}One record
PATCH /api/collections/{name}/records/{id}Change the fields given, and only those
DELETE /api/collections/{name}/records/{id}Delete it, into the recycle bin; answers { "bin": … }
GET /api/collections/{name}/aggregateCount, sum, average, minimum or maximum, by group

A list takes these in its query string:

Parameter
filterWhich records: status = "open" && priority > 2 (see below)
sortFields, comma separated, - for descending: -created,title
page, perPageWhich page, and how many on it
searchWords to find in the collection’s searchable fields
asOfThe collection as it stood then, for one that keeps history: 2026-06-30
near, onRows closest in meaning to these words first (Search by meaning); on names the fields compared
GET /api/collections/grades/records?filter=value%20%3E%3D%208&sort=-date&perPage=20
Authorization: Bearer eyJ…

A page answers with the records and where they are in the whole: items, page, perPage, totalItems, totalPages.

Filters

A small language, compiled into the query, so a filter never reaches the database as text of its own. Field names are checked against the collection, and values are always bound.

Compare= != > >= < <=
Contains, or doesn’t~ !~: title ~ "trip"
Combine&& || and parentheses
Values"text" or 'text', numbers, true, false, null
(status = "open" || status = "waiting") && priority >= 2 && title !~ "test"

Search by meaning

near=museum trips answers the rows closest in meaning first, each with its _score (1 is the same). It compares the collection’s searchable fields, or the ones named in on=title,body. Rules and filter apply as on any list. See Search by meaning for how it works, and for setting up an embeddings model.

const { items } = await sluurp.collection("notes").list({ near: "museum trips" });

Counting and summing

GET /api/collections/{name}/aggregate takes op (count, sum, avg, min, max), field for all but count, group (a field to group by, and more to group further), and filter and asOf as a list does:

GET /api/collections/grades/aggregate?op=avg&field=value&group=subject

History and the recycle bin

For a collection that keeps its history:

Method and path
GET …/records/{id}/historyEvery version of a record, newest first, with who and why
POST …/records/{id}/history/{seq}/restorePut a version back
GET /api/collections/{name}/changes?since=NEvery change since the one numbered N, in order, to follow along

A write can say why it was made, in an X-Sluurp-Reason header, and the reason is kept with the version. A deleted record goes into the recycle bin, and POST /api/bin/{bin}/restore puts it back, with what the delete took with it.

Files

Method and path
GET /api/files/{collection}/{id}/{field}The file. For an image, ?w=, ?h=, ?fit=, ?format= and ?q= resize and convert it
POST /api/files/{collection}/{id}/{field}Upload one, as multipart form data in a part called file
DELETE /api/files/{collection}/{id}/{field}Remove it

Signing in

Method and path
POST /api/collections/{name}/auth-with-password{ "identity", "password" }: answers a token and the record
POST /api/collections/{name}/auth-refreshA fresh token for the one sent
POST /api/collections/{name}/signupMake an account, where the collection allows it
POST /api/collections/{name}/request-password-resetMail a link to set a new password

Errors

An error is a status and a sentence: { "status": 404, "message": "record grades/x not found" }. 400 is a request that doesn’t make sense, 401 needs signing in, 403 is refused by a rule, 404 isn’t there (or isn’t there for you), 409 conflicts with what exists, and 429 is too much at once: try again shortly.

And more

Several writes as one, all or none: POST /api/batch. A server function: GET or POST /api/fn/{name} (Server functions). Live changes, over one WebSocket: Sync. Events from outside: POST /api/events/{name}.