Zernio
APIFor you
Zernio
APIFor you
Platforms

Meta Ads

Overview

Set up

Connect

Build

CampaignsAd setsCreativesCreative libraryPreviewsBoost a postLead Gen Forms

Target

TargetingCustom Audiences

Ad types

Creative testingConversion campaignsCatalog adsMessaging & call adsClick-to-WhatsApp adsReach & frequency

Measure

InsightsMeta PixelsConversions APIAd URL tracking tags

Operate

Duplicate & lifecycleAccount & ops readsAd LibraryAd comments

Reference

Enums, limits & error codes
Dashboard
OverviewPlatformsAPI ReferenceResources
Meta Ads

Account & ops reads

Read a Meta ad account's change log, A/B studies, finances, labels, budget schedules, Business portfolios, Instagram identities and app-promotion setup.


Read what surrounds the campaign tree: who changed what, what is being tested, how much money is left, and how the account is organized.

Before you start

Every endpoint here is Meta only. Response shapes vary by endpoint. The discovery reads below normalize Meta's identity and app data.

All of them take accountId. All but the business list and the budget schedules also take adAccountId. The budget schedules are scoped by campaignId or adSetId instead.

Endpoints on this page

EndpointReturns
GET /v1/ads/activityThe ad account's change history
GET /v1/ads/studiesA/B tests and lift studies
GET /v1/ads/accounts/financeBalance, spend, spend cap and funding source
GET /v1/ads/labelsAd labels
GET /v1/ads/high-demand-periodsScheduled budget increases
GET /v1/ads/businessesThe Business Manager portfolios the connection belongs to
GET /v1/ads/instagram-accountsInstagram identities an ad can run as, and the resolved default
GET /v1/ads/advertisable-applicationsApps this ad account can promote
GET /v1/ads/ios-fourteen-campaign-limitsAn app's iOS 14 SKAdNetwork campaign limits

Two related reads live on their own pages: the ad set details and learning phase and pixel firing stats.

Change / audit log

GET /v1/ads/activity returns who changed what and when, with Meta's translated_event_type and the structured before and after values in extra_data.

TypeScript
import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: activity } = await zernio.adaccounts.getAdsActivityLog({
  query: {
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    adAccountId: 'act_1234567890',
    since: '2027-02-01',
    until: '2027-02-20'
  }
});

Response (200), one row:

JSON
{
  "adAccountId": "act_1234567890",
  "data": [
    {
      "event_type": "update_campaign_budget",
      "translated_event_type": "Edited campaign budget",
      "object_id": "120250000000000000",
      "extra_data": "{\"old_value\":\"7500\",\"new_value\":\"25000\"}"
    }
  ],
  "paging": { "after": "QVFIUm..." }
}

objectId filters the returned page rather than the query. Meta's activities edge has no server-side per-object filter.

To walk one object's full history, raise limit (1 to 200, default 50). Pass the previous response's paging.after back as after until after comes back null.

Terminal
curl "https://zernio.com/api/v1/ads/activity?accountId=66b2e19d8c3f5a7e9d0b1c2d&adAccountId=act_1234567890&objectId=120250000000000000&limit=200&after=QVFIUm..." \
  -H "Authorization: Bearer $ZERNIO_API_KEY"

Response (200), the last page:

JSON
{
  "adAccountId": "act_1234567890",
  "data": [],
  "paging": { "after": null }
}

A/B tests & lift studies

GET /v1/ads/studies returns the account's /ad_studies rows verbatim.

The default projection covers the id, name, type, timing and the cells with their split percentages. fields overrides it.

TypeScript
const { data: studies } = await zernio.adaccounts.listAdStudies({
  query: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', adAccountId: 'act_1234567890' }
});

Response (200), one row:

JSON
{
  "adAccountId": "act_1234567890",
  "data": [
    { "id": "7412998877665544", "name": "Creative split March", "type": "SPLIT_TEST" }
  ],
  "paging": { "after": null }
}

