Skip to content

TikTok Ads

Create campaigns and Spark Ads via Zernio API - No TikTok Business Center developer onboarding required

Included with the Usage plan. No TikTok Business Center developer onboarding needed. Connect via OAuth and start building.

What's Supported

FeatureStatus
Standalone campaigns (Campaign > Ad Group > Ad)Yes
Website conversion ads (TikTok Pixel optimization)Yes
Spark Ads (boost organic videos)Yes
Spark Ad custom destination URL + CTAYes
Spark Code (cross-creator boosts via auth_code)Yes
Bid strategy (Cost Cap, ROAS floor)Yes
Campaign duplication (manual graph copy)Yes
Attach-to-existing-adset on /v1/ads/createYes
Creative swap on PUT /v1/ads/{adId}Yes
Targeting updates after creationYes
Agency Business Centers (multi-advertiser + BC list endpoint)Yes
Custom Audiences + LookalikesYes
Age, gender, location, interest targetingYes
Video creative from URLYes
Real-time analytics (spend, views, CTR, CPM)Yes
Catalog / TikTok Shop adsRoadmap
Chunked video upload + async transcodeRoadmap

Create a Spark Ad (Boost)

typescript
const ad = await zernio.ads.boostPost({ body: {
  postId: "POST_ID",
  accountId: "ACCOUNT_ID",
  adAccountId: "7123456789012345678",
  name: "Boost viral video",
  goal: "traffic",
  budget: { amount: 50, type: "daily" },
  schedule: { startDate: "2026-04-20", endDate: "2026-04-27" },
  // TikTok-only Spark Ad creative overrides. Required for traffic /
  // conversion campaigns — without linkUrl the Spark Ad has no clickable
  // destination. CTA is the button label (e.g. SHOP_NOW, LEARN_MORE).
  linkUrl: "https://example.com/landing",
  callToAction: "SHOP_NOW",
  // Optional: Cost Cap bidding. bidStrategy is the cross-platform Meta enum;
  // we map it to TikTok's bid_type / bid_price automatically.
  bidStrategy: "COST_CAP",
  bidAmount: 0.50,
}});
python
ad = client.ads.boost_post(
    post_id="POST_ID",
    account_id="ACCOUNT_ID",
    ad_account_id="7123456789012345678",
    name="Boost viral video",
    goal="traffic",
    budget={"amount": 50, "type": "daily"},
    schedule={"startDate": "2026-04-20", "endDate": "2026-04-27"},
    link_url="https://example.com/landing",
    call_to_action="SHOP_NOW",
    bid_strategy="COST_CAP",
    bid_amount=0.50,
)
bash
curl -X POST "https://zernio.com/api/v1/ads/boost" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "postId": "POST_ID",
    "accountId": "ACCOUNT_ID",
    "adAccountId": "7123456789012345678",
    "name": "Boost viral video",
    "goal": "traffic",
    "budget": { "amount": 50, "type": "daily" },
    "schedule": { "startDate": "2026-04-20", "endDate": "2026-04-27" },
    "linkUrl": "https://example.com/landing",
    "callToAction": "SHOP_NOW",
    "bidStrategy": "COST_CAP",
    "bidAmount": 0.50
  }'

Spark Ads retain the creator's identity, organic engagement signals, and follower handle. linkUrl and callToAction map to landing_page_url and call_to_action on TikTok's /v2/ad/create/ creative — pass them when boosting for traffic/conversion goals so the ad has a clickable destination distinct from the original organic post.

Create a Standalone Campaign

typescript
const ad = await zernio.ads.createStandaloneAd({ body: {
  accountId: "acc_tiktokads_123",
  adAccountId: "7123456789012345678",
  name: "Spring launch",
  goal: "traffic",
  budgetAmount: 100,
  budgetType: "daily",
  body: "Spring drop is live",
  linkUrl: "https://example.com/spring",
  imageUrl: "https://cdn.example.com/launch.mp4",
  callToAction: "SHOP_NOW",
  countries: ["US"],
  ageMin: 18,
  ageMax: 34,
}});
python
ad = client.ads.create_standalone_ad(
    account_id="acc_tiktokads_123",
    ad_account_id="7123456789012345678",
    name="Spring launch",
    goal="traffic",
    budget_amount=100,
    budget_type="daily",
    body="Spring drop is live",
    link_url="https://example.com/spring",
    image_url="https://cdn.example.com/launch.mp4",
    call_to_action="SHOP_NOW",
    countries=["US"],
    age_min=18,
    age_max=34,
)
bash
curl -X POST "https://zernio.com/api/v1/ads/create" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "acc_tiktokads_123",
    "adAccountId": "7123456789012345678",
    "name": "Spring launch",
    "goal": "traffic",
    "budgetAmount": 100,
    "budgetType": "daily",
    "body": "Spring drop is live",
    "linkUrl": "https://example.com/spring",
    "imageUrl": "https://cdn.example.com/launch.mp4",
    "callToAction": "SHOP_NOW",
    "countries": ["US"],
    "ageMin": 18,
    "ageMax": 34
  }'

