Boost post as ad
Creates a paid ad campaign from an existing published post. Creates the full platform campaign hierarchy (campaign, ad set, ad).
API key authentication - use your Zernio API key as a Bearer token
In: header
Zernio post ID (provide this or platformPostId)
Platform post ID (alternative to postId)
Social account ID
Platform ad account ID
length <= 255Available goals vary by platform. Meta (Facebook/Instagram) and TikTok support all 7. LinkedIn supports all except app_promotion. Twitter/X supports engagement, traffic, awareness, video_views, app_promotion. Pinterest and Google Ads support only engagement, traffic, awareness, video_views.
"engagement" | "traffic" | "awareness" | "video_views" | "lead_generation" | "conversions" | "app_promotion"Same geo/demographic fields as the TargetingSpec used by /v1/ads/create.
Geo keys (regions/cities/zips/metros) resolve via
GET /v1/ads/targeting/search?dimension=geo. City radius and lat/lng
customLocations are Meta-only and preserve the boosted post's
social proof (the ad references the existing post).
Meta only. A verbatim Meta-native targeting spec (e.g.
{ "geo_locations": { "cities": [{ "key": "...", "radius": 15, "distance_unit": "kilometer" }] } }),
forwarded unchanged. Mutually exclusive with targeting (sending both is a 400).
Use for advanced fields the structured object does not expose (flexible_spec,
excluded audiences, business places).
"LOWEST_COST_WITHOUT_CAP" | "LOWEST_COST_WITH_BID_CAP" | "COST_CAP" | "LOWEST_COST_WITH_MIN_ROAS"Bid cap in WHOLE currency units (USD: 5 = $5.00; JPY: 100 = ¥100). Required when
bidStrategy is LOWEST_COST_WITH_BID_CAP or COST_CAP. Backward-compat: providing
bidAmount without bidStrategy is treated as LOWEST_COST_WITH_BID_CAP.
Minimum ROAS as a decimal multiplier (e.g. 2.0 = 2.0x ROAS). Required when
bidStrategy is LOWEST_COST_WITH_MIN_ROAS. Sent to Meta as
bid_constraints.roas_average_floor × 10000 (Meta uses fixed-point integers).
Platform-specific options. The platform is derived from accountId;
sending options for a different platform returns a 400. LinkedIn
(campaign bidding and delivery controls) is the only platform with
options today.
Meta only. Tracking specs (pixel, URL tags).
Meta only. Required for housing, employment, credit, or political ads.
Meta (metaads) only. 2-letter ISO country codes the special ad category applies to. Requires specialAdCategories to be set (400 otherwise).
TikTok-only. Custom destination URL for the Spark Ad. Without this, TikTok
Spark Ads have no clickable destination — required for traffic / conversion
objectives. Maps to landing_page_url on the creative entry of /v2/ad/create/
(TikTok SDK AdcreateCreatives.landing_page_url). Ignored on Meta / LinkedIn /
Pinterest / X / Google (those infer the destination from the boosted post).
uriTikTok-only. Call-to-action button label on the Spark Ad creative (e.g.
LEARN_MORE, SHOP_NOW, DOWNLOAD_NOW, SIGN_UP, WATCH_NOW). Maps to
call_to_action on the creative entry of /v2/ad/create/. Pass-through —
the platform validates the value. See TikTok's "Enumeration - Call-to-Action"
reference for the full list.
TikTok-only. Spark Code (creator's auth_code) authorizing cross-creator
Spark Ads — the advertiser can boost a video owned by a DIFFERENT TikTok
account. Without this, boosts are limited to videos owned by the same
account running the ads (same-BC creators only). The creator generates the
code in their TikTok app's Promote settings and shares it with the
advertiser. Maps to auth_code on the creative entry of /v2/ad/create/.
Legal entity that benefits from the ad. Required when targeting EU users
(EU DSA, Article 26). Optional if the ad account has a default beneficiary:
set it once via PATCH /v1/ads/accounts or in Meta Ads Manager, and Meta
fills it in whenever the field is omitted.
length <= 100Legal entity that pays for the ad. Can differ from dsaBeneficiary
(for example, an agency paying for a client's ads). Same rules as
dsaBeneficiary: required for EU targeting unless the ad account has
a default payor.
length <= 100Response Body
application/json
application/json
import Zernio from '@zernio/node';const zernio = new Zernio({ apiKey: process.env.ZERNIO_API_KEY });const { data } = await zernio.adcampaigns.boostPost({ body: { accountId: 'account_abc123', adAccountId: 'adaccount_abc123', name: 'Example', goal: 'engagement', budget: { amount: 0, type: 'daily', }, },});console.log(data);{
"ad": {
"_id": "string",
"name": "string",
"platform": "facebook",
"status": "active",
"configuredStatus": "ACTIVE",
"reviewStatus": "in_review",
"adType": "boost",
"goal": "engagement",
"isExternal": true,
"budget": {
"amount": 0,
"type": "daily"
},
"metrics": {
"spend": 0,
"impressions": 0,
"reach": 0,
"clicks": 0,
"ctr": 0,
"cpc": 0,
"cpm": 0,
"engagement": 0,
"conversions": 0,
"costPerConversion": 0,
"actions": {
"link_click": 160,
"post_engagement": 300,
"offsite_conversion.fb_pixel_purchase": 42
},
"actionValues": {
"offsite_conversion.fb_pixel_purchase": 2456.78,
"offsite_conversion.fb_pixel_add_to_cart": 980.5
},
"purchaseValue": 0,
"roas": 0,
"videoPlayActions": 0,
"video30SecWatchedActions": 0,
"videoThruplayWatchedActions": 0,
"videoP25WatchedActions": 0,
"videoP50WatchedActions": 0,
"videoP75WatchedActions": 0,
"videoP95WatchedActions": 0,
"videoP100WatchedActions": 0,
"videoAvgTimeWatchedActions": 0,
"lastSyncedAt": "2019-08-24T14:15:22Z"
},
"platformAdId": "string",
"platformAdAccountId": "string",
"platformCampaignId": "string",
"platformAdSetId": "string",
"campaignName": "string",
"adSetName": "string",
"platformObjective": "OUTCOME_SALES",
"optimizationGoal": "OFFSITE_CONVERSIONS",
"platformAdAccountName": "Zernio - previously Late",
"platformCreatedAt": "2019-08-24T14:15:22Z",
"bidStrategy": "LOWEST_COST_WITHOUT_CAP",
"bidAmount": 5,
"roasAverageFloor": 2,
"promotedObject": {
"custom_event_type": "PURCHASE",
"pixel_id": "string",
"page_id": "string",
"application_id": "string",
"product_set_id": "string"
},
"creative": {
"thumbnailUrl": "string",
"imageUrl": "string",
"videoId": "string",
"videoUrl": "string",
"objectType": "string",
"objectStoryId": "string",
"effectiveObjectStoryId": "string",
"effectiveInstagramMediaId": "string",
"instagramUserId": "string",
"instagramPermalinkUrl": "string",
"mediaUrls": [
"string"
],
"body": "string",
"googleHeadline": "string",
"googleDescription": "string",
"linkUrl": "string",
"pinterestImageUrl": "string",
"pinterestTitle": "string",
"pinterestDescription": "string"
},
"targeting": {},
"schedule": {
"startDate": "2019-08-24T14:15:22Z",
"endDate": "2019-08-24T14:15:22Z"
},
"rejectionReason": "string",
"createdAt": "2019-08-24T14:15:22Z",
"updatedAt": "2019-08-24T14:15:22Z"
},
"message": "string"
}{
"error": "Unauthorized"
}Pause or resume many campaigns POST
Process up to 50 campaigns in one call. Each campaign is updated concurrently and the response contains a per-campaign result so a single bad row does not fail the whole batch.
Duplicate a campaign POST
Duplicates a campaign, including its ad sets, ads, creatives, and targeting by default (`deepCopy: true`). The copy is created paused so callers can review before launching. Per-platform implementation: - **Meta** uses the native `POST /{campaign-id}/copies` endpoint. - **TikTok** has no native copy primitive; Zernio walks the source graph (`/v2/campaign/get/`, `/v2/adgroup/get/`, `/v2/ad/get/`) and recreates each entity via the corresponding `/create/` endpoints, carrying over budget / targeting / bid_type / bid_price / deep_bid_type / creative fields. Spark Ad linkage (`tiktok_item_id`) is preserved. - **LinkedIn** has no native copy primitive; Zernio walks the source CampaignGroup → Campaigns → Creatives and recreates each entity, carrying over `type` / `costType` / `unitCost` / `optimizationTargetType` / `creativeSelection` / `objectiveType` / `format` / `dailyBudget` / `totalBudget` / `targetingCriteria` / `runSchedule` and every Creative's `content` object verbatim. `statusOption: INHERITED_FROM_SOURCE` is evaluated **per entity**: any Group / Campaign / Creative whose source is `ACTIVE` gets its clone activated too. Duplicating an ACTIVE campaign with `INHERITED_FROM_SOURCE` starts a second front of spend the moment the clone activates — the safe default is `PAUSED`. The new hierarchy is asynchronous to materialize in our DB — we trigger sync discovery automatically. Set `syncAfter: false` to skip and poll `/v1/ads/tree` on your own cadence. Other platforms return 501 Not Implemented.