---
title: "Read one calculated metric"
method: POST
path: "/calculated-metrics/get"
tags: ["Calculated Metrics"]
---

# Read one calculated metric

`POST /calculated-metrics/get`

## Request

- Base URL: `https://api.reportingninja.com/v1`
- URL: `https://api.reportingninja.com/v1/calculated-metrics/get`
- Auth: HTTP bearer

Returns one calculated metric by key, in the same shape the listing publishes it. Which reports and widgets use it is not published.

## Request body

- CalculatedMetricGetRequest
  - `calculated_metric_key` string, required: The metric to read. From /v1/calculated-metrics/list.

## Response `200`

The call succeeded. This endpoint reports its errors with a real HTTP status, so a 200 always carries status=ok.

- CalculatedMetricGetResponse
  - `status` 'ok', required
  - `data` CalculatedMetricGetData, required: The metric.
    - `calculated_metric` CalculatedMetric, required: One calculated metric of the account. Only the block of its own kind is filled in, and all three are null when the definition cannot be named in public.
      - `calculated_metric_key` string, nullable, required: The metric. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is. It is null in ONE place: the preview of a dry run of a creation, where the row does not exist yet and so has no key.
      - `name` string, nullable, required
      - `display_name` string, nullable: The name shown on a widget that uses it, when it differs from the name.
      - `internal_notes` string, nullable: A note for the account's own staff.
      - `integration_id` string, nullable, required: The integration the metric belongs to, or null when the data source has no public id.
      - `integration` string: The name the product gives a data source with no public integration_id. Present only in that case. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
      - `level` string, nullable, required: The data view the metric belongs to. Most metrics belong to none and are read as null; when the one they do belong to has no public name, level is null and not_expressible_reason says level_not_available.
      - `type` 'expression' | 'function' | 'filtered', nullable, required: Which of the three kinds it is.
      - `is_system_defined` boolean, required: Whether the metric is one the platform ships. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
      - `expression` CalculatedMetricExpression, required: A metric written as a formula.
        - `format` 'none' | 'time' | 'currency' | 'percentage' | 'time_millis' | 'text', nullable, required: How the result is formatted. time_millis and text are two older values outside the four the editor offers, published as they are stored.
        - `formula` string[], required: The formula as a flat list of infix tokens: the metric ids as the definition stores them, normally the ones /v1/fields publishes, plus the operators + - * /, parentheses and literal numbers. A metric the product retired can still appear in an older formula.
        - `green_when_increases` boolean, required: Whether a rise of the metric is shown as a good thing.
      - `function` CalculatedMetricFunction, required: A metric aggregated over a period.
        - `metric` string, nullable, required: The metric being aggregated, named as /v1/fields names it.
        - `function` 'average' | 'min' | 'max', nullable, required
        - `time_period` 'day' | 'week' | 'month', nullable, required
      - `filtered` CalculatedMetricFiltered, required: A metric read through a saved filter.
        - `metric` string, nullable, required: The metric being read, named as /v1/fields names it.
        - `filter_key` string, nullable, required: The saved filter it is read through. From /v1/filters/list. It is published as it is stored and may name a filter that was deleted, which /v1/filters/get answers with 404.
      - `not_expressible_reason` 'source_not_available' | 'level_not_available' | 'unreadable', nullable, required: Why the definition is not published as it stands. With source_not_available and with unreadable the three blocks are null; with level_not_available only level is, because the id of a metric does not depend on the data view it is read in. Null when there is nothing to report. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
      - `not_expressible_message` string, nullable: The same thing in a sentence. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
  - `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.

## 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.
- `404`: A key of the request does not name anything this account owns. NOT_FOUND covers reports, sections, widgets, schedules, portal views, brand templates, email templates and language files; TEMPLATE_NOT_FOUND covers a catalog slug, version or section position that stopped being published between the validation and the write. An entity of another account is reported the same way as one that does not exist.
- `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)
