Filtering, sorting and paging
Query JSON records with a small filter language, sort on any field, search text and page through results with cursors.
Listing records takes query parameters. Everything here works on
GET …/records and on public collections.
Filtering
filter takes a small expression. It reads close to how you would say it:
data.done == false
data.priority in ["high", "medium"]
data.tags contains "typescript"
data.title startsWith "Ship"
data.assignee.name == "Ayush"
createdAt >= "2026-01-01T00:00:00Z" and data.done == false
(data.a == 1 or data.b == 2) and not data.archived existsRemember to URL-encode it. With curl, -G --data-urlencode does it for you:
curl -s -G "…/records" -H "Authorization: Bearer $BAAS_KEY" \
--data-urlencode 'filter=data.done == false' \
--data-urlencode 'limit=20'const query = new URLSearchParams({ filter: 'data.done == false', limit: '20' });
const res = await fetch(`${BASE}/workspaces/${WS}/collections/todos/records?${query}`, {
headers: { Authorization: `Bearer ${KEY}` },
});What you can compare
| Your fields | data.name, data.profile.city, data.items[0].name, data["first name"] |
| Built-in fields | id, createdAt, updatedAt, version, schemaVersion, createdBy, updatedBy |
| Values | "text", numbers, true, false, null, lists like ["a","b"] |
| Joining | and, or, not, and parentheses |
Operators
| Operator | Meaning |
|---|---|
== | Equal |
!= | Not equal, or the field is missing |
> >= < <= | Numbers compare as numbers; text compares alphabetically (so ISO dates work) |
in [...] / not in [...] | Is (or is not) one of these |
contains | An array contains this value |
startsWith | Text begins with this |
like / ilike | SQL-style patterns with %; ilike ignores case |
exists / not exists | The field is present at all |
A filter is parsed and turned into a safe database query, so text from your users cannot
be used to attack the database. A filter the parser does not understand returns
400 INVALID_FILTER and tells you which character it stopped at.
Searching text
q searches text anywhere in the record; searchFields narrows it down:
curl -s -G "…/records" -H "Authorization: Bearer $BAAS_KEY" \
--data-urlencode 'q=invoice' \
--data-urlencode 'searchFields=title,description'It is a case-insensitive "contains", meant for a search box — not a ranked full-text engine.
Sorting
--data-urlencode 'sort=-createdAt' # newest first (the default)
--data-urlencode 'sort=data.priority' # by one of your own fieldsPut - in front for descending. You can sort by createdAt, updatedAt, version,
id, or any data. path.
Paging
Results come back one page at a time with a cursor. Keep following nextCursor until it
is null:
let cursor = null;
const all = [];
do {
const query = new URLSearchParams({ limit: '100' });
if (cursor) query.set('cursor', cursor);
const res = await fetch(`${BASE}/workspaces/${WS}/collections/todos/records?${query}`, {
headers: { Authorization: `Bearer ${KEY}` },
});
const page = await res.json();
all.push(...page.data);
cursor = page.pagination.nextCursor;
} while (cursor);Cursors are safer than page numbers: no record is skipped or shown twice when rows are
added while you page. A cursor belongs to the sort it was made with, so do not change
sort halfway through.
Default page size is 50, maximum 200.
Fetching less
select returns only the fields you name, which keeps responses small:
--data-urlencode 'select=title,done'The built-in fields (id, version, timestamps) always come back.
Date ranges and the trash
| Parameter | Meaning |
|---|---|
createdAfter, createdBefore | ISO 8601 range on creation time |
updatedAfter, updatedBefore | The same for last change |
deleted | exclude (default), include, or only for the trash |
Making queries fast
If you filter on the same field constantly, add an index for it in the collection's
Indexes tab. Indexes are built in the background and help ==, in and range filters on
that path. Five per collection.
Query limits
| Filter length | 2,000 characters |
| Conditions per filter | 20 |
| Nesting depth | 5 |
Values in one in [...] | 50 |
| Depth of a field path | 8 segments |
select fields | 25 |
Next steps
View this page as Markdown — handy for copying into an editor or an AI assistant.