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
| Feature | Status |
|---|---|
| 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 + CTA | Yes |
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/create | Yes |
Creative swap on PUT /v1/ads/{adId} | Yes |
| Targeting updates after creation | Yes |
| Agency Business Centers (multi-advertiser + BC list endpoint) | Yes |
| Custom Audiences + Lookalikes | Yes |
| Age, gender, location, interest targeting | Yes |
| Video creative from URL | Yes |
| Real-time analytics (spend, views, CTR, CPM) | Yes |
| Catalog / TikTok Shop ads | Roadmap |
| Chunked video upload + async transcode | Roadmap |
Create a Spark Ad (Boost)
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,
}});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,
)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
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,
}});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,
)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 field | Maps to TikTok | Required |
|---|---|---|
pixelId | ad group pixel_id | Yes |
customEventType | ad 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:
| Code | Pixel event |
|---|---|
ON_WEB_ORDER | Complete Payment |
INITIATE_ORDER | Place an Order |
ON_WEB_CART | Add to Cart |
ON_WEB_REGISTER | Complete Registration |
ON_WEB_DETAIL | View Content |
FORM | Submit Form |
ON_WEB_SEARCH | Search |
ON_WEB_ADD_TO_WISHLIST | Add to Wishlist |
LANDING_PAGE_VIEW | Landing Page View |
Find your Pixel ID (and which events it tracks) in TikTok Ads Manager → Assets → Events.
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" },
}});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"},
)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
| Type | Format | Max Size | Notes |
|---|---|---|---|
| Video | MP4, MOV, MPEG | 500 MB | 9:16 vertical, 720p+, 5-60 sec |
| Image | JPEG, PNG | 30 MB | 1080x1920 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:
bidStrategy | Maps to TikTok | Required field |
|---|---|---|
LOWEST_COST_WITHOUT_CAP (default) | bid_type: BID_TYPE_NO_BID | — |
LOWEST_COST_WITH_BID_CAP | bid_type: BID_TYPE_CUSTOM + bid_price | bidAmount |
COST_CAP | bid_type: BID_TYPE_CUSTOM + bid_price | bidAmount |
LOWEST_COST_WITH_MIN_ROAS | bid_type: BID_TYPE_NO_BID + deep_bid_type: MIN_ROAS | roasAverageFloor (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:
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...',
},
});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...",
)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.