# GraphQL filters, pagination, and limits

> Encode factQuery filters safely, page with explicit limits and offsets, and stay within time-window, complexity, depth, and token guards.

`factQuery` filters are a JSON document carried inside one GraphQL string.
Always pass them as a variable so your JSON encoder handles the nested escaping.

## Valid filter variables

```graphql
query ShippedOrders($slug: String!, $filters: String!, $offset: Int!) {
  factQuery(
    dataModelSlug: $slug
    mode: "timeline"
    filters: $filters
    since: "2026-07-01T00:00:00Z"
    limit: 20
    offset: $offset
  ) {
    hasNextPage
    results { entityId observedAt data }
  }
}
```

```json
{
  "slug": "order",
  "offset": 0,
  "filters": "[{\"field\":\"status\",\"op\":\"eq\",\"value\":\"shipped\"},{\"field\":\"total\",\"op\":\"gte\",\"value\":100}]"
}
```

Each clause has `field`, `op`, and non-null `value`. `field` must exactly match
one DataModelField and at most 20 clauses are accepted.

| Field type | Operators |
|---|---|
| String | `eq`, `neq`, `contains` |
| Integer/Float | `eq`, `neq`, `gt`, `gte`, `lt`, `lte` |
| Datetime | `eq`, `neq`, `gt`, `gte`, `lt`, `lte` |
| Boolean | `eq`, `neq` |

`contains` is a case-insensitive literal substring. `%` and `_` in the value
are escaped instead of becoming SQL wildcards.

## Invalid filters

These are query errors and must be corrected before retrying:

- malformed JSON in the GraphQL string;
- unknown or non-filterable field names;
- an operator unsupported by the model field type;
- values that cannot cast to the field type;
- `null` values; or
- more than 20 clauses.

The error identifies the invalid field/operator where safe. It does not expose
arbitrary warehouse payload paths.

## Offset pagination

`limit` defaults to 50, has a hard maximum of 250, and should still be explicit.
Start `offset` at zero and add the applied limit while `hasNextPage` is true.
Request `count` on the first page only when needed.

The 250 maximum is not a throughput promise: complexity often binds first.

## Complexity

The default maximum query complexity is 200. A page field selection is
multiplied by the requested limit, so narrower selections permit more rows. A
query selecting several page fields and `results { entityId observedAt data }`
can exceed the limit at `limit: 50`; reduce the limit or selected fields.

Do not request fields “just in case.” GraphQL's explicit selection is a cost and
data-minimization tool.

## Depth, document size, and windows

Default configurable guards are depth 10 and 8,000 parseable tokens. Violations
are rejected before resolution. Timeline windows default to 90 days and clamp
at 366 days; explicitly bounded smaller windows are easier to operate.

Per-credential rate limiting also applies: 300 requests per 60 seconds by
default, with `429` and `Retry-After` on breach.

For exact types and response objects, open the
[factQuery reference](/reference/graphql/fact-query).
