---
title: "List available integrations"
method: POST
path: "/integrations"
tags: ["Integrations"]
---

# List available integrations

`POST /integrations`

## Request

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

Returns the set of integrations exposed by the public API, with the category of each one, whether its field list is account specific and whether its data can be read through the query endpoints. The catalog grows incrementally; check this endpoint to discover which integrations are available before calling /integrations/detail, /connections, /fields or /query.

## Request body

- EmptyBody: No parameters.

## 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
  - IntegrationsListResponse
    - `status` 'ok', required
    - `data` IntegrationsListData, required: The catalog as a list. Only the identity fields of each integration travel here; the data views, the settings and the filter catalog are answered by /v1/integrations/detail.
      - `integrations` Integration[], required
        - `id` string, required
        - `name` string, required
        - `category` string, required
        - `supports_custom_fields_per_account` boolean, required
        - `notes` string[]: Integration-wide advisories that do not fit in filters/settings/data_views (e.g. data freshness windows, upstream API quotas, billing notes). Optional: only present when there are global notes to publish. Each entry is a short, audience-API sentence; the array is unordered and clients should treat it as a bag of standalone notes.
        - `data_views` DataView[], nullable: The data views of the integration, or null when it has none (facebook_ads, ga4, google_search_console, twitter_analytics and cross_platform).
          - `id` string, required
          - `name` string, required
          - `description` string: Public-facing summary explaining what this data_view returns. Short (1-2 sentence) hint for the API consumer about the underlying scope/endpoint and notable restrictions; does NOT replace the integration-level `filters.note` which covers global concerns (settings, prefix, custom fields, etc.). Always present in real responses (iter 24+); omitted from compact examples in this spec for readability but emitted by /v1/integrations/detail for every data_view.
          - `type` 'campaign' | 'list': Mailchimp-only: indicates which entity (campaign|list) this data_view affines with. The API validates that data_view.type matches settings.entity for mailchimp queries. Not present for other integrations.
        - `settings` Setting[], nullable: The settings the integration reads, or null when it reads none.
          - `id` string, required
          - `name` string, required
          - `description` string
          - `required` boolean: When true, the setting must always be sent in /v1/query. Only one setting is declared required without a recommended_value: mailchimp.entity.
          - `dynamic` boolean: When true, the valid values are account-specific (discover via /v1/connections). The API does not validate the value against a closed list, but the setting is still REQUIRED when its applies_to_data_views matches.
          - `multi_select` boolean: When true, the value is a JSON array of strings (each validated against `options[]`).
          - `note` string
          - `applies_to_data_views` string[]: When present, the setting is REQUIRED only when the current data_view is in this list. When absent, the setting applies to every data_view of the integration.
          - `recommended_value` union: A value the upstream native API would typically apply when this setting is not explicitly customized. PROVIDED ONLY AS A RECOMMENDATION for clients that do not have a specific preference. The API does NOT inject this value automatically: the client must always include the setting in /v1/query when it applies (per `applies_to_data_views`). Reporting Ninja does not define the recommended_value; it mirrors the upstream API's documented default behavior.
            - string
            - string[]
          - `options` object[]
            - `value` string, required
            - `label` string, required
          - `options_by_data_view` object: Per-data_view options map. Used when the valid values of a setting depend on the current data_view (e.g. pinterest_organic.pinterest_sort_by: 5 values for top_pins, 9 for top_video_pins).
        - `filters` FiltersDescriptor: Describes the filter capabilities of an integration. Two shapes coexist: (a) integrations with a closed catalog of filter fields (e.g. facebook_ads, google_ads) expose `fields[]` with the operators valid for each filter field; (b) integrations with dynamic filter fields (e.g. ga4) expose `note`, `dimension_operators` and `metric_operators` and require the client to send `type_of_field` per filter.
          - `supported` boolean, required
          - `combination` 'and' | 'or' | 'and_or': How filters can be combined. `and` (facebook_ads, google_ads, microsoft_ads): all filters are AND-ed; the client must NOT send `combination` per filter (rejected for microsoft_ads). `and_or` (ga4): each filter may include `combination` (`AND` default, or `OR`); see ga4 notes for the iter 3 caveat about the second filter defining the group combination.
          - `fields` FilterField[]: Present for integrations with a closed catalog of filter fields.
            - `id` string, required
            - `name` string, required
            - `category` string, required
            - `operators` string[], required
            - `allowed_values` string[]: Closed list of valid filter values (case-sensitive). Present only for fields with a fixed enum (e.g. microsoft_ads CampaignStatus / AdDistribution / DeviceType). When present, every value sent in /v1/query.filters[].value MUST belong to this list (each element if value is an array).
            - `data_views` string[]: List of data_view ids where this filter field is valid. Present for integrations where filter fields depend on the data_view (e.g. microsoft_ads). If absent, the field is valid for ALL data_views of the integration.
            - `aliases` string[]: Other ids accepted for this same filter field, typically the id the field has in /v1/fields (e.g. facebook_ads `campaign_name` for the filter field `campaign.name`). Any of them may be sent as filters[].field and behaves exactly like the canonical id; `id` remains the canonical one and the one returned in this catalog. Present only for fields that have synonyms.
            - `type_of_field` string: Whether the field is a dimension or a metric. Present for the integrations that ask the client to send type_of_field with each filter, and for the ones whose catalog states it field by field.
          - `note` string: Free-text guidance for the client: how filters can be combined, per-field caveats, matching rules for text operators, or the reason filters are not supported. Present for most integrations, and always worth reading before building a filter.
          - `dimension_operators` string[]: Operators valid for dimension filters. Present for integrations with dynamic filter fields.
          - `metric_operators` string[]: Operators valid for metric filters. Present for integrations with dynamic filter fields.
        - `queryable` boolean, required: Whether /v1/query and /v1/query/async accept this integration. When false, everything else is available -- the integration can be connected, its fields and filters can be read and its widgets can be created -- and only reading its rows through the query endpoints is refused.
        - `not_queryable_reason` string: Why the data of this integration cannot be read through the query endpoints. Present only when queryable is false.
    - `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)
