# factQuery arguments and results

> Understand latest versus timeline semantics and every public factQuery argument, PageOfFact field, and Fact field.

`factQuery` reads one data model as a logical table. The model slug selects the
table, its fields define allowed payload columns, and each `Fact` is one mapped
record. It requires an authenticated service account with `api_read`.

## Operation signature

```graphql
factQuery(
  dataModelSlug: String!
  mode: String
  timeAxis: String
  since: DateTime
  until: DateTime
  filters: String
  sort: String
  sortDirection: String
  limit: Int
  offset: Int
): PageOfFact
```

| Argument | Values | Default | Meaning |
|---|---|---|---|
| `dataModelSlug` | required string | — | Model resolved in the credential workspace |
| `mode` | `latest`, `timeline` | `latest` | Current row per entity or history |
| `timeAxis` | `observed_at`, `inserted_at` | `observed_at` | Timestamp used for the window |
| `since` | ISO-8601 DateTime | mode-dependent | Inclusive lower bound |
| `until` | ISO-8601 DateTime | now in timeline | Upper bound |
| `filters` | JSON array encoded as string | none | At most 20 field/operator/value clauses |
| `sort` | `observed_at`, `inserted_at`, `entity_id` | `observed_at` | Timeline ordering field |
| `sortDirection` | `asc`, `desc` | `desc` | Timeline direction |
| `limit` | integer, maximum 250 | `50` | Page size, also complexity-sensitive |
| `offset` | non-negative integer | `0` | Rows to skip |

Unknown enumerated values are errors, not silent fallbacks. Pass `limit`
explicitly even though the runtime defaults to 50.

## Latest mode

`latest` returns one most recent row for each non-null `entityId`. It is useful
for current-state views. Rows without entity identity are excluded, and clients
must not depend on server ordering from the distinct-per-entity query.

Latest mode is not automatically time-clamped because a current row may be old.
Any explicit `since` or `until` still applies as a normal filter.

## Timeline mode

`timeline` returns every matching historical row. Sort defaults to newest
`observed_at` first. If no window is supplied, it uses the last 90 days ending
now. A requested span wider than 366 days is clamped forward to 366 days.

Always provide a bounded page size and, for repeatable exports, explicit time
bounds. Offset pagination is supported; keyset pagination is not.

## PageOfFact

The result page exposes:

- `results` — selected `Fact` rows;
- `count` — total matching count when requested;
- `hasNextPage` and `hasPreviousPage`;
- `pageNumber` and `lastPage`; and
- `limit` — applied page size.

Requesting `count` can require an additional count query. Ask for it only when
the client uses it.

## Fact

Public fields include `id`, `dataModelId`, `entityId`, `observedAt`,
`insertedAt`, and `data`. The `data` JSON object contains values mapped to the
model's public fields. Lineage and internal execution fields remain outside this
logical-table response unless explicitly present in the current
[GraphQL SDL](/schema.graphql).

## Errors

Bad query syntax/types, unknown model slugs, invalid filters, and cost-limit
violations appear in GraphQL's `errors` array, often with HTTP `200`. Invalid
tokens and `429` rate limits use transport-level API errors. Keep
`x-request-id` for both.

Use the browsable [factQuery endpoint reference](/reference/graphql/fact-query)
for copyable query/variables examples.
