---
title: "Export Dataset Endpoint"
method: POST
path: "/api/v2/datasets/{name}/export"
tags: ["APPS_V2"]
---

# Export Dataset Endpoint

`POST /api/v2/datasets/{name}/export`

Export a dataset's full RBAC-scoped result as a CSV download.

Reuses the exact query pipeline `POST /datasets/{name}/query` runs
(`apps.engine.service.run_dataset_export`, via `run_dataset_query(...,
export_mode=True)`) — same RBAC resolve, same filter/sort/search
normalization — so an export can never disagree with what the table
endpoint would have shown for an identical request. No separate
freshness endpoint or field here: freshness is delivered on the query
endpoints' own response metas, not on a one-shot CSV download.

## Path parameters

- `name` string, required

## Headers

- `x-tenant` string, required
- `x-user-group-id` string, nullable
- `x-timezone` string, nullable

## Request body

- DatasetQueryRequest — Request payload for `apps.engine.run_dataset_query`. `start_date`/`end_date` are calendar dates, not datetimes — the frontend always sends date-only `YYYY-MM-DD` values. `end_date` is INCLUSIVE for the caller; see `half_open_window` for the half-open datetime conversion the engine actually queries against. `granularity` buckets trend results via `apps.engine.sql_builder.SqlBuilder.date_trunc`. `group_by`/`order_by` are deliberately not accepted: free-text grouping/ordering from a client would be an injection surface against dataset-authored SQL. Datasets bake their own ordering, and grouping comes from `granularity`. `sort` is the one narrow exception: it carries a declared column key, resolved server-side via `resolve_sort` — never a raw SQL fragment. `search` is free-text substring search, ORed across the dataset's `searchable=True` columns (see `DatasetSpec.searchable_columns` and `resolve_search`). This is distinct from `filters`, which are exact IN-list matches against one named dimension each. The distinction mirrors v1's `/warehouse/` endpoint, which exposes BOTH a `warehouseName` exact IN-list param and a separate `search` ILIKE param (`app/service/warehouses.py:154-157`) — one endpoint, two independent matching semantics, because "give me exactly these warehouses" and "give me warehouses whose name contains this" are different questions. `page`/`size` apply ONLY when `DatasetSpec.paginated` is `True` — for a non-paginated (chart/trend) dataset they are accepted but ignored; `limit` continues to govern how many rows THAT dataset's `build` renders. `page` is 1-based to match v1's `GET /warehouse/?page=1&size= 20&...` and the frontend's existing pagination controls exactly; the engine (`apps.engine.sql_builder.SqlBuilder.limit_sql`) is what converts it to a 0-based `OFFSET = (page - 1) * size`, so no caller of this request model ever has to do that arithmetic itself.
  - `filters` object
  - `start_date` string, date, required
  - `end_date` string, date, required
  - `granularity` 'day' | 'week' | 'month'
  - `limit` integer
  - `sort` SortSpec — A request's sort key + direction. `key` is a declared column key, NEVER raw SQL — it is resolved against the dataset's `sortable` columns by `resolve_sort`, which is the only thing that turns it into SQL text. `direction` is lowercase to match the frontend's `SortDirection` (`"asc" | "desc"`) exactly.
    - `key` string, required
    - `direction` 'asc' | 'desc'
  - `search` string, nullable
  - `page` integer
  - `size` integer

## Response `200`

Successful Response

- unknown

## Other responses

- `403` — Not supported for public user
- `422` — Validation Error

---

[API](https://skmtc.net/myaltimate/apis/fastapi.md) · [All operations](https://skmtc.net/myaltimate/apis/fastapi/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/myaltimate/fastapi/revisions/7b73a4034706/schema)