TikTok's ads endpoint is video-only. Pass the video URL as imageUrl, the field name is kept for cross-platform consistency. headline is ignored on TikTok (no headline slot in TikTok creatives); body is the video caption. callToAction is supported and maps to the in-feed CTA button. Valid goal values: engagement, traffic, awareness, video_views, lead_generation, conversions, app_promotion. For goal: "conversions" you must also pass promotedObject.pixelId (see Conversion campaigns below).

Conversion campaigns

A TikTok website-conversion campaign (goal: "conversions", which Zernio maps to a CONVERSIONS objective with a WEBSITE / CONVERT ad group) needs a TikTok Pixel. Without one, TikTok rejects the ad group with 40002: Please select a pixel. Pass the pixel via promotedObject (the same cross-platform field Meta uses):

promotedObject fieldMaps to TikTokRequired
pixelIdad group pixel_idYes
customEventTypead group optimization_event (the pixel event to optimise for)No (auto-bid CONVERT works without it)

customEventType takes a TikTok optimization_event code (TikTok's own UPPER_SNAKE codes, not Meta's PURCHASE/LEAD vocabulary and not PascalCase). The value must be one of the events your pixel is configured to track. Common website codes:

CodePixel event
ON_WEB_ORDERComplete Payment
INITIATE_ORDERPlace an Order
ON_WEB_CARTAdd to Cart
ON_WEB_REGISTERComplete Registration
ON_WEB_DETAILView Content
FORMSubmit Form
ON_WEB_SEARCHSearch
ON_WEB_ADD_TO_WISHLISTAdd to Wishlist
LANDING_PAGE_VIEWLanding Page View

Find your Pixel ID (and which events it tracks) in TikTok Ads Manager → Assets → Events.

typescript
const ad = await zernio.ads.createStandaloneAd({ body: {
  accountId: "acc_tiktokads_123",
  adAccountId: "7123456789012345678",
  name: "Spring launch - purchases",
  goal: "conversions",
  budgetAmount: 100,
  budgetType: "daily",
  body: "Spring drop is live",
  linkUrl: "https://example.com/spring",
  imageUrl: "https://cdn.example.com/launch.mp4",
  callToAction: "SHOP_NOW",
  countries: ["US"],
  ageMin: 18,
  ageMax: 34,
  // Required for goal: "conversions" on TikTok. customEventType is optional.
  promotedObject: { pixelId: "D9K4M2P7N5Q8R3T6V1W0", customEventType: "ON_WEB_ORDER" },
}});
python
ad = client.ads.create_standalone_ad(
    account_id="acc_tiktokads_123",
    ad_account_id="7123456789012345678",
    name="Spring launch - purchases",
    goal="conversions",
    budget_amount=100,
    budget_type="daily",
    body="Spring drop is live",
    link_url="https://example.com/spring",
    image_url="https://cdn.example.com/launch.mp4",
    call_to_action="SHOP_NOW",
    countries=["US"],
    age_min=18,
    age_max=34,
    promoted_object={"pixelId": "D9K4M2P7N5Q8R3T6V1W0", "customEventType": "ON_WEB_ORDER"},
)
bash
curl -X POST "https://zernio.com/api/v1/ads/create" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "acc_tiktokads_123",
    "adAccountId": "7123456789012345678",
    "name": "Spring launch - purchases",
    "goal": "conversions",
    "budgetAmount": 100,
    "budgetType": "daily",
    "body": "Spring drop is live",
    "linkUrl": "https://example.com/spring",
    "imageUrl": "https://cdn.example.com/launch.mp4",
    "callToAction": "SHOP_NOW",
    "countries": ["US"],
    "ageMin": 18,
    "ageMax": 34,
    "promotedObject": { "pixelId": "D9K4M2P7N5Q8R3T6V1W0", "customEventType": "ON_WEB_ORDER" }
  }'

Already set up a conversion ad group in TikTok Ads Manager (with the pixel + event configured)? Pass its ID as adSetId on POST /v1/ads/create instead. The new creative inherits the pixel and optimization_event from the parent ad group, so promotedObject isn't needed. See Attach to existing ad group.

Media Requirements

TypeFormatMax SizeNotes
VideoMP4, MOV, MPEG500 MB9:16 vertical, 720p+, 5-60 sec
ImageJPEG, PNG30 MB1080x1920 for full-screen

Agency Business Centers

