Zernio
Zernio
API Reference

Campaigns and Ads

Overview

Get campaign treeGETGet daily account metricsGET

Campaigns

List campaignsGETCreate a standalone campaignPOSTUpdate a campaignPUTDuplicate a campaignPOSTDelete a campaignDELETEPause or resume a campaignPUTPause or resume many campaignsPOSTAttach extension assets to a Google Search campaignPOST

Ad Sets

Live ad-set details incl. learning phaseGETUpdate an ad setPUTDuplicate an ad setPOSTDelete an ad setDELETEPause or resume a single ad setPUTList Search keywordsGET

Ads

List adsGETGet ad detailsGETCreate standalone adPOSTBoost post as adPOSTUpdate adPUTDuplicate an adPOSTCancel an adDELETEPause or resume a single adPUT

Other

Add Search keywords to an ad groupPOSTCreate a standalone ad groupPOSTCreate a Google Ads portfolio bid strategyPOSTRead a campaign's current biddingGETRead a Google campaign's device, location, and language targetingGETList ad setsGETList Google Ads portfolio bid strategiesGETList campaign-level negative keywordsGETRemove a Search keywordDELETEReplace campaign-level negative keywordsPUTPause or enable a Search keywordPATCHUpdate a Google Ads portfolio bid strategyPATCHEdit a Google campaign's device, location, or language targetingPUT
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources
Campaigns and Ads

Update ad

Patch one or more fields on an ad. Status, budget, targeting, and creative changes are propagated to the platform.

Per-platform support:

  • Meta (Facebook + Instagram): all fields supported.
  • TikTok: status, budget, targeting (via /v2/adgroup/update/), and creative (via /v2/ad/update/ patch-style — headline is ignored, body becomes ad_text).
  • Google: status, budget, KEYWORD edits via targeting.keywords / targeting.negativeKeywords, and DEVICE bid adjustments via targeting.devices — each list you send becomes the FULL new set of its kind (criteria not in the list are removed); a kind left out is untouched. Any other targeting field returns 400: Google cannot mutate broad targeting post-create without recreating the campaign. creative returns 501.
  • LinkedIn: status, budget, targeting (geo countries only, applied to the LinkedIn Campaign via PARTIAL_UPDATE), and creative (uploads new media, creates a replacement inline creative on the same campaign, pauses the old one).
  • Pinterest / X / OpenAI Ads: status + budget only. Sending targeting or creative returns 501 with code unsupported_platform_operation. OpenAI Ads budget is lifetime-only (see budget.type below).

Google keyword replacement: These edits affect the ad's entire ad group, including sibling ads. Positive (targeting.keywords) and negative (targeting.negativeKeywords) sets are independent: omit a field to leave that set unchanged, or send [] to remove every keyword of that kind.

Zernio compares each supplied set with Google's live criteria by case-insensitive keyword text and match type. A matching criterion is left untouched, retaining its criterion ID, enabled/paused status, keyword-level bid overrides, labels, and criterion-associated history/statistics. Zernio does not reset its quality score; Google continues to calculate scores and statistics normally. Text comparison does not trim whitespace.

A bare string or an object without matchType means broad, not the existing criterion's match type. For example, resending an existing { "text": "plumber", "matchType": "exact" } preserves it; sending "plumber" instead removes that EXACT criterion and requests a BROAD one. Changing text or match type removes criteria no longer requested and creates any missing criteria. New criteria get new IDs and do not inherit removed criteria's bid overrides, labels, or history. Historical reporting for a removed criterion is not transferred to its replacement.

To add keywords without replacing a set, use POST /v1/ads/keywords. Use PATCH /v1/ads/keywords/{keywordId} to pause/enable one keyword, or DELETE /v1/ads/keywords/{keywordId} to remove it.


PlatformsMetaGoogleTikTokLinkedInPinterestX
PUT
/v1/ads/{adId}

Authorization

bearerAuth
AuthorizationBearer <token>

API key authentication - use your Zernio API key as a Bearer token

In: header

Path Parameters

adId*string

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