Account finances

GET /v1/ads/accounts/finance returns the money numbers in whole currency units.

TypeScript
const { data: finance } = await zernio.adaccounts.getAdAccountFinance({
  query: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', adAccountId: 'act_1234567890' }
});

Response (200):

JSON
{
  "adAccountId": "act_1234567890",
  "currency": "USD",
  "balance": 214.50,
  "amountSpent": 7823.11,
  "spendCap": null,
  "fundingSource": { "displayString": "Mastercard *4985", "type": 1 }
}

spendCap: null means no cap. This is the account-level cap, a billing guardrail.

The campaign-level one is a delivery setting on platformSpecificData.spendCap.

Ad labels

GET /v1/ads/labels returns the account's /adlabels rows with limit and after paging.

Labels are how Ads Manager users group campaigns, ad sets, ads and creatives across the tree. Reading them lets you mirror those groupings in your own interface.

TypeScript
const { data: labels } = await zernio.adaccounts.listAdLabels({
  query: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', adAccountId: 'act_1234567890' }
});

Response (200), one row:

JSON
{
  "adAccountId": "act_1234567890",
  "data": [
    {
      "id": "120250000000000021",
      "name": "Q1 evergreen",
      "created_time": "2027-01-04T09:00:00+0000",
      "updated_time": "2027-02-11T16:42:03+0000"
    }
  ],
  "paging": { "after": null }
}

High demand periods

GET /v1/ads/high-demand-periods returns Meta's budget schedules as raw rows. These are the scheduled increases behind a weekend or a sale.

Pass exactly one of campaignId or adSetId.

TypeScript
const { data: schedules } = await zernio.adaccounts.listHighDemandPeriods({
  query: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', campaignId: '120250000000000000' }
});

Response (200):

JSON
{
  "objectId": "120250000000000000",
  "data": [
    {
      "id": "6512348899001",
      "time_start": 1809216000,
      "time_end": 1809475200,
      "budget_value": 2,
      "budget_value_type": "MULTIPLIER",
      "recurrence_type": "ONE_TIME"
    }
  ],
  "paging": { "after": null }
}

The Graph edge exists on campaign and ad set nodes only, so an account-level read is a Meta #100 error and Zernio does not offer that shape.

POST /v1/ads/high-demand-periods creates one, with the same target rule: exactly one of campaignId or adSetId.

TypeScript
const { data: schedule } = await zernio.adaccounts.createHighDemandPeriod({
  body: {
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    campaignId: '120250000000000000',
    budgetValue: 2,
    budgetValueType: 'MULTIPLIER',
    timeStart: 1809216000,
    timeEnd: 1809475200,
    recurrenceType: 'ONE_TIME'
  }
});

Response (201):

JSON
{
  "objectId": "120250000000000000",
  "id": "6512348899001"
}

budgetValueType takes one of two values:

  • ABSOLUTE: a whole-unit amount in the account currency
  • MULTIPLIER: a factor of the existing budget, where 2 doubles it

timeStart and timeEnd are Unix seconds on a 15-minute boundary. recurrenceType is ONE_TIME, WEEKLY or MONTHLY.

A campaign cannot mix ABSOLUTE and MULTIPLIER schedules. The second type is rejected with Meta's "Can't mix your budget scaling selection".

Business portfolios

GET /v1/ads/businesses lists the Business Manager portfolios the connected Meta user belongs to. It reads Meta's /me/businesses, with limit and after paging.

TypeScript
const { data: businesses } = await zernio.adaccounts.listMetaBusinesses({
  query: { accountId: '66b2e19d8c3f5a7e9d0b1c2d' }
});

Response (200):

JSON
{
  "data": [
    {
      "id": "1049283746152738",
      "name": "Acme Holdings",
      "verification_status": "verified",
      "created_time": "2024-11-03T09:12:44+0000"
    }
  ],
  "paging": { "after": null }
}