Connecting a TikTok account that owns one or more Business Centers automatically enumerates every advertiser under those BCs. There's no per-call cap — GET /v1/ads/accounts walks /v2/bc/asset/get/ (paginated server-side) and returns the full roster. Solo advertisers without a BC fall back to the OAuth-time advertiser list (a single token can typically reach a handful of advertisers without BC).

The advertiser list is cached on your connection for 1 hour and lazy-refreshed on the next call after expiry.

If you previously hit 502 Bad Gateway with a "advertiser_ids: Length must be between 1 and 100" message on agency tokens, that's resolved — the lookup now chunks /v2/advertiser/info/ calls underneath.

Bid Strategy

Pass bidStrategy (cross-platform Meta enum) on POST /v1/ads/boost, POST /v1/ads/create, or PUT /v1/ads/ad-sets/{adSetId}. Zernio maps it to TikTok's bid_type / bid_price / deep_bid_type automatically:

bidStrategyMaps to TikTokRequired field
LOWEST_COST_WITHOUT_CAP (default)bid_type: BID_TYPE_NO_BID
LOWEST_COST_WITH_BID_CAPbid_type: BID_TYPE_CUSTOM + bid_pricebidAmount
COST_CAPbid_type: BID_TYPE_CUSTOM + bid_pricebidAmount
LOWEST_COST_WITH_MIN_ROASbid_type: BID_TYPE_NO_BID + deep_bid_type: MIN_ROASroasAverageFloor (account must be value-optimization-enabled)

bidAmount is in whole currency units of the ad account (USD: 5 = $5.00). On reads (GET /v1/ads/tree, GET /v1/ads/campaigns), TikTok's native bid_type is normalized back to the same Meta enum so cross-platform consumers see one shape regardless of source platform.

Spark Code (cross-creator Spark Ads)

To boost a creator's organic video from a different TikTok account than the one running the ads, the creator generates a Spark Code in their TikTok app's Promote settings and shares it with the advertiser. Pass it on POST /v1/ads/boost:

typescript
const ad = await zernio.ads.boostPost({
  body: {
    platformPostId: 'CREATORS_VIDEO_ID',
    accountId: 'ACCOUNT_ID',
    adAccountId: '7123456789012345678',
    name: 'Cross-creator boost',
    goal: 'traffic',
    budget: { amount: 50, type: 'daily' },
    linkUrl: 'https://example.com',
    callToAction: 'SHOP_NOW',
    sparkAuthCode: 'BCAQAAAA...',
  },
});
python
ad = client.ads.boost_post(
    platform_post_id="CREATORS_VIDEO_ID",
    account_id="ACCOUNT_ID",
    ad_account_id="7123456789012345678",
    name="Cross-creator boost",
    goal="traffic",
    budget={"amount": 50, "type": "daily"},
    link_url="https://example.com",
    call_to_action="SHOP_NOW",
    spark_auth_code="BCAQAAAA...",
)
bash
curl -X POST "https://zernio.com/api/v1/ads/boost" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platformPostId": "CREATORS_VIDEO_ID",
    "accountId": "ACCOUNT_ID",
    "adAccountId": "7123456789012345678",
    "name": "Cross-creator boost",
    "goal": "traffic",
    "budget": { "amount": 50, "type": "daily" },
    "linkUrl": "https://example.com",
    "callToAction": "SHOP_NOW",
    "sparkAuthCode": "BCAQAAAA..."
  }'

Without sparkAuthCode, boosts are limited to videos owned by the same TikTok account that's running the ads. Maps to auth_code on TikTok's AdcreateCreatives.

Attach to existing ad group

POST /v1/ads/create with adSetId set creates a new ad inside an existing TikTok ad group, skipping the campaign + ad-group create. Bid strategy, budget, targeting, and goal are inherited from the existing ad group. Use this when you've configured one ad group manually in TikTok Ads Manager and just want to add Zernio-managed creatives to it.

Creative swap

PUT /v1/ads/{adId} accepts a creative object to replace the live creative on an existing TikTok ad. Patch-style — only fields you supply are touched. headline is ignored (no slot on TikTok); body becomes ad_text; linkUrl becomes landing_page_url; videoUrl triggers a fresh upload.

Targeting update after creation

PUT /v1/ads/{adId} accepts targeting for TikTok ads — Zernio forwards it to /v2/adgroup/update/ with the same field set as create. (Pinterest / X / LinkedIn / Google return 501; those platforms force re-creation for targeting changes.)

Campaign duplication

POST /v1/ads/campaigns/{campaignId}/duplicate works on TikTok via a manual graph walk: Zernio reads the source campaign + ad groups + ads, then recreates each entity with their bid configuration, targeting, schedule, and creative fields preserved. Spark Ad linkage (tiktok_item_id) carries over. Everything is created paused so you can review before launching.