---
title: Views, ordering and pagination
description: "Two kinds of list page differently, a view holds filters and sorts, a cursor carries the ordering that produced it, and not every field can be sorted on."
---

## Paging

Two kinds of list exist, and they page differently.

Most lists — products, price books, quotes — take `limit` and `after`, and answer `nextCursor`. Pass `nextCursor` back as `after` for the next page, and stop when it is `null`. `nextCursor` is the id of the last row on the page.

`limit` runs from 1 to 100 and defaults to 50 on both kinds of list.

Record lists take more: `limit`, `dir`, `orderBy` or `view`, `relatedTo` with `via`, and `cursor` or `after`. Four combinations are refused with `400`, and they cannot be expressed in a parameter list:

- `via` requires `relatedTo`
- `orderBy` cannot be combined with `view`
- `cursor` requires one of `orderBy` or `view`
- `after` cannot be combined with either — page an ordered query with `cursor`

**A cursor belongs to the ordering that produced it.** The ordering — the sort, the direction, the related-record filter, the view — travels inside the cursor. Replay a cursor against a changed view and the request fails loudly with `cursor_ordering_mismatch` rather than answering a page that looks right and is not. Fetch from the first page again when the ordering changes.

**`total` is counted on the first page only.** A request with no `after` and no `cursor` answers a count; every later page answers `total: null`. The count is paid once rather than on every page, so hold the first page's figure if you want to show one.

## A view is a filter and a sort together

A view holds conditions, how they combine, and sorts: at most ten conditions and at most two sorts. `match` is `all` or `any` and defaults to `all`, so an unqualified view narrows rather than widens.

A condition names a field, an operator, and — depending on the operator — a value. There are thirty-three operators in four families, and the family decides the shape of the condition.

| Family | Count | Takes | Examples |
| --- | --- | --- | --- |
| Unary | 15 | no value | `isEmpty`, `isTrue`, `isToday`, `isThisMonth`, `isPast` |
| Value | 13 | exactly one | `contains`, `is`, `gt`, `before`, `onOrAfter`, `has` |
| List | 4 | a set | `isAnyOf`, `isNoneOf`, `hasAnyOf`, `hasNoneOf` |
| Range | 1 | two bounds | `isBetween` |

**An operator belongs to a field kind, not to the API.** A text field takes the text operators, a number field the numeric ones, and the sets barely overlap:

| Field kind | Operators it accepts |
| --- | --- |
| `text`, `longtext`, `email`, `url`, `phone`, `user`, the record `name` | the text set — `contains`, `is`, membership, emptiness |
| `decimal`, `currency`, `percent`, `formula` | the numeric set |
| `date`, `datetime`, `updated_at` | the date set, including the relative operators above |
| `select` | the select set |
| `multi_select` | `has`, `hasAnyOf`, `hasNoneOf`, `isEmpty`, `isNotEmpty` |
| `relation` | `has`, `isEmpty`, `isNotEmpty` |
| `boolean` | `isTrue`, `isFalse`, `isEmpty` |
| `address` | `contains`, `isEmpty`, `isNotEmpty` |
| `rollup`, `days_since` | **none** — they have no column to filter on |

A combination outside that table is refused with `filter_unsupported_for_field`, not ignored, so read the field's kind before building a condition from user input.

## Not every field can be sorted on

Sorting needs a total order the database can produce and a single value a cursor can carry. Five kinds have neither:

- `relation`, `multi_select` and `address` hold no one value to order by.
- `rollup` and `days_since` have no column at all; the client computes them.

Everything else sorts. Ordering by one of those five is refused with `order_field_unsupported`, for the same reason they cannot appear in a cursor.
