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 path | What it does |
|---|---|
GET /api/collections/{name}/records | A page of records |
POST /api/collections/{name}/records | Create 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}/aggregate | Count, sum, average, minimum or maximum, by group |
A list takes these in its query string:
| Parameter | |
|---|---|
filter | Which records: status = "open" && priority > 2 (see below) |
sort | Fields, comma separated, - for descending: -created,title |
page, perPage | Which page, and how many on it |
search | Words to find in the collection’s searchable fields |
asOf | The collection as it stood then, for one that keeps history: 2026-06-30 |
near, on | Rows 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=subjectHistory and the recycle bin
For a collection that keeps its history:
| Method and path | |
|---|---|
GET …/records/{id}/history | Every version of a record, newest first, with who and why |
POST …/records/{id}/history/{seq}/restore | Put a version back |
GET /api/collections/{name}/changes?since=N | Every 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-refresh | A fresh token for the one sent |
POST /api/collections/{name}/signup | Make an account, where the collection allows it |
POST /api/collections/{name}/request-password-reset | Mail 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}.