---
title: "Query Chart Endpoint"
method: POST
path: "/api/v2/charts/{name}/query"
tags: ["APPS_V2"]
---

# Query Chart Endpoint

`POST /api/v2/charts/{name}/query`

Run a chart's underlying dataset query.

Returns the chart descriptor plus a chart-shaped envelope
(`columns`/`rows`/`meta`). `timezone` is threaded through exactly like
`apps.api.datasets.query_dataset_endpoint` — see that endpoint's
docstring.

ONE execution path, per-endpoint PROJECTION: this endpoint never runs
its own query — it calls the exact same `run_dataset_query` a plain
dataset query uses, then reshapes that result's `DatasetEnvelope` into
a `ChartEnvelope` (`chart_columns` instead of `columns_descriptors`,
`ChartMeta` instead of `DatasetMeta`). A chart and its dataset can never
disagree about RBAC, filters, or row data — only presentation differs.

## Path parameters

- `name` string, required

## Headers

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

## Request body

- ChartQueryRequest — Request payload for `POST /charts/{name}/query`. A chart is a plot, not a table — it can never paginate (`apps.catalog .registry.validate_charts_against_datasets` refuses a chart declared over a `paginated=True` dataset), so `page`/`size` are absent here rather than accepted-and-ignored the way `DatasetQueryRequest` treats them for a non-paginated dataset. `sort` IS accepted: a categorical-x chart (e.g. a ranked bar chart) is legitimately re-ranked by a client-supplied sort — there the sort key determines the ranking, not just the display order. A sort that would scramble a TEMPORAL x-axis is refused, but not by this model — that check needs the chart's `x` column and its declared type, which live in the catalog, not the request. See `apps.api.charts._reject_axis_breaking_sort`.
  - `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

## Response `200`

Successful Response

- ChartEnvelope — A chart query response: its descriptor plus a chart-shaped result. `columns` is `list[ChartColumn]`, not `list[ColumnDescriptor]` — a chart plots values, it never renders a table, so it does not carry the table-only affordances (`sortable`/`filterable`/`edit`/`link`/...) a `ColumnDescriptor` has. `meta` is `ChartMeta`, not `DatasetMeta`, for the same reason: see that class's docstring for why pagination and `dataset` fields do not belong here.
  - `chart` ChartDescriptor, required
    - `name` string, required
    - `label` string, required
    - `dataset` string, required
    - `chart_type` string, required
    - `x` string, required
    - `series` string[], required
    - `stacked` boolean, required
    - `filters` FilterDescriptor[], required
      - `key` string, required
      - `label` string, required
      - `listable` boolean, required
      - `options` OptionDescriptor[], nullable
        - `value` string, required
        - `label` string, nullable
    - `related` RelatedDescriptor, required — Wire form of `apps.catalog.relations.RelatedNames` — the symmetrised page-affinity edges of one dataset/chart/summary, split by kind. This module owns no catalog lookups (see the module docstring's import rules), so nothing here builds a `RelatedDescriptor` from a `RelatedNames` — that projection lives in `apps.api.serializers`, the layer that already resolves `apps.catalog.RELATIONS`.
      - `datasets` string[], required
      - `charts` string[], required
      - `summaries` string[], required
    - `reference` ReferenceLineDescriptor, required
      - `of` string, required
      - `percentile` number, required
      - `label` string, required
      - `color` PaletteColorDescriptor, required — Wire form of `apps.specs.palette.PaletteColor` — a color stepped per surface. Colors live ONLY in descriptors; the frontend never hardcodes a chart color.
        - `light` string, required
        - `dark` string, required
    - `series_colors` object, required
    - `series_from` string, nullable
    - `show_change` boolean
  - `columns` ChartColumn[], required
    - `key` string, required
    - `label` string, required
    - `type` string, required
    - `unit` string, nullable
    - `order` integer, required
  - `rows` object[], required
  - `meta` ChartMeta, required — Metadata for a `ChartEnvelope` (`apps.api.serializers`). No pagination fields (`total`/`page`/`size`/`pages`): `apps.catalog .registry.validate_charts_against_datasets` refuses a chart declared over a `paginated=True` dataset (a chart needs the dataset's ENTIRE requested window in one response to render a full series), so those four could never be non-`None` here — carrying them anyway would only invite a client to write pagination-handling code for a response that can never paginate. No `dataset` field: the `ChartDescriptor` already embedded in the same `ChartEnvelope` (`ChartEnvelope.chart.dataset`) already names it.
    - `row_count` integer, required
    - `reference` ReferenceValue — A resolved `apps.specs.charts.ReferenceLine`, computed by `resolve_reference` from one chart query's rows.
      - `label` string, required
      - `of` string, required
      - `value` number, required
    - `last_event_time` string, date-time, nullable

## 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/a71fce1f5c26/schema)
