Catalog Ads
Advantage+ catalog ads — dynamic product and vehicle ads from a Meta product catalog
Advantage+ catalog ads
Catalog ads (Meta's dynamic product ads / Advantage+ catalog ads) promote items from a Meta product catalog — e-commerce products, vehicle inventory, hotels — with creatives Meta renders per item. One ad, N items: Meta picks which product or vehicle each person sees.
Zernio supports them end-to-end with goal: "catalog_sales" on POST /v1/ads/create, plus two discovery endpoints to find the customer's catalogs and product sets. Everything works with the permissions accounts already granted on connect — no reconnect needed.
Catalog contents are managed in Meta Commerce Manager (or a feed provider), not through this API. Zernio reads the catalogs and runs ads on them. The ad account also needs a Meta Pixel — a Meta requirement for catalog campaigns (see the table below).
1. Find the catalog and product set
Catalogs hang off the ad account's business. List them, then list the chosen catalog's product sets — the product set is what the ad actually promotes.
const { catalogs } = await zernio.ads.listAdCatalogs({
accountId: 'acc_metaads_123',
adAccountId: 'act_1234567890',
});
// [{ id: '1003405825408877', name: 'Vehicle Inventory', vertical: 'vehicles', productCount: 132 }]
const { productSets } = await zernio.ads.listAdCatalogProductSets({
catalogId: catalogs[0].id,
accountId: 'acc_metaads_123',
});
// [{ id: '1003260032095191', name: 'All vehicles', productCount: 132 }]catalogs = client.ads.list_ad_catalogs(
account_id="acc_metaads_123",
ad_account_id="act_1234567890",
)
product_sets = client.ads.list_ad_catalog_product_sets(
catalog_id=catalogs["catalogs"][0]["id"],
account_id="acc_metaads_123",
)curl "https://zernio.com/api/v1/ads/catalogs?accountId=acc_metaads_123&adAccountId=act_1234567890" \
-H "Authorization: Bearer $ZERNIO_API_KEY"
curl "https://zernio.com/api/v1/ads/catalogs/1003405825408877/product-sets?accountId=acc_metaads_123" \
-H "Authorization: Bearer $ZERNIO_API_KEY"2. Create the catalog campaign
goal: "catalog_sales" builds the whole chain: a Sales-objective campaign, an ad set bound to the product set, and a catalog template creative. There is no imageUrl or video — Meta renders the visuals from the catalog items.
The copy fields become the template, and they accept Meta's catalog template tags ({{product.name}}, {{product.price}}, or for vehicle catalogs {{vehicle.make}}, {{vehicle.model}}, {{vehicle.year}}, {{vehicle.price}}), so each rendered item carries its own data.
Required promotedObject field | Why |
|---|---|
productSetId | The items to promote (and the template creative's source). |
pixelId | Meta requires the pixel on catalog ad sets — its "Promoted Object is Required" error actually means the pixel is missing. |
customEventType | The conversion event to optimize toward, e.g. PURCHASE, LEAD, VIEW_CONTENT. |
const result = await zernio.ads.createStandaloneAd({
body: {
accountId: 'acc_metaads_123',
adAccountId: 'act_1234567890',
name: 'Vehicle inventory - catalog',
goal: 'catalog_sales',
budgetAmount: 30,
budgetType: 'daily',
headline: '{{vehicle.year}} {{vehicle.make}} {{vehicle.model}}',
body: 'Find your next car',
description: '{{vehicle.price}}',
callToAction: 'LEARN_MORE',
linkUrl: 'https://example.com/inventory',
countries: ['CL'],
promotedObject: {
productSetId: '1003260032095191',
pixelId: '1729525464415281',
customEventType: 'PURCHASE',
},
},
});result = client.ads.create_standalone_ad(
account_id="acc_metaads_123",
ad_account_id="act_1234567890",
name="Vehicle inventory - catalog",
goal="catalog_sales",
budget_amount=30,
budget_type="daily",
headline="{{vehicle.year}} {{vehicle.make}} {{vehicle.model}}",
body="Find your next car",
description="{{vehicle.price}}",
call_to_action="LEARN_MORE",
link_url="https://example.com/inventory",
countries=["CL"],
promoted_object={
"productSetId": "1003260032095191",
"pixelId": "1729525464415281",
"customEventType": "PURCHASE",
},
)curl -X POST "https://zernio.com/api/v1/ads/create" \
-H "Authorization: Bearer $ZERNIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "acc_metaads_123",
"adAccountId": "act_1234567890",
"name": "Vehicle inventory - catalog",
"goal": "catalog_sales",
"budgetAmount": 30,
"budgetType": "daily",
"headline": "{{vehicle.year}} {{vehicle.make}} {{vehicle.model}}",
"body": "Find your next car",
"description": "{{vehicle.price}}",
"callToAction": "LEARN_MORE",
"linkUrl": "https://example.com/inventory",
"countries": ["CL"],
"promotedObject": {
"productSetId": "1003260032095191",
"pixelId": "1729525464415281",
"customEventType": "PURCHASE"
}
}'Behavior and limits
- Targeting works like any other campaign (countries, age, gender, placements, saved audiences). Broad targeting is fine — Meta matches items to people. Retargeting off pixel events can be layered with
optimizationGoal: "OFFSITE_CONVERSIONS". - Single shape only.
catalog_salescan't be combined withcreatives[],adSetId(attach),dynamicCreative, orplacementAssets— the catalog template is already the multi-item mechanism. - Instagram placement is handled for you: catalog creatives need an explicit Instagram identity, and Zernio falls back to the Page-backed Instagram account automatically when the connected Page has no linked IG.
- The default ad-set optimization is
LINK_CLICKS; override withoptimizationGoal(e.g.OFFSITE_CONVERSIONS). - The legacy
PRODUCT_CATALOG_SALESobjective is retired by Meta on modern ad accounts; Zernio uses the current Sales-objective shape.