---
title: "List campaigns"
method: GET
path: "/v1/ads/campaigns"
tags: ["Ad Campaigns"]
---

# List campaigns

`GET /v1/ads/campaigns`

Returns campaigns as virtual aggregations over ad documents grouped by platform campaign ID.
Metrics (spend, impressions, clicks, etc.) are summed across all ads in each campaign.
Campaign status is derived from child ad statuses (active > pending_review > paused > error > completed > cancelled > rejected).

## Query parameters

- `includeEmpty` boolean
- `page` integer
- `limit` integer
- `source` 'zernio' | 'all'
- `platform` 'facebook' | 'instagram' | 'tiktok' | 'linkedin' | 'pinterest' | 'google' | 'twitter' | 'openai'
- `status` 'active' | 'paused' | 'pending_review' | 'rejected' | 'completed' | 'cancelled' | 'error'
- `adAccountId` string
- `pageId` string
- `accountId` string
- `profileId` string
- `fromDate` string, date
- `toDate` string, date

## Response `200`

Paginated campaigns

- object
  - `campaigns` AdCampaign[]
    - `platformCampaignId` string
    - `platform` 'facebook' | 'instagram' | 'tiktok' | 'linkedin' | 'pinterest' | 'google' | 'twitter' | 'openai'
    - `campaignName` string
    - `status` 'active' | 'paused' | 'pending_review' | 'rejected' | 'completed' | 'cancelled' | 'error'
    - `reviewStatus` 'in_review' | 'approved' | 'rejected' | 'with_issues' — Platform-side review state, independent of the delivery `status` and the `configuredStatus` on/off toggle. `in_review` means the platform is still reviewing. Absent when the platform reports no review signal (e.g. a paused ad whose review state is masked behind the pause).
    - `platformCampaignStatus` string, nullable — Raw platform-level campaign status (Meta `effective_status`).
    - `campaignIssuesInfo` object[], nullable — Platform-reported campaign issues (Meta `issues_info[]`).
    - `adCount` integer
    - `budget` object, nullable — Effective budget (back-compat). Use `budgetLevel` to disambiguate CBO vs ABO.
      - `amount` number
      - `type` 'daily' | 'lifetime'
    - `campaignBudget` object, nullable — Campaign-level budget (CBO). Null for ABO campaigns.
      - `amount` number
      - `type` 'daily' | 'lifetime'
    - `budgetLevel` 'campaign' | 'adset', nullable — Canonical CBO/ABO indicator. See AdTreeCampaign.budgetLevel.
    - `isBudgetScheduleEnabled` boolean — Meta-only. Mirrors Campaign.is_budget_schedule_enabled.
    - `currency` string, nullable — ISO 4217 currency code for all budget amounts. Budgets are NOT normalized to USD.
    - `metrics` 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
    - `platformAdAccountId` string
    - `platformAdAccountName` string, nullable — Human-readable advertiser/account name from the platform. Refreshed on every sync.
    - `accountId` string
    - `profileId` string
    - `advertisingChannelType` string, nullable — Google-only. Raw campaign.advertising_channel_type. See AdTreeCampaign.advertisingChannelType.
    - `platformObjective` string, nullable — Raw Meta campaign objective (e.g. OUTCOME_SALES, OUTCOME_LEADS, OUTCOME_TRAFFIC)
    - `optimizationGoal` string, nullable — Optimization goal shared across ad sets, or comma-separated values when ad sets differ. Meta: e.g. OFFSITE_CONVERSIONS, VALUE, LEAD_GENERATION. LinkedIn: the campaign optimizationTargetType (e.g. MAX_CLICK, MAX_IMPRESSION, NONE); `NONE` with a manual costType is a campaign LinkedIn will not deliver.
    - `bidStrategy` 'LOWEST_COST_WITHOUT_CAP' | 'LOWEST_COST_WITH_BID_CAP' | 'COST_CAP' | 'LOWEST_COST_WITH_MIN_ROAS' — Meta bid strategy. Same enum applies at campaign and ad-set level; ad-set value (when set) overrides campaign-level. Cross-field rules: - `LOWEST_COST_WITHOUT_CAP` (default): auto-bid, forbids `bidAmount` and `roasAverageFloor`. - `LOWEST_COST_WITH_BID_CAP` / `COST_CAP`: require `bidAmount` (whole currency units). - `LOWEST_COST_WITH_MIN_ROAS`: requires `roasAverageFloor` (decimal multiplier, 2.0 = 2.0x). Source: facebook-business-sdk-codegen api_specs/specs/enum_types.json (`AdSet_bid_strategy`, `Campaign_bid_strategy`).
    - `bidAmount` number, nullable — Representative bid from the top-spending ad set (whole currency units). Meta: populated when bidStrategy is LOWEST_COST_WITH_BID_CAP or COST_CAP. LinkedIn: the campaign unitCost, ungated, where 0 is a real delivery-stopping value.
    - `roasAverageFloor` number, nullable — Representative ROAS floor from the top-spending ad set. Decimal multiplier (2.0 = 2.0x).
    - `promotedObject` object, nullable — Meta promoted object at campaign level (conversion event details)
      - `custom_event_type` string
      - `pixel_id` string
      - `page_id` string
    - `earliestAd` string, date-time
    - `latestAd` string, date-time
  - `pagination` Pagination
    - `page` integer
    - `limit` integer
    - `total` integer
    - `pages` integer

## Other responses

- `400` — Invalid request
- `401` — Unauthorized
- `403` — Ads access required. Legacy plans need the Ads add-on; included by default on usage-based plans.

---

[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)
