diff --git a/reference/rest/headers.md b/reference/rest/headers.md
index 818398eb..3b880d4a 100644
--- a/reference/rest/headers.md
+++ b/reference/rest/headers.md
@@ -20,6 +20,8 @@ These headers are included in all Harper REST API responses:
| `etag` | `"abc123"` | Encoded version/last-modification time of the returned record. Used for conditional requests. |
| `location` | `/MyTable/new-id` | Returned on `POST` responses. Contains the path to the newly created record. |
+Collection responses to a [count request](./querying.md#pagination-and-total-count) additionally include `Content-Range`, `Range-Unit`, and `Preference-Applied` ().
+
## Request Headers
### Content-Type
@@ -67,6 +69,19 @@ Accept-Encoding: gzip, br
Compression is particularly effective for JSON responses. For binary formats like CBOR, compression provides diminishing returns compared to the already-compact encoding.
+### Prefer
+
+
+
+Opt in to a total match count on a collection `GET`/`HEAD`, returned via the `Content-Range` response header for pagination:
+
+```http
+GET /Product/?category=software&limit(0,25)
+Prefer: count=exact
+```
+
+Accepts `count=exact` (precise, scans the full matched set) or `count=estimated` (fast, approximate). See [Pagination and Total Count](./querying.md#pagination-and-total-count) for the full request/response contract.
+
### Authorization
Credentials for authenticating requests. See [Security Overview](../security/overview.md) for details on supported authentication mechanisms (Basic, JWT, mTLS).
diff --git a/reference/rest/overview.md b/reference/rest/overview.md
index 74b755f7..37c94502 100644
--- a/reference/rest/overview.md
+++ b/reference/rest/overview.md
@@ -33,8 +33,11 @@ rest: true
rest:
lastModified: true # enables Last-Modified response header support
webSocket: false # disables automatic WebSocket support (enabled by default)
+ exactCount: true # opt in to Prefer: count=exact scans (off by default; otherwise served as estimates)
```
+ `exactCount` (default `false`) controls whether the [pagination total-count](./querying.md#pagination-and-total-count) feature honors `Prefer: count=exact`. Because an exact count scans the full matched set, it is off by default; set this to `true` to enable exact counts for this application's REST interface. A `count=exact` request is otherwise served as a cheaper estimate.
+
## URL Structure
The REST interface follows a consistent URL structure:
diff --git a/reference/rest/querying.md b/reference/rest/querying.md
index b05a19c0..139176ea 100644
--- a/reference/rest/querying.md
+++ b/reference/rest/querying.md
@@ -173,6 +173,62 @@ GET /Product/?rating=gt=3&sort(+name)
GET /Product/?sort(+rating,-price)
```
+## Pagination and Total Count
+
+
+
+Use `limit(start,end)` to page through a collection, and opt in to a total match count with the `Prefer` request header (`Prefer: count=exact`) so a client can render pagination (for example "1-25 of 1,234") without a second request.
+
+Counting is opt-in: without the header, no count is computed and no count headers are returned. It applies only to `GET`/`HEAD` requests and requires a `limit()` within a supported page size — a request with no `limit()`, an oversized one, or a non-numeric one is served normally with no count headers, since counting an unbounded page would defeat the point of paging.
+
+### Requesting a count
+
+Send a `Prefer` header on a `GET` (or `HEAD`) request to a collection:
+
+| Value | Meaning |
+| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `count=exact` | The exact number of matching records. Scans the full matched set, so it is opt-in per mount (see below) and served as an estimate unless enabled. |
+| `count=estimated` | A fast planner/table estimate. Cheap, approximate. |
+
+```http
+GET /Product/?category=software&limit(0,25)
+Prefer: count=exact
+```
+
+### Count response headers
+
+The count is returned in [RFC 7233](https://datatracker.ietf.org/doc/html/rfc7233)-style response headers alongside the page body:
+
+| Header | Example | Description |
+| -------------------- | ----------------- | --------------------------------------------------------------------------------------------- |
+| `Content-Range` | `items 0-24/1234` | The 0-based, inclusive range of records returned (`start-end`) out of the total matching set. |
+| `Range-Unit` | `items` | The unit used by `Content-Range`. |
+| `Preference-Applied` | `count=exact` | The count mode the server applied (`exact` or `estimated`). |
+
+```http
+HTTP/1.1 200 OK
+Content-Range: items 0-24/1234
+Range-Unit: items
+Preference-Applied: count=exact
+```
+
+The response status is always `200` — `Content-Range` is informational (Harper does not use `206 Partial Content`). A `HEAD` request with `Prefer: count=` returns the count headers with no body, a cheap way to ask "how many match?" without transferring the page. When CORS is enabled, these three headers are added to `Access-Control-Expose-Headers` so browser clients can read them cross-origin.
+
+### Unavailable totals
+
+The total is reported as `*` (for example `Content-Range: items 0-24/*`) when it cannot be produced — an exact scan that reaches its internal work limit, or a query with no cardinality estimate (for example a `!=` or `=ct=` (contains) condition). `Preference-Applied` still echoes the requested mode, so an unavailable total (`.../*`) is distinct from a request that asked for no count.
+
+### Enabling exact counts
+
+Because an exact count scans the full matched set, it is **off by default**. Enable it in an application's REST configuration via the [`exactCount` option](./overview.md#configuration):
+
+```yaml
+rest:
+ exactCount: true
+```
+
+Without it, a `count=exact` request is served as an estimate (the response reports `Preference-Applied: count=estimated`). Estimated counts are always available.
+
## Relationships and Joins