---
title: "Poll an async query's status"
method: POST
path: "/query/status"
tags: ["Query"]
---

# Poll an async query's status

`POST /query/status`

## Request

- Base URL: `https://api.reportingninja.com/v1`
- URL: `https://api.reportingninja.com/v1/query/status`
- Auth: HTTP bearer

Returns the current state of a previously submitted async query (pending, running, success, error). When status is `success`, the original /v1/query response body is embedded under data.result with the same shape (status + data.rows[] + meta). When status is `error`, data.error_message contains the failure reason. Authorization: same Bearer token. A job_id belonging to another account returns the same `job_id not found or expired` envelope as unknown ids. Successful results are retained for 8 hours; after that the job returns the same not-found envelope.

## Request body

- QueryStatusRequest: Which asynchronous job to poll.
  - `job_id` string, required: The job the submission answered.

## Response `200`

The answer of the call, or a business error envelope. This endpoint reports its own errors with HTTP 200 and status=error: read envelope.status before reading data.

- union
  - AsyncQueryStatusResponse: Envelope returned by POST /v1/query/status. When `data.status` is `success`, `data.result` embeds the same `{status, data.rows[], meta}` body that /v1/query would have produced. When `data.status` is `error`, `data.error_message` describes the failure.
    - `status` 'ok', required
    - `data` object, required
      - `job_id` string, required
      - `status` 'pending' | 'running' | 'success' | 'error', required: Current state. `pending`: awaiting processing. `running`: executing. `success`: completed; see data.result. `error`: completed with failure; see data.error_message.
      - `created_at` integer: UNIX epoch milliseconds when the job was submitted.
      - `started_at` integer: UNIX epoch milliseconds when the executor started. Only present when status is running/success/error.
      - `completed_at` integer: UNIX epoch milliseconds when the executor completed (with success or error). Only present when status is success/error.
      - `result` object: Embedded /v1/query response body (status + data.rows[] + meta). Same shape as the synchronous /v1/query response. Only present when status is success.
      - `error_message` string: Truncated error description (<= 500 chars). Only present when status is error.
    - `meta` Meta, required
      - `request_id` string, required: Id of this request, useful when reporting an incident.
      - `timestamp` string, date-time, required: When the answer was built, in UTC.
  - ErrorEnvelope
    - `status` 'error', required
    - `error_code` 'AUTH_INVALID' | 'AUTH_EXPIRED' | 'RATE_LIMITED' | 'WRITE_ACCESS_DISABLED' | 'WRITE_TEMPORARILY_UNAVAILABLE' | 'NOT_FOUND' | 'ENDPOINT_NOT_FOUND' | 'INVALID_REQUEST' | 'INVALID_MODE' | 'PAYLOAD_TOO_LARGE' | 'MISSING_REQUIRED_FIELD' | 'INVALID_VALUE' | 'INVALID_LIMIT' | 'UNKNOWN_PARAM' | 'INTEGRATION_NOT_SUPPORTED' | 'INTEGRATION_NOT_IMPLEMENTED' | 'INTEGRATION_NOT_QUERYABLE' | 'FIELD_NOT_AVAILABLE' | 'FILTER_NOT_WIRED' | 'INVALID_FILTER' | 'INVALID_DATE_RANGE' | 'CONNECTION_NOT_FOUND' | 'PLAN_LIMIT_REACHED' | 'PROVIDER_PERMISSION' | 'PROVIDER_UNAVAILABLE' | 'JOB_NOT_FOUND' | 'CURSOR_EXPIRED' | 'VALIDATION_FAILED' | 'APPEND_NOT_ALLOWED' | 'TEMPLATE_NOT_FOUND' | 'CONFIRMATION_EXPIRED' | 'CONFIRMATION_MISMATCH' | 'IDEMPOTENCY_CONFLICT' | 'IDEMPOTENCY_IN_PROGRESS' | 'VERSION_MISMATCH' | 'SECTION_BUSY' | 'REPORT_IN_USE' | 'NOT_ALLOWED' | 'IN_USE' | 'REPORT_IN_TRASH' | 'REPORT_NOT_IN_TRASH' | 'WIDGET_EXECUTION_FAILED' | 'ROW_RULE' | 'POSITION_CHANGED' | 'SECTION_CHANGED' | 'AI_SUMMARY_NOT_ALLOWED' | 'DATA_VIEW_CHANGED' | 'WIDGET_CHANGED' | 'WIDGET_NOT_EDITABLE' | 'SCHEDULE_ALREADY_EXISTS' | 'ALREADY_QUEUED' | 'SCHEDULE_ALREADY_QUEUED' | 'SCHEDULE_QUEUED' | 'SCHEDULE_USES_SYSTEM_TEMPLATE' | 'NOT_EDITABLE_BY_API' | 'PORTAL_NOT_CONFIGURED' | 'USER_EMAIL_EXISTS' | 'ACCOUNT_HAS_NO_OWNER' | 'EMAIL_QUOTA_REACHED' | 'NOTIFICATION_FAILED' | 'INTERNAL', required: Machine-readable code that classifies the error. Stable across versions. Clients should switch on this code instead of parsing the `message` text. Reading the HTTP status alone is not enough: the data endpoints answer their business errors with HTTP 200 and status=error, while the report endpoints answer with the real status.
    - `message` string, required: Human-readable error description. Useful for logs and UIs; not stable across versions -- use `error_code` for programmatic handling.
    - `meta` WriteMeta, required: The meta of a write answer. A replay carries two extra fields so it can be told apart from a first call and still traced back to it.
      - `request_id` string, required: Id of THIS request, including on a replay.
      - `timestamp` string, date-time, required
      - `replayed` boolean: True when this answer is the stored answer of an earlier call with the same idempotency_key and the same body. Absent on a first call.
      - `replayed_from_request_id` string, nullable: The request_id of the call that actually did the work. Present only on a replay, and null when the stored answer carried no id.
    - `details` object: Machine-readable detail of the refusal, present only on the errors that have one. Four shapes are published today. VALIDATION_FAILED and UNKNOWN_PARAM carry `problems`, an array of `Problem` naming the path, the code and the message of each thing that is wrong, with `suggestions` when a closed list exists; a body wrong in more than 50 places gets the first 50 and a last entry with code TOO_MANY_PROBLEMS. VERSION_MISMATCH carries `expected` and `actual`. REPORT_IN_USE carries `portal_views` and `reports`, each an array of {key, name} that must be detached first, and an entry of `reports` that is itself in the trash adds `in_trash: true`. REPORT_IN_TRASH carries `trashed_at`, `restorable_until` and `restore`. APPEND_NOT_ALLOWED and PORTAL_NOT_CONFIGURED carry `reason`, the INVALID_VALUE of a widget placement carries `first_free_row`, WIDGET_EXECUTION_FAILED carries the `verdict`, `message` and `reason` of the run, and ALREADY_QUEUED carries `schedule_key` and `report_key`: the one-off request of the snapshot that is still waiting, and the report it was asked of. IN_USE carries `used_by`, naming everything of the account that still holds the row a deletion was asked of, in the shape of that entity: an `EntityUsage` for a saved filter and a calculated metric, an `EntityUsageReports` for a language file and an `EntityUsageSchedules` for an email template. The IN_USE of the email template the client portal sends its notifications with carries `reason` `client_portal` as well, and it is the only IN_USE that carries a reason. NOT_ALLOWED carries `reason`, the rule that stops the write (`system_defined` for a row the platform ships and keeps).
      - `problems` Problem[]
        - `path` string, required: Dotted path of the offending field inside the request body, empty for a problem about the body as a whole.
        - `code` 'MISSING_REQUIRED_FIELD' | 'INVALID_VALUE' | 'UNKNOWN_PARAM' | 'INVALID_DATE_RANGE' | 'INTEGRATION_NOT_SUPPORTED' | 'CONNECTION_NOT_FOUND' | 'ACCOUNT_NOT_FOUND' | 'ONE_CONNECTION_PER_INTEGRATION' | 'DATE_PRESET_NOT_IN_REPORTS' | 'TEMPLATE_NOT_FOUND' | 'TEMPLATE_PARAMETERIZED' | 'ROW_RULE' | 'POSITION_CHANGED' | 'SECTION_CHANGED' | 'TOO_MANY_PROBLEMS' | 'DATA_SOURCES_PARTIALLY_BOUND' | 'WELCOME_EMAIL_FAILED' | 'REPORT_WILL_BE_SHARED_PUBLICLY' | 'MAY_BE_DELIVERED_AGAIN_TODAY' | 'SUMMARY_SECTION_SKIPPED' | 'COVER_FILE_REMOVED' | 'COVER_TYPE_SET' | 'LOGO_REMOVED' | 'READ_ONLY_FIELD_IGNORED' | 'AI_BRIEF_NOT_SAVED' | 'SPENDS_AI_CREDITS' | 'DROPS_ACCOUNT_CACHE' | 'CHART_TYPE_NOT_FOR_THIS_SHAPE' | 'CHART_TYPE_NOT_FOR_TREND' | 'CONTENT_NOT_FOR_THIS_SHAPE' | 'FULL_WIDTH_NOT_FOR_KPI' | 'DATA_VIEW_FIELDS_PER_ACCOUNT' | 'DATA_VIEW_NOT_AVAILABLE' | 'DATA_VIEW_NOT_EMPTY' | 'DATA_VIEW_NOT_OF_THE_SECTION' | 'DATA_VIEW_REQUIRED' | 'DATA_VIEW_UNKNOWN' | 'DATE_RANGE_CUSTOM_INCOMPLETE' | 'DATE_RANGE_CUSTOM_INVALID' | 'DATE_RANGE_CUSTOM_INVERTED' | 'DATE_RANGE_PRESET_UNKNOWN' | 'DATE_RANGE_X_REQUIRED' | 'DIMENSION_DUPLICATED' | 'DIMENSION_IS_A_METRIC' | 'DIMENSION_REQUIRED' | 'DIMENSION_UNKNOWN' | 'DSL_INVALID' | 'FIELD_NOT_IN_AD_TYPES' | 'FILTERS_NOT_SUPPORTED' | 'FILTER_COMBINATION_NOT_SUPPORTED' | 'FILTER_CONDITIONS_MIXED_KINDS' | 'FILTER_FIELD_NOT_A_COLUMN' | 'FILTER_FIELD_NOT_IN_DATA_VIEW' | 'FILTER_FIELD_UNKNOWN' | 'FILTER_LIKE_NEEDS_WILDCARD' | 'FILTER_OPERATOR_INVALID' | 'FILTER_TYPE_OF_FIELD_MISMATCH' | 'FILTER_TYPE_OF_FIELD_NOT_USED' | 'FILTER_TYPE_OF_FIELD_REQUIRED' | 'FILTER_VALUE_EMPTY' | 'FILTER_VALUE_NOT_ALLOWED' | 'FILTER_VALUE_NOT_NUMERIC' | 'GRAPH_EXTRA_DIMENSIONS' | 'GRAPH_TIME_DIMENSION_MIXED' | 'INTEGRATION_UNKNOWN' | 'INTENT_MISSING' | 'KPI_NEEDS_ONE_METRIC' | 'KPI_TAKES_NO_DIMENSIONS' | 'LIMIT_IGNORED_IN_TREND' | 'LIMIT_OUT_OF_RANGE' | 'METRICS_SUMMARY_TAKES_NO_DIMENSIONS' | 'METRIC_DUPLICATED' | 'METRIC_IS_A_DIMENSION' | 'METRIC_UNKNOWN' | 'METRICS_REQUIRED' | 'NAME_EMPTY' | 'NAME_NOT_PLAIN_TEXT' | 'NAME_TOO_LONG' | 'PIE_TAKES_ONE_DIMENSION' | 'PREVIOUS_PERIOD_NOT_AVAILABLE_FOR_THIS_TABLE' | 'COMPARISON_NOT_AVAILABLE_FOR_THIS_WIDGET' | 'PREVIOUS_PERIOD_NOT_FOR_THIS_SHAPE' | 'SHOW_CHANGE_NOT_FOR_THIS_SHAPE' | 'SHOW_CHANGE_NEEDS_PREVIOUS_PERIOD' | 'SETTING_DUPLICATED' | 'SETTING_NOT_FOR_DATA_VIEW' | 'SETTING_NOT_IN_WIDGET' | 'SETTING_UNKNOWN' | 'SETTING_VALUE_INVALID' | 'SORT_FIELD_NOT_SELECTED' | 'TABLE_TIME_DIMENSION_NOT_DRAWABLE' | 'TEXT_CONTENT_REQUIRED' | 'TEXT_CONTENT_TOO_LONG' | 'TEXT_NOT_A_WIDGET_TYPE' | 'TEXT_TAKES_NO_DATA' | 'TOO_MANY_DIMENSIONS' | 'TOO_MANY_METRICS' | 'TRANSLATION_FAILED' | 'TREND_ORDER_IS_THE_TIME_AXIS' | 'TREND_TAKES_NO_DIMENSIONS' | 'VISUALIZATION_NOT_AVAILABLE' | 'VISUALIZATION_NOT_OF_REQUESTED_TYPE' | 'FIELD_NOT_AVAILABLE' | 'FILTER_CONDITIONS_MIXED_LEVELS' | 'FILTER_LEVEL_MISMATCH' | 'FORMULA_TOKEN_UNKNOWN' | 'FORMULA_INVALID' | 'LANGUAGE_FILE_KEYS_IGNORED' | 'SELECTION_EMPTY' | 'FILTER_KEY_AND_FILTERS' | 'FILTER_KEY_NOT_FOR_THIS_SHAPE' | 'KEY_NOT_FOR_GENERATION' | 'COMPARE_NOT_FOR_THIS_SHAPE' | 'COMPARE_NOT_FOR_INTEGRATION' | 'COMPARE_ITEMS_CARDINALITY' | 'COMPARE_METRICS_CARDINALITY' | 'COMPARE_OVER_TIME_NOT_AVAILABLE' | 'COMPARE_METRIC_HAS_NO_BREAKDOWN' | 'COMPARE_KEY_NOT_FOR_THIS_COMPARATOR' | 'COMPARATOR_CLASS_FIXED', required: What is wrong with that field, or what is worth knowing about it. The first group is the vocabulary of the body itself; the eleven that follow it appear only in `warnings[]`, bar TOO_MANY_PROBLEMS, which closes a `problems` list that was truncated; and the rest are the codes of the widget DSL, which travel here when a composition or a widget carries a dsl that does not validate.
        - `message` string, required: Human-readable explanation. Not stable across versions.
        - `suggestions` string[]: The accepted values, when the field takes a closed list.
      - `used_by` union: What still reads the definition the call tried to delete. The families depend on the entity: a saved filter or a calculated metric is asked about reports, filtered metrics, portal views and Data Studio account groups; an email template about schedules and the client portal; a language file about reports.
        - EntityUsage: What still reads a saved filter or a calculated metric, published inside details.used_by of an IN_USE. Only the families the question covers are present: a filter is asked about reports, calculated metrics, portal views, alerts and Data Studio account groups, and carries the five; a calculated metric is asked about reports and account groups, and carries those two alone rather than empty lists nobody looked into.
          - `reports` EntityUsageReport[], required
            - `report_key` string, nullable, required: The report the widgets and sections belong to. It can be the key of a TEMPLATE, because a widget of a template based report belongs to the template; and it is null when the report of the element could not be resolved, in which case the entry stands for a single widget or section.
            - `internal_name` string, nullable, required: What the report is called inside the account. Null when the key names a template or a report that is not there any more.
            - `in_trash` boolean, required: Whether the report is in the trash. A report in the trash still reads the definition.
            - `widgets` integer, required: How many widgets of it read the definition.
            - `sections` integer, required: How many sections of it read the definition as a global filter.
          - `calculated_metrics` EntityUsageEntity[]: Only for a saved filter: the filtered metrics built on it.
            - `calculated_metric_key` string: Present in calculated_metrics.
            - `portal_view_key` string: Present in portal_views.
            - `name` string, nullable: What it is called.
          - `portal_views` EntityUsageEntity[]: Only for a saved filter: the portal views that hand it to their clients.
            - `calculated_metric_key` string: Present in calculated_metrics.
            - `portal_view_key` string: Present in portal_views.
            - `name` string, nullable: What it is called.
          - `alerts` integer: Only for a saved filter: how many alerts of the account are evaluated with it.
          - `account_groups` integer, required: How many Data Studio account groups read it.
        - EntityUsageSchedules: What still sends an email template, published inside details.used_by of an IN_USE.
          - `schedules` EntityUsageSchedule[], required
            - `schedule_key` string, required: The delivery, as /v1/schedules/list names it.
            - `report_key` string, nullable, required: The report it delivers.
            - `frequency` string, nullable, required: How often it goes out.
          - `client_portal` boolean, required: Whether the client portal of the account sends its notifications with this template, which is chosen in the application.
        - EntityUsageReports: What still reads a language file, published inside details.used_by of an IN_USE.
          - `reports` EntityUsageReportRef[], required
            - `report_key` string, required
            - `internal_name` string, nullable, required: What the report is called inside the account.
            - `in_trash` boolean, required: Whether the report is in the trash. A report in the trash still points at the file and would come back pointing at nothing.
      - `reason` string, nullable: The rule behind the refusal, where it has a name: the system_defined of a NOT_ALLOWED, the template_based of an APPEND_NOT_ALLOWED, the reason a widget could not be run. Null when the refusal has no named rule.

