---
title: "Get Framework Data"
method: GET
path: "/v2/framework/{framework_id}/data"
tags: ["Framework"]
---

# Get Framework Data

`GET /v2/framework/{framework_id}/data`

## Path parameters

- `framework_id` string, uuid, required

## Query parameters

- `data_type` string — Data type
- `page` integer, nullable
- `size` integer, nullable
- `fetch_all` boolean — Fetch all data
- `poll` boolean — Opt into the job-polling contract (PRO-3420): if the query hasn't finished within its short submission wait, respond with a QueryJobResponse envelope instead of blocking until it's done. Callers that don't pass this keep the original always-blocking behavior unchanged.
- `job_id` string, nullable — Finalize a job an earlier poll=true call already submitted, once GET /v2/query-jobs/{job_id} reports it terminal, instead of submitting a new one. The normal response shaping/pagination still runs on the result.

## Request body

- object, nullable

## Response `200`

Successful Response

- union
  - TimeSeriesResponse
    - `type` 'timeseries', required
    - `total` integer, required
    - `page` integer, required
    - `size` integer, required
    - `pages` integer, required
    - `insight_column` string, nullable
    - `data` TimeSeriesEntry[], required
      - `entity_representation` string, required
      - `entity_id` integer, required
      - `entity_name` string, required
      - `filters` object, required
      - `insight` string, required
      - `series_name` string, required
      - `series` TimeSeriesPoint[], required
        - `date` string, required
        - `value` number, nullable, required
    - `message` string, nullable
    - `metadata_info` object, nullable
  - DataFrameResponse
    - `type` 'dataframe', required
    - `total` integer, required
    - `page` integer, required
    - `size` integer, required
    - `pages` integer, required
    - `insight_column` string, nullable
    - `data` object[], required
    - `message` string, nullable
    - `metadata_info` object, nullable
  - QueryJobResponse
    - `job_id` string, required
    - `state` 'queued' | 'running' | 'done' | 'failed', required — Job lifecycle, mirroring pyca's ``QueryStatusTracker`` states. One collapsed ``queued``, with no separate "waiting for a worker" vs "waiting for the ConcurrencyGate" state (PRO-3278 decision): the two are indistinguishable to the person waiting. Be aware of where the worker actually draws that line today. It calls ``mark_running`` *before* ``async_read_query``, and the gate is acquired inside that call, so a job parked on the gate reports ``running`` — with no position, no ETA, and a ``queue_wait_ms`` frozen at pickup — rather than ``queued``. Under a saturated gate, which is the condition this whole system exists to make visible, that understates the wait by exactly the gate wait. Moving ``mark_running`` past gate admission is a change to the worker (PRO-3281), tracked separately. An enum rather than a bare ``str`` so a state pyca adds later surfaces as a loud, explicit failure in ``QueryJobService`` instead of flowing through to clients that have no branch for it.
    - `position` integer, nullable — 1-indexed place in the queue, while queued.
    - `eta_seconds` number, nullable — Estimated seconds until this job finishes, from recent completion durations. None while no completions have been recorded yet.
    - `queue_wait_ms` integer — Time spent queued. Still climbing while queued; frozen at the real wait once the job starts running.
    - `poll_after_ms` integer, nullable — How long the client should wait before polling again. Absent on a terminal state, which needs no further polling.
    - `error` string, nullable
    - `result` QueryJobResult — A finished job's rows, in the same column/row *structure* as ``SQLQueryResponse`` — columns plus a row matrix — so PRO-3283's SQL editor migration is a rename rather than a reshape. Note the ``type`` caveat above: the structure translates, the type vocabulary does not. No ``applied_limit``/``limit_injected`` here: those describe a caller's own SQL rewrite, which happens before submission, not anything the job knows about. ``truncated`` also isn't here -- see ``QueryJobResponse.truncated`` instead: a done job's *cached* result can now be a worker-applied byte-payload trim (not just a caller's own rewrite), so that flag belongs on the envelope wrapping this, not on the row/column structure itself.
      - `columns` QueryJobColumn[], required
        - `name` string, required
        - `type` string, nullable
      - `rows` array[], required
        - unknown[]
          - unknown
      - `row_count` integer, required
    - `result_json` unknown
    - `truncated` boolean — True if `result`'s rows are a prefix of the real query result -- the worker applied the same byte-payload trim run_query's own sync path uses (see trim_rows_for_payload) before caching, because the result exceeded MAX_PAYLOAD_BYTES. Lives on this envelope rather than QueryJobResult itself: QueryJobResult documents a done job's cached result as always complete, which is no longer true for a trimmed sql_editor job (bugbot finding: 'Async SQL trim hides truncation'), and callers already check SQLQueryResponse.truncated on the sync path -- this mirrors it.

## Other responses

- `404` — Not Found
- `422` — Validation Error

## Changes

- **2026-09-03** `475c0fdb4aaf` — 3 info
  - added the new optional `query` request parameter `job_id`
  - added the new optional `query` request parameter `poll`
  - added `#/components/schemas/QueryJobResponse` to the response body `anyOf` list for the response status `200`

[Change history](https://skmtc.dev/carbonarc/apis/carbon-arc-api-documentation/changes/v2/framework/:framework_id/data/get.md)

---

[API](https://skmtc.dev/carbonarc/apis/carbon-arc-api-documentation.md) · [All operations](https://skmtc.dev/carbonarc/apis/carbon-arc-api-documentation/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/carbonarc/carbon-arc-api-documentation/revisions/475c0fdb4aaf/schema)