{  "ad": {    "_id": "string",    "name": "string",    "platform": "facebook",    "status": "active",    "configuredStatus": "string",    "reviewStatus": "in_review",    "adType": "boost",    "creativeType": "carousel",    "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,      "allConversions": 0,      "costPerConversion": 0,      "actions": {        "property1": 0,        "property2": 0      },      "actionValues": {        "property1": 0,        "property2": 0      },      "purchaseValue": 0,      "roas": 0,      "costPerAction": {        "property1": 0,        "property2": 0      },      "outboundClicks": 0,      "outboundClicksCtr": 0,      "inlineLinkClicks": 0,      "inlineLinkClickCtr": 0,      "uniqueClicks": 0,      "uniqueCtr": 0,      "videoPlayActions": 0,      "video30SecWatchedActions": 0,      "videoThruplayWatchedActions": 0,      "videoP25WatchedActions": 0,      "videoP50WatchedActions": 0,      "videoP75WatchedActions": 0,      "videoP95WatchedActions": 0,      "videoP100WatchedActions": 0,      "videoAvgTimeWatchedActions": 0,      "costPerThruplay": 0,      "funnel": {        "landingPageViews": 0,        "contentViews": 0,        "searches": 0,        "wishlistAdds": 0,        "cartAdds": 0,        "checkoutsInitiated": 0,        "paymentInfoAdds": 0,        "purchases": 0,        "leads": 0,        "registrationsCompleted": 0,        "appInstalls": 0,        "messagingConversationsStarted": 0,        "messagingFirstReplies": 0      },      "engagementBreakdown": {        "postEngagement": 0,        "pageEngagement": 0,        "reactions": 0,        "comments": 0,        "shares": 0,        "saves": 0,        "pageLikes": 0,        "videoViews": 0,        "linkClicks": 0      },      "lastSyncedAt": "2019-08-24T14:15:22Z"    },    "platformAdId": "string",    "platformAdAccountId": "string",    "platformCampaignId": "string",    "platformAdSetId": "string",    "campaignName": "string",    "adSetName": "string",    "platformObjective": "string",    "optimizationGoal": "string",    "costType": "string",    "servingStatuses": [      "string"    ],    "platformAdAccountName": "string",    "platformCreatedAt": "2019-08-24T14:15:22Z",    "bidStrategy": "LOWEST_COST_WITHOUT_CAP",    "bidAmount": 0,    "roasAverageFloor": 0,    "promotedObject": {      "custom_event_type": "string",      "pixel_id": "string",      "page_id": "string",      "application_id": "string",      "product_set_id": "string"    },    "creative": {      "thumbnailUrl": "string",      "imageUrl": "string",      "videoId": "string",      "videoUrl": "string",      "creativeId": "string",      "objectType": "string",      "objectStoryId": "string",      "effectiveObjectStoryId": "string",      "pageId": "string",      "effectiveInstagramMediaId": "string",      "instagramUserId": "string",      "instagramPermalinkUrl": "string",      "mediaUrls": [        "string"      ],      "isServing": true,      "servingHoldReasons": [        "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"}
Was this page helpful?

Boost post as ad

Creates a paid ad from an existing published post, keeping the post's engagement. By default it provisions the whole hierarchy (campaign, ad set, ad). **Attach shape (Meta).** Send `adSetId` to put the ad under an EXISTING ad set instead, so that ad set keeps its learning phase. It then owns `budget`, `schedule` and `targeting`, and sending any of those alongside `adSetId` is a 400 rather than a silent drop. `budget` is required only without `adSetId`. `instagramAccountId`, `destinationType` and `adSetId` are Meta-only and return 400 on other platforms. **Retries.** Boosts are NOT idempotent and can take minutes when Meta requires re-hosting an Instagram video, so do not retry on client timeout. Send an Idempotency-Key header to make retries safe: same key and body replays the original 201, and distinct keys always create distinct ads. Without the header, an identical request is treated as a retry: while one is in flight it returns 409, and within 10 minutes of a completed boost it returns the already-created ad instead of creating another. To intentionally duplicate an ad, send distinct Idempotency-Keys (or vary the body, e.g. the name).

Duplicate an ad

Duplicates a single ad via Meta's native `POST /{ad-id}/copies`. The copy is created paused. `adSetId` retargets the copy into another ad set; omitted = the source's own ad set. Accepts the Zernio ad id or the platform ad id. Sync discovery is triggered automatically (`syncAfter: false` to skip).

status?string

Value in

  • "active"
  • "paused"
budget?
targeting?

Meta + TikTok (demographics/interests), Google (keyword and device bid adjustment edits only), and LinkedIn (geo countries). Pinterest / X return 501.

creative?

Replace or patch the ad's creative. Meta, TikTok, and LinkedIn.

  • Meta: patch-style. Pass any subset — fields you omit are preserved from the live creative, including media (image_hash/video_id are reused, no re-upload) and url_tags. Sending the full set (headline, body, callToAction, linkUrl, imageUrl) rebuilds the creative from scratch instead. Partial patching reads the live object_story_spec, which Meta strips on SHARE / page-post / dark / asset_feed creatives — those return 422 asking for the full set. A videoUrl/videoId on an image creative is a type change and also needs the full set. existingCreativeId repoints the ad at a creative from GET /v1/ads/creatives and ignores every other field. Meta creatives are immutable, so any change creates a new creative and repoints the ad; the old creative is retained on the ad account for historical reporting.
  • TikTok: patch-style. Pass any subset; headline is ignored (TikTok creatives have no headline slot). body becomes the in-feed ad_text; linkUrl becomes landing_page_url; videoUrl triggers a fresh upload. description, videoId and existingCreativeId are Meta-only and return 400.
  • LinkedIn: requires new media (image via imageUrl or video via videoUrl); a text-only creative update returns 400. Uploads the media, creates a new inline media creative on the same campaign, and pauses the old creative (best-effort). The old creative is retained for historical reporting. videoId and existingCreativeId are Meta-only and return 400.
name?string

Rename the ad. Now propagated to Meta (POST /{ad-id}); non-Meta platforms return 501.

Lengthlength <= 255