---
title: "Get campaign analytics"
method: GET
path: "/v1/ads/campaigns/{campaignId}/analytics"
tags: ["Ad Insights"]
---

# Get campaign analytics

`GET /v1/ads/campaigns/{campaignId}/analytics`

Returns performance analytics for a whole campaign in one call: summary metrics, a daily
timeline over the requested date range (summed across the campaign's ads), and optional
demographic breakdowns. Breakdowns are fetched live from Meta at the campaign level (one call
per dimension, no per-ad fan-out), so an agency dashboard gets campaign-level age/gender/etc.
without summing thousands of per-ad reads. `campaignId` is the platform campaign id; pass
`platform` when a campaign id could be ambiguous across platforms. If no date range is provided,
defaults to the last 90 days. Date range is capped at 730 days max.

## Path parameters

- `campaignId` string, required

## Query parameters

- `platform` string
- `fromDate` string, date
- `toDate` string, date
- `breakdowns` string

## Response `200`

Campaign analytics

- object
  - `campaign` object
    - `id` string
    - `name` string, nullable
    - `platform` string
    - `status` string, nullable — Effective campaign status (ACTIVE when any child ad is active).
    - `currency` string, nullable — ISO 4217 code of the ad account (e.g. USD, THB). All money values in `summary` and `daily` are in this currency.
  - `backfillPending` boolean — Present and true only on `202` responses: part of the requested date range is still being backfilled from the platform in the background. Retry the same request shortly; it returns 200 once the range is fully ingested.
  - `analytics` object
    - `summary` AdMetrics
      - `spend` number
      - `impressions` integer
      - `reach` integer — Unique people reached in the requested date range. Meta (facebook/instagram) and TikTok: the platform's own de-duplicated reach for the exact range, fetched live and cached up to ~1 hour (may lag recent delivery; on a transient platform error the value temporarily falls back to a sum of per-day reach, which overcounts people reached on multiple days or by multiple child ads). Because it is de-duplicated, reach is NOT additive on these platforms: neither daily values nor child nodes sum to the range total. Google, LinkedIn, X, Pinterest and OpenAI report 0 (reach not synced). Frequency (impressions / reach) is only meaningful for Meta and TikTok.
      - `clicks` integer
      - `ctr` number — Click-through rate (%)
      - `cpc` number — Cost per click
      - `cpm` number — Cost per 1000 impressions
      - `engagement` integer
      - `conversions` number — Count of conversion events over the requested date range. FRACTIONAL: attribution splits one conversion across touchpoints and Google additionally reports modeled conversions, so values like 0.347 are normal. Meta: events matching the campaign's promoted_object.custom_event_type (PURCHASE, LEAD, etc.). Google: the account's tracked conversions. X and LinkedIn: their reported website/lead conversions (added 2026-07). 0 for non-conversion campaigns or when no events have fired.
      - `costPerConversion` number — Derived spend / conversions in the same currency as spend. 0 when conversions is 0.
      - `actions` object — Per-action-type counts summed over the date range, keyed by the platform's action-type names. Meta: raw Insights action_type keys (link_click, offsite_conversion.fb_pixel_purchase, onsite_conversion.lead_grouped, ...) — both engagement and conversion events. X: conversion types (purchase, sign_up, site_visit, download, custom). LinkedIn: conversion types (post_click, post_view, lead_gen). Google returns {} (its per-action names aren't synced per ad). Empty object when no actions are reported. NOTE: keys differ by platform, so branch on the ad's platform when interpreting them.
      - `actionValues` object — Monetary mirror of `actions`, from Meta's Insights `action_values[]` array. Same keying — values are the revenue attributed to each action_type, in ad-account native currency (same unit as `spend`; see the campaign node's `currency` field). Use this to compute revenue-per-event (e.g. avg purchase value). Meta-only; other platforms return {}.
      - `purchaseValue` number — Convenience sum of purchase-type action values — picked from `actionValues` via the same priority list as `conversions` so both fields describe the same events. In ad-account native currency. 0 when the campaign has no purchase event configured. Meta-only.
      - `roas` number — Return on ad spend — derived as `purchaseValue / spend`. 0 when `spend` is 0. Equivalent to Meta's `purchase_roas` under default attribution. At ad-set and campaign levels this is recomputed from summed purchaseValue + spend (NOT averaged across children) so it's mathematically correct at every rollup level.
      - `videoPlayActions` integer — Meta video ads only (0 for non-video ads and other platforms), like all video* fields below. Number of times the video started playing (Meta `video_play_actions`), summed over the date range and across children at ad-set/campaign level.
      - `video30SecWatchedActions` integer — Views of at least 30 seconds (or to the end, for shorter videos). Meta `video_30_sec_watched_actions`.
      - `videoThruplayWatchedActions` integer — ThruPlays (watched to completion, or at least 15 seconds). Meta `video_thruplay_watched_actions`.
      - `videoP25WatchedActions` integer — Views reaching 25% of the video's length. With the other percentile fields, powers hook/hold/drop-off analysis (e.g. hook rate = videoP25WatchedActions / videoPlayActions). Meta `video_p25_watched_actions`.
      - `videoP50WatchedActions` integer — Views reaching 50% of the video's length. Meta `video_p50_watched_actions`.
      - `videoP75WatchedActions` integer — Views reaching 75% of the video's length. Meta `video_p75_watched_actions`.
      - `videoP95WatchedActions` integer — Views reaching 95% of the video's length. Meta `video_p95_watched_actions`.
      - `videoP100WatchedActions` integer — Views reaching 100% of the video's length. Meta `video_p100_watched_actions`.
      - `videoAvgTimeWatchedActions` number — Average seconds watched per play (Meta `video_avg_time_watched_actions`). Aggregated over date ranges and across children as a play-weighted average (total watch time / total plays), never a plain average of averages.
      - `costPerThruplay` number — Derived `spend / videoThruplayWatchedActions`, in ad-account native currency. Rounded to 4 decimals rather than the usual 2 because a ThruPlay routinely costs well under a cent. 0 when the ad has no ThruPlays.
      - `funnel` AdFunnelCounts — Named conversion-funnel steps, resolved from the same data as `actions` so you never have to parse action-type strings yourself. Meta reports one event under several action types at once (`offsite_conversion.fb_pixel_purchase`, `omni_purchase`, `purchase`, …). Each field below takes the FIRST family member present rather than summing them, which is what makes these counts safe to add up — summing the raw `actions` keys yourself double or triple counts. The same priority order backs `conversions`, so a purchase-optimised campaign reports the identical number in `conversions` and `funnel.purchases`. Every field is 0 when that step never fired. Populated for Meta ads; other platforms report a different action taxonomy and generally leave these at 0 (read `actions` for those). At ad-set and campaign level each step is summed from its per-ad values.
        - `landingPageViews` integer — Landing page views — the visitor actually loaded the destination, unlike a link click. Meta `landing_page_view`.
        - `contentViews` integer — Content views (Meta `ViewContent` pixel event).
        - `searches` integer — On-site searches (Meta `Search` pixel event).
        - `wishlistAdds` integer — Adds to wishlist (Meta `AddToWishlist` pixel event).
        - `cartAdds` integer — Adds to cart (Meta `AddToCart` pixel event).
        - `checkoutsInitiated` integer — Checkouts started (Meta `InitiateCheckout` pixel event).
        - `paymentInfoAdds` integer — Payment details added at checkout (Meta `AddPaymentInfo` pixel event).
        - `purchases` integer — Purchases (Meta `Purchase` pixel event). Pair with `purchaseValue` for revenue.
        - `leads` integer — Leads, from either the website pixel or an instant form — whichever the ad uses.
        - `registrationsCompleted` integer — Completed registrations (Meta `CompleteRegistration` pixel event).
        - `appInstalls` integer — Mobile app installs attributed to the ad.
        - `messagingConversationsStarted` integer — Messaging conversations started within 7 days — the headline metric for click-to-WhatsApp and click-to-Messenger ads.
        - `messagingFirstReplies` integer — Messaging threads where the person sent a first reply.
      - `engagementBreakdown` AdEngagementCounts — The single `engagement` total split into the interactions behind it. Note that `engagement` is not the sum of these: Meta's own `post_engagement` and `page_engagement` totals already contain the individual interactions, and all of them are counted into `engagement`. Use these fields when you need a specific interaction, and `engagement` only as the coarse total it has always been. Meta-only; other platforms leave these at 0.
        - `postEngagement` integer — Meta's own post-engagement total (`post_engagement`).
        - `pageEngagement` integer — Meta's own page-engagement total (`page_engagement`).
        - `reactions` integer — Reactions on the ad's post (`post_reaction`).
        - `comments` integer — Comments on the ad's post.
        - `shares` integer — Shares of the ad's post. Meta reports these under the action type literally named `post`.
        - `saves` integer — Saves of the ad's post (`onsite_conversion.post_save`).
        - `pageLikes` integer — New Page likes attributed to the ad (`like`).
        - `videoViews` integer — 3-second video views (`video_view`). For completion-based counts use `videoThruplayWatchedActions`.
        - `linkClicks` integer — Attributed link clicks (`link_click`). This is the attribution-window count, which differs from the in-session `inline_link_clicks` reported by `GET /v1/ads/{adId}/analytics`.
      - `lastSyncedAt` string, date-time — Present on individual ads only, not on campaign aggregations
    - `daily` object[]
      - `spend` number
      - `impressions` integer
      - `reach` integer — Unique people reached in the requested date range. Meta (facebook/instagram) and TikTok: the platform's own de-duplicated reach for the exact range, fetched live and cached up to ~1 hour (may lag recent delivery; on a transient platform error the value temporarily falls back to a sum of per-day reach, which overcounts people reached on multiple days or by multiple child ads). Because it is de-duplicated, reach is NOT additive on these platforms: neither daily values nor child nodes sum to the range total. Google, LinkedIn, X, Pinterest and OpenAI report 0 (reach not synced). Frequency (impressions / reach) is only meaningful for Meta and TikTok.
      - `clicks` integer
      - `ctr` number — Click-through rate (%)
      - `cpc` number — Cost per click
      - `cpm` number — Cost per 1000 impressions
      - `engagement` integer
      - `conversions` number — Count of conversion events over the requested date range. FRACTIONAL: attribution splits one conversion across touchpoints and Google additionally reports modeled conversions, so values like 0.347 are normal. Meta: events matching the campaign's promoted_object.custom_event_type (PURCHASE, LEAD, etc.). Google: the account's tracked conversions. X and LinkedIn: their reported website/lead conversions (added 2026-07). 0 for non-conversion campaigns or when no events have fired.
      - `costPerConversion` number — Derived spend / conversions in the same currency as spend. 0 when conversions is 0.
      - `actions` object — Per-action-type counts summed over the date range, keyed by the platform's action-type names. Meta: raw Insights action_type keys (link_click, offsite_conversion.fb_pixel_purchase, onsite_conversion.lead_grouped, ...) — both engagement and conversion events. X: conversion types (purchase, sign_up, site_visit, download, custom). LinkedIn: conversion types (post_click, post_view, lead_gen). Google returns {} (its per-action names aren't synced per ad). Empty object when no actions are reported. NOTE: keys differ by platform, so branch on the ad's platform when interpreting them.
      - `actionValues` object — Monetary mirror of `actions`, from Meta's Insights `action_values[]` array. Same keying — values are the revenue attributed to each action_type, in ad-account native currency (same unit as `spend`; see the campaign node's `currency` field). Use this to compute revenue-per-event (e.g. avg purchase value). Meta-only; other platforms return {}.
      - `purchaseValue` number — Convenience sum of purchase-type action values — picked from `actionValues` via the same priority list as `conversions` so both fields describe the same events. In ad-account native currency. 0 when the campaign has no purchase event configured. Meta-only.
      - `roas` number — Return on ad spend — derived as `purchaseValue / spend`. 0 when `spend` is 0. Equivalent to Meta's `purchase_roas` under default attribution. At ad-set and campaign levels this is recomputed from summed purchaseValue + spend (NOT averaged across children) so it's mathematically correct at every rollup level.
      - `videoPlayActions` integer — Meta video ads only (0 for non-video ads and other platforms), like all video* fields below. Number of times the video started playing (Meta `video_play_actions`), summed over the date range and across children at ad-set/campaign level.
      - `video30SecWatchedActions` integer — Views of at least 30 seconds (or to the end, for shorter videos). Meta `video_30_sec_watched_actions`.
      - `videoThruplayWatchedActions` integer — ThruPlays (watched to completion, or at least 15 seconds). Meta `video_thruplay_watched_actions`.
      - `videoP25WatchedActions` integer — Views reaching 25% of the video's length. With the other percentile fields, powers hook/hold/drop-off analysis (e.g. hook rate = videoP25WatchedActions / videoPlayActions). Meta `video_p25_watched_actions`.
      - `videoP50WatchedActions` integer — Views reaching 50% of the video's length. Meta `video_p50_watched_actions`.
      - `videoP75WatchedActions` integer — Views reaching 75% of the video's length. Meta `video_p75_watched_actions`.
      - `videoP95WatchedActions` integer — Views reaching 95% of the video's length. Meta `video_p95_watched_actions`.
      - `videoP100WatchedActions` integer — Views reaching 100% of the video's length. Meta `video_p100_watched_actions`.
      - `videoAvgTimeWatchedActions` number — Average seconds watched per play (Meta `video_avg_time_watched_actions`). Aggregated over date ranges and across children as a play-weighted average (total watch time / total plays), never a plain average of averages.
      - `costPerThruplay` number — Derived `spend / videoThruplayWatchedActions`, in ad-account native currency. Rounded to 4 decimals rather than the usual 2 because a ThruPlay routinely costs well under a cent. 0 when the ad has no ThruPlays.
      - `funnel` AdFunnelCounts — Named conversion-funnel steps, resolved from the same data as `actions` so you never have to parse action-type strings yourself. Meta reports one event under several action types at once (`offsite_conversion.fb_pixel_purchase`, `omni_purchase`, `purchase`, …). Each field below takes the FIRST family member present rather than summing them, which is what makes these counts safe to add up — summing the raw `actions` keys yourself double or triple counts. The same priority order backs `conversions`, so a purchase-optimised campaign reports the identical number in `conversions` and `funnel.purchases`. Every field is 0 when that step never fired. Populated for Meta ads; other platforms report a different action taxonomy and generally leave these at 0 (read `actions` for those). At ad-set and campaign level each step is summed from its per-ad values.
        - `landingPageViews` integer — Landing page views — the visitor actually loaded the destination, unlike a link click. Meta `landing_page_view`.
        - `contentViews` integer — Content views (Meta `ViewContent` pixel event).
        - `searches` integer — On-site searches (Meta `Search` pixel event).
        - `wishlistAdds` integer — Adds to wishlist (Meta `AddToWishlist` pixel event).
        - `cartAdds` integer — Adds to cart (Meta `AddToCart` pixel event).
        - `checkoutsInitiated` integer — Checkouts started (Meta `InitiateCheckout` pixel event).
        - `paymentInfoAdds` integer — Payment details added at checkout (Meta `AddPaymentInfo` pixel event).
        - `purchases` integer — Purchases (Meta `Purchase` pixel event). Pair with `purchaseValue` for revenue.
        - `leads` integer — Leads, from either the website pixel or an instant form — whichever the ad uses.
        - `registrationsCompleted` integer — Completed registrations (Meta `CompleteRegistration` pixel event).
        - `appInstalls` integer — Mobile app installs attributed to the ad.
        - `messagingConversationsStarted` integer — Messaging conversations started within 7 days — the headline metric for click-to-WhatsApp and click-to-Messenger ads.
        - `messagingFirstReplies` integer — Messaging threads where the person sent a first reply.
      - `engagementBreakdown` AdEngagementCounts — The single `engagement` total split into the interactions behind it. Note that `engagement` is not the sum of these: Meta's own `post_engagement` and `page_engagement` totals already contain the individual interactions, and all of them are counted into `engagement`. Use these fields when you need a specific interaction, and `engagement` only as the coarse total it has always been. Meta-only; other platforms leave these at 0.
        - `postEngagement` integer — Meta's own post-engagement total (`post_engagement`).
        - `pageEngagement` integer — Meta's own page-engagement total (`page_engagement`).
        - `reactions` integer — Reactions on the ad's post (`post_reaction`).
        - `comments` integer — Comments on the ad's post.
        - `shares` integer — Shares of the ad's post. Meta reports these under the action type literally named `post`.
        - `saves` integer — Saves of the ad's post (`onsite_conversion.post_save`).
        - `pageLikes` integer — New Page likes attributed to the ad (`like`).
        - `videoViews` integer — 3-second video views (`video_view`). For completion-based counts use `videoThruplayWatchedActions`.
        - `linkClicks` integer — Attributed link clicks (`link_click`). This is the attribution-window count, which differs from the in-session `inline_link_clicks` reported by `GET /v1/ads/{adId}/analytics`.
      - `lastSyncedAt` string, date-time — Present on individual ads only, not on campaign aggregations
      - `date` string, date
    - `breakdowns` object

## Other responses

- `202` — Part of the requested date range predates the ingested history; a background backfill job has been queued. The body has the same shape as the 200 response, carries the currently-available data, and includes `backfillPending: true`. A `Retry-After` header carries the recommended poll interval in seconds. Allow the job a short time to run (typically 1-3 minutes) and submit the request again; once ingestion completes the same request returns 200 with the full range.
- `400` — Invalid parameter (e.g. an unknown `breakdowns` dimension). The message lists the offending value(s) and the supported set.
- `401` — Unauthorized
- `403` — Ads access required. Legacy plans need the Ads add-on; included by default on usage-based plans.
- `404` — Resource not found

---

[API](https://skmtc.net/zernio/apis/zernio-api.md) · [All operations](https://skmtc.net/zernio/apis/zernio-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/zernio/zernio-api/revisions/51932b099b2f/schema)