This read is token-scoped, so it takes no adAccountId.

System-user tokens can return an empty list. In that case, supply a known business ID when creating an ad account.

GET /v1/ads/business-centers is the TikTok equivalent despite the similar name.

Instagram ad identities

GET /v1/ads/instagram-accounts discovers which Instagram identity an ad on this Page can run as. Call it before you set instagramAccountId on boost or messaging ads.

It walks connected_instagram_accounts, Page linkage and Page-backed identities. A best-effort Business fallback follows, and its permission errors do not fail the read.

TypeScript
const { data: identities } = await zernio.adaccounts.listAdsInstagramAccounts({
  query: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', adAccountId: 'act_1234567890' }
});

const igUserId = identities.resolved.igUserId;

Response (200), trimmed:

JSON
{
  "accounts": [{ "igUserId": "17841400000000000", "username": "example", "isPageBacked": false, "source": "page_backed" }],
  "pages": [{ "pageId": "123456789", "name": "Example Page", "instagramBusinessAccount": { "igUserId": "17841400000000000", "username": "example" } }],
  "resolved": { "pageId": "123456789", "igUserId": "17841400000000000", "source": "page_backed" }
}

accounts lists every identity discovered, each tagged with its source (ad_account, page_backed or business).

resolved is the identity the shared ad-creation resolver would pick. It is the same one an ad runs as if you omit instagramAccountId. A null igUserId means nothing resolved.

Format-specific fallbacks Meta applies only at creative creation time are not predicted here.

App promotion discovery

Two reads support app_promotion campaigns:

  • GET /v1/ads/advertisable-applications lists the Meta apps this ad account can promote
  • GET /v1/ads/ios-fourteen-campaign-limits reads an app's iOS 14 SKAdNetwork campaign limits

Both take accountId and adAccountId. The limits read also takes applicationId from the apps list.

Terminal
curl "https://zernio.com/api/v1/ads/advertisable-applications?accountId=66b2e19d8c3f5a7e9d0b1c2d&adAccountId=act_1234567890" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"

Response (200):

JSON
{
  "applications": [
    {
      "id": "123456789",
      "name": "Example App",
      "supportedPlatforms": ["IOS", "ANDROID"],
      "storeUrls": { "iphone": "https://apps.apple.com/app/id123456789", "google_play": "https://play.google.com/store/apps/details?id=com.example.app" }
    }
  ]
}

A listed app still needs a configured mobile platform and store URL before it can run install promotion.

Take applications[].id as applicationId:

Terminal
curl "https://zernio.com/api/v1/ads/ios-fourteen-campaign-limits?accountId=66b2e19d8c3f5a7e9d0b1c2d&adAccountId=act_1234567890&applicationId=123456789" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"

limits is null when Meta has none configured for the app. A non-null read does not by itself confirm the app is set up for iOS promotion.

If it fails

A 400 on the budget schedules read means neither scope was given:

JSON
{
  "error": "Provide exactly one of campaignId or adSetId",
  "type": "invalid_request_error",
  "code": "invalid_field_value",
  "param": "campaignId"
}

Name a campaign or an ad set.

A 501 means the account is not Meta: every read on this page is Meta only.

Related

  • Campaigns: the tree these reads sit around.
  • Insights: performance rather than configuration.
  • Lifecycle: the writes the change log records.
  • Activity log and Account finance: every field.
Was this page helpful?

Duplicate & lifecycle

Validate a Meta create before it spends, pause and resume delivery, duplicate a campaign, ad set or ad, and delete a campaign.

Ad Library

Search Meta's public ad archive with GET /v1/ads/library, and know which ads it actually covers before you build a competitor screen.

On this page

Before you startEndpoints on this pageChange / audit logA/B tests & lift studiesAccount financesAd labelsHigh demand periodsBusiness portfoliosInstagram ad identitiesApp promotion discoveryIf it failsRelated