> For the complete documentation index, see [llms.txt](https://v2.dataos.info/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://v2.dataos.info/concepts/foundations/activation/apis/getting-started/querying-data-products/rest.md).

# REST

REST is the canonical query shape for semantic queries. Use it when you need the most control over filters, time dimensions, totals, and join hints.

**Endpoint:** `POST /api/v1/query/semantic/rest`

**Body:** `{"query": { ... }}`

### Response lifecycle

`POST` returns `202 Accepted` with a statement `id`. Poll `GET /api/v1/query/statement/{id}` until `status` is `SUCCESS` or `FAILED`. Fetch rows from `GET /api/v1/query/statement/{id}/result?format=json` only after `SUCCESS`.

```bash
ID=$(curl -s -X POST https://<host>/api/v1/query/semantic/rest \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":{"measures":["users.total_users"],"dimensions":["users.plan_type"]}}' \
  | jq -r '.id')

until [ "$(curl -s -H "Authorization: Bearer $TOKEN" \
  "https://<host>/api/v1/query/statement/$ID" | jq -r '.status')" = "SUCCESS" ]; do
  sleep 2
done

curl -s -H "Authorization: Bearer $TOKEN" \
  "https://<host>/api/v1/query/statement/$ID/result?format=json" | jq .
```

Status codes, deduplication, and export formats: see [Statement lifecycle](/concepts/foundations/activation/apis/getting-started/querying-data-products/statement-lifecycle.md).

At least one of `measures`, `dimensions`, or `timeDimensions` is required.

### Top-level fields

<table><thead><tr><th width="198.449951171875">Field</th><th>Description</th></tr></thead><tbody><tr><td><code>measures</code></td><td>Metrics to compute (<code>model.measure</code>)</td></tr><tr><td><code>dimensions</code></td><td>Group-by attributes; time fields may use <code>model.date.granularity</code></td></tr><tr><td><code>segments</code></td><td>Pre-defined boolean filters from the model (ANDed)</td></tr><tr><td><code>filters</code></td><td>Ad-hoc filter trees (see <a href="#filters">Filters</a>)</td></tr><tr><td><code>timeDimensions</code></td><td>Time grouping and range filtering</td></tr><tr><td><code>order</code></td><td>Sort — object or array of <code>[member, direction]</code> pairs</td></tr><tr><td><code>limit</code></td><td>Max rows (default 10,000; max 50,000)</td></tr><tr><td><code>offset</code></td><td>Pagination offset</td></tr><tr><td><code>timezone</code></td><td>IANA timezone (default <code>UTC</code>)</td></tr><tr><td><code>total</code></td><td>When <code>true</code>, also return full row count before limit/offset</td></tr><tr><td><code>ungrouped</code></td><td>Row-level results without <code>GROUP BY</code></td></tr><tr><td><code>joinHints</code></td><td>Explicit join path sequences (see <a href="#8-cross-model-queries">§8</a>)</td></tr></tbody></table>

Default order when omitted: first `timeDimensions` entry with granularity (asc) → first measure (desc) → first dimension (asc). Pass `"order": []` to disable ordering.

### Filters <a href="#filters" id="filters"></a>

The `filters` array holds leaf filters and logical groups. Each top-level entry is ANDed with the others.

**Leaf filter:** `{ "member": "model.field", "operator": "...", "values": [...] }`

**Logical filter:** exactly one of `{ "and": [...] }` or `{ "or": [...] }`. Children nest recursively.

Dimension filters restrict rows before aggregation (SQL `WHERE`). Measure filters restrict aggregated results (SQL `HAVING`). Do not mix dimension and measure filters inside the same `and`/`or` group — use separate top-level entries.

**Segments** (`segments[]`) are pre-authored boolean filters, ANDed together. In Semantic SQL they appear as `WHERE segment_name IS TRUE`.

**REST ↔ SQL mapping**

| REST                        | SQL                          | Notes                                              |
| --------------------------- | ---------------------------- | -------------------------------------------------- |
| Top-level `filters` entries | AND between clauses          | Each array element is one tree                     |
| `{ "and": [...] }`          | `( … AND … )`                | Recursive                                          |
| `{ "or": [...] }`           | `( … OR … )`                 | Recursive                                          |
| Dimension leaf filter       | `WHERE`                      | Before aggregation                                 |
| Measure leaf filter         | `HAVING`                     | After aggregation                                  |
| `equals` + multiple values  | `= v1 OR = v2`               | Same as `in` for strings/numbers                   |
| `in` / `notIn`              | `IN (...)` / `NOT IN (...)`  |                                                    |
| `contains`                  | `ILIKE '%v%'`                | Case-insensitive                                   |
| `notContains`               | `NOT ILIKE …`                | Includes NULL rows unless `null` in values         |
| `startsWith` / `endsWith`   | `ILIKE 'v%'` / `ILIKE '%v'`  |                                                    |
| `gt` / `gte` / `lt` / `lte` | comparisons                  | On dimensions or measures                          |
| `set` / `notSet`            | `IS NOT NULL` / `IS NULL`    | Omit `values`                                      |
| `inDateRange`               | typed range / `BETWEEN`      | Prefer `timeDimensions[].dateRange` when bucketing |
| `measureFilter`             | measure's built-in predicate | Omit `values`                                      |

**Simple filters**

REST:

```json
{
  "filters": [
    {
      "member": "users.plan_type",
      "operator": "notEquals",
      "values": ["free"]
    },
    {
      "member": "users.email",
      "operator": "set"
    }
  ]
}
```

Semantic SQL in [Semantic SQL](/concepts/foundations/activation/apis/getting-started/querying-data-products/semantic-sql.md):

```sql
WHERE users.plan_type <> 'free'
  AND users.email IS NOT NULL
```

**Nested OR with inner AND**

Equivalent to `(country ∈ {US, CA, GB}) OR (country = DE AND status = completed AND plan ≠ free)`.

REST:

```json
{
  "filters": [
    {
      "or": [
        {
          "member": "users.country",
          "operator": "equals",
          "values": ["US", "CA", "GB"]
        },
        {
          "and": [
            {
              "member": "users.country",
              "operator": "equals",
              "values": ["DE"]
            },
            {
              "member": "subscriptions.status",
              "operator": "equals",
              "values": ["completed"]
            },
            {
              "member": "users.plan_type",
              "operator": "notEquals",
              "values": ["free"]
            }
          ]
        }
      ]
    }
  ]
}
```

Semantic SQL:

```sql
WHERE (
  users.country IN ('US', 'CA', 'GB')
  OR (
    users.country = 'DE'
    AND subscriptions.status = 'completed'
    AND users.plan_type <> 'free'
  )
)
```

**Dimension filters + separate measure filter**

REST:

```json
{
  "filters": [
    {
      "and": [
        {
          "member": "subscriptions.plan_type",
          "operator": "equals",
          "values": ["enterprise", "business"]
        },
        {
          "member": "users.industry",
          "operator": "set"
        }
      ]
    },
    {
      "member": "subscriptions.mrr",
      "operator": "gte",
      "values": ["10000"]
    }
  ]
}
```

Semantic SQL:

```sql
SELECT users.industry, MEASURE(subscriptions.mrr)
FROM users
CROSS JOIN subscriptions
WHERE subscriptions.plan_type IN ('enterprise', 'business')
  AND users.industry IS NOT NULL
GROUP BY 1
HAVING MEASURE(subscriptions.mrr) >= 10000
```

**Filter operators**

| Operator         | `ltype`                       | Needs `values` | Description                                    |
| ---------------- | ----------------------------- | -------------- | ---------------------------------------------- |
| `equals`         | string, number, boolean, time | yes            | Exact match; multiple values = OR              |
| `notEquals`      | string, number, boolean, time | yes            | Inverse of equals                              |
| `in`             | string, number                | yes            | Value in list                                  |
| `notIn`          | string, number                | yes            | Value not in list                              |
| `contains`       | string                        | yes            | Case-insensitive substring                     |
| `notContains`    | string                        | yes            | Inverse; includes NULL unless `null` in values |
| `startsWith`     | string                        | yes            | Case-insensitive prefix                        |
| `notStartsWith`  | string                        | yes            | Inverse                                        |
| `endsWith`       | string                        | yes            | Case-insensitive suffix                        |
| `notEndsWith`    | string                        | yes            | Inverse                                        |
| `gt`             | number, measure               | yes            | Greater than                                   |
| `gte`            | number, measure               | yes            | Greater than or equal                          |
| `lt`             | number, measure               | yes            | Less than                                      |
| `lte`            | number, measure               | yes            | Less than or equal                             |
| `set`            | any                           | no             | Not NULL                                       |
| `notSet`         | any                           | no             | Is NULL                                        |
| `inDateRange`    | time                          | yes            | Inclusive range `["start", "end"]`             |
| `notInDateRange` | time                          | yes            | Outside range                                  |
| `onTheDate`      | time                          | yes            | Exact date                                     |
| `beforeDate`     | time                          | yes            | Strictly before                                |
| `beforeOrOnDate` | time                          | yes            | On or before                                   |
| `afterDate`      | time                          | yes            | Strictly after                                 |
| `afterOrOnDate`  | time                          | yes            | On or after                                    |
| `measureFilter`  | measure                       | no             | Measure's built-in filter predicate            |

Notes:

* `equals` with multiple values is equivalent to `in`.
* Dates: `"YYYY-MM-DD"` or `"YYYY-MM-DDTHH:mm:ss.SSS"`. Date-only values pad to day boundaries.
* `onTheDate` matches one calendar day on a time member. The same filter is `inDateRange` with the same start and end date.

### Time dimensions

```json
{
  "timeDimensions": [
    {
      "dimension": "users.signup_date",
      "granularity": "week",
      "dateRange": "last 90 days"
    }
  ]
}
```

<table><thead><tr><th width="186.84906005859375">Field</th><th>Description</th></tr></thead><tbody><tr><td><code>dimension</code></td><td>Qualified time field (required)</td></tr><tr><td><code>granularity</code></td><td>Bucket: <code>second</code>, <code>minute</code>, <code>hour</code>, <code>day</code>, <code>week</code>, <code>month</code>, <code>quarter</code>, <code>year</code>, or custom</td></tr><tr><td><code>dateRange</code></td><td>Preset string or <code>["YYYY-MM-DD", "YYYY-MM-DD"]</code></td></tr><tr><td><code>compareDateRange</code></td><td>Period-over-period — array of date ranges; mutually exclusive with <code>dateRange</code></td></tr></tbody></table>

**Date range presets**

Accepted as `dateRange` strings in `timeDimensions[]`. Evaluated in the query `timezone`.

<table><thead><tr><th width="190.07940673828125">Category</th><th>Presets</th></tr></thead><tbody><tr><td>Single day</td><td><code>today</code>, <code>yesterday</code>, <code>tomorrow</code></td></tr><tr><td>Rolling window</td><td><code>last 7 days</code>, <code>last 14 days</code>, <code>last 30 days</code>, <code>last 60 days</code>, <code>last 90 days</code>, <code>last 180 days</code>, <code>last 365 days</code>, <code>last 6 months</code>, <code>last 3 months</code></td></tr><tr><td>Calendar — past</td><td><code>last week</code>, <code>last month</code>, <code>last quarter</code>, <code>last year</code></td></tr><tr><td>Calendar — current</td><td><code>this week</code>, <code>this month</code>, <code>this quarter</code>, <code>this year</code></td></tr><tr><td>Calendar — future</td><td><code>next week</code>, <code>next month</code>, <code>next quarter</code>, <code>next year</code></td></tr><tr><td>Extended</td><td><code>from 7 days ago to now</code>, <code>from now to 2 weeks from now</code>, <code>last 360 days</code></td></tr></tbody></table>

For anything not listed, use an explicit array:

```json
{
  "dateRange": ["2025-01-01", "2025-03-31"]
}
```

For `flow` and `stock` measures, pair time filters with guidance in [Measure and dimension behavior](/concepts/foundations/activation/apis/getting-started/querying-data-products/measure-and-dimension-behavior.md).

Metric example:

```json
{
  "timeDimensions": [
    {
      "dimension": "store_revenue.ts",
      "granularity": "day",
      "dateRange": ["2025-01-01", "2025-03-31"]
    }
  ]
}
```

### Complete examples

Semantic:

```json
{
  "query": {
    "measures": ["users.active_users"],
    "dimensions": ["users.plan_type", "users.signup_channel"],
    "segments": ["users.recent_signups"],
    "timeDimensions": [
      {
        "dimension": "users.signup_date",
        "granularity": "week",
        "dateRange": "last 90 days"
      }
    ],
    "filters": [
      {
        "member": "users.plan_type",
        "operator": "notEquals",
        "values": ["free"]
      }
    ],
    "order": [["users.signup_date", "desc"]],
    "limit": 500,
    "timezone": "UTC"
  }
}
```

Metric (full REST — see [Metrics](/concepts/foundations/activation/apis/getting-started/querying-data-products/metrics.md) for the shortcut API):

```json
{
  "query": {
    "measures": ["store_revenue.measure"],
    "dimensions": ["store_revenue.product_category", "store_revenue.ts.day"],
    "segments": ["store_revenue.orders_completed_orders"],
    "timeDimensions": [
      {
        "dimension": "store_revenue.ts",
        "granularity": "day",
        "dateRange": "last 90 days"
      }
    ],
    "limit": 1000
  }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://v2.dataos.info/concepts/foundations/activation/apis/getting-started/querying-data-products/rest.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