## Other responses

- `400`: The request was refused before anything was written. INVALID_REQUEST covers the framework refusals of every endpoint (the Content-Type is not application/json, the body could not be read, the body is not valid JSON). The report endpoints add their own: VALIDATION_FAILED with details.problems, UNKNOWN_PARAM with the path of the field the endpoint does not read, and the single-field refusals MISSING_REQUIRED_FIELD, INVALID_VALUE and APPEND_NOT_ALLOWED. The description of each operation lists the ones it emits. Everything else a body can get wrong arrives as an entry of details.problems rather than as the code of the envelope.
- `401`: Invalid or missing Bearer token.
- `413`: The request body is over 1 MB. The cap is applied before authentication, so this answer is refused without the key being read, and it carries the usual error envelope.
- `429`: A budget was spent. RATE_LIMITED is the per-key read budget (300/minute, 5000/day) or the per-account write budget (60/minute, 1000/day). EMAIL_QUOTA_REACHED is the separate budget of 50 calls per account per day that send email, and its Retry-After points at the next UTC midnight. The Retry-After header gives the seconds until the more restrictive window resets. The X-RateLimit-* headers report the read budget, except on the 429 of a write, where they report the write budget.
- `500`: Internal server error.

---

[API](https://skmtc.dev/reportingninja/apis/reporting-ninja-api.md) · [All operations](https://skmtc.dev/reportingninja/apis/reporting-ninja-api/llms.txt) · [OpenAPI document](https://skmtc.dev/reportingninja/apis/reporting-ninja-api/revisions/13876c71be34?raw)
