---
title: "List dimensions and metrics for a connection"
method: POST
path: "/fields"
tags: ["Fields"]
---

# List dimensions and metrics for a connection

`POST /fields`

## Request

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

Returns the set of field_ids you can pass to /v1/query, plus default_dimension and default_metric suggestions. By default returns the standard catalog plus the calculated metrics defined in the account for the integration, listed as regular metrics whose field_id can be requested in /v1/query.fields. To include account-specific custom fields (e.g. Facebook Ads custom conversions, GA4 custom metrics and dimensions, or the campaign name dimensions of a cross_platform accounts group), set include_custom_fields=true and provide connection_key + account_id. include_custom_fields is only allowed when the integration's catalog declares supports_custom_fields_per_account=true. For all other integrations, sending include_custom_fields (or connection_key/account_id) returns an error.

## Request body

- FieldsRequest
  - `integration_id` string, required
  - `connection_key` string: REQUIRED when include_custom_fields is true. Also REQUIRED, with include_custom_fields false or absent, for hubspot with data_view contacts or deals, whose field list is always account specific. MUST NOT be present in any other case, nor for integrations whose supports_custom_fields_per_account is false. For cross_platform, account_id MUST equal connection_key (both are the key of the accounts group).
  - `account_id` string: REQUIRED when include_custom_fields is true. Also REQUIRED, with include_custom_fields false or absent, for hubspot with data_view contacts or deals, whose field list is always account specific. MUST NOT be present in any other case, nor for integrations whose supports_custom_fields_per_account is false. For cross_platform, account_id MUST equal connection_key (both are the key of the accounts group).
  - `data_view` string: Required when the integration exposes data_views. For example, REQUIRED for `google_ads` (one of its 22 data_views: `customer`, `campaign`, `ad_group`, `keyword_view`, etc.). NOT applicable to `facebook_ads`, `ga4`, `google_search_console`, `twitter_analytics` or `cross_platform`. Consult /v1/integrations/detail for the full list of valid values per integration.
  - `include_custom_fields` boolean: Whether to include account-specific custom fields (custom conversions for facebook_ads, custom dimensions/metrics for ga4, the campaign name dimensions of the accounts group for cross_platform). Default false. Only allowed when the integration's catalog declares supports_custom_fields_per_account=true. When true, both connection_key and account_id are REQUIRED. When false or absent, connection_key and account_id MUST NOT be present in the request. The deprecated key `include_custom_conversions` is rejected with an error -- use `include_custom_fields` instead.
  - `settings` object: Per-integration settings of this call. Only mailchimp and amazon_ads read one. For amazon_ads an unknown key answers UNKNOWN_PARAM, and creative_type and tactics are rejected here: they change what /v1/query reads, not which fields exist.
    - `entity` 'campaign' | 'list': REQUIRED for mailchimp: which of the two Mailchimp levels the fields belong to. It must agree with data_view (campaign_* with campaign, list_* with list).
    - `ad_types` string: Optional for amazon_ads: a comma separated list of ad types (sponsored_products, sponsored_brands, sponsored_display) whose fields are returned. Each one must be offered by the data_view of the call. When absent, the fields of every ad type of that data_view are returned.

## 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
  - FieldsListResponse
    - `status` 'ok', required
    - `data` FieldsListData, required: The dimensions and metrics available for one integration and data view.
      - `fields` Field[], required
        - `field_id` string, required
        - `field_name` string, required
        - `field_description` string
        - `field_type` 'text' | 'number' | 'currency' | 'percent' | 'date' | 'date_month' | 'url' | 'country' | 'region' | 'hour', required: Public field type. NOTE for ga4 temporal dimensions (date, week, month): the value format in /v1/query responses depends on the query. (a) If one of them is the only dimension of the query ("trend" mode), values come back with separators: `date` as YYYY-MM-DD (e.g. 2026-05-10), `month` as YYYY-MM (e.g. 2026-05), `week` as a YYYY-MM-DD date. (b) If the query has any other dimension (non-trend), values come back in raw GA4 format with NO year prefix for week/month: `date` as YYYYMMDD (e.g. 20260512), `month` as MM (e.g. 05), `week` as WW (e.g. 20). NOTE for percent metrics (CTR, conversion rate, engagement rate, bounce rate, etc.): values come back in 0-100 scale (e.g. `1.22` means 1.22%). Divide by 100 client-side if a 0-1 representation is needed.
        - `dim_met` 'dimension' | 'metric', required
      - `default_dimension` string: The dimension the app picks when none is chosen. Present only when the catalog declares one.
      - `default_metric` string: The metric the app picks when none is chosen. Present only when the catalog declares one.
    - `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)
