Zernio
Zernio
Overview

Social Posting

XInstagramFacebookLinkedInTikTokYouTubePinterestRedditBlueskyThreadsGoogle Business ProfileSnapchat

Messaging

WhatsAppRCSiMessageTelegramDiscordSlack

Telephony

Phone NumbersVoice & CallsSMS

Advertising

Meta AdsGoogle AdsLinkedIn AdsTikTok AdsPinterest AdsX AdsOpenAI AdsReddit Ads

Blogs & Commerce

ShopifyWordPress
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources

TikTok Ads

Run Spark Ads, standalone video campaigns and website conversion campaigns on a tiktokads account, with Custom Audiences and Insights.


Boost a TikTok video as a Spark Ad with POST /v1/ads/boost, or create a campaign, ad group and video ad in one POST /v1/ads/create call, on a tiktokads account. No TikTok Business Center developer onboarding is needed: connect with OAuth and call the ads endpoints. Zernio's adSetId is the TikTok ad group id (on Meta the same field is an ad set, on LinkedIn a campaign).

Quick reference

FeatureStatus
Standalone campaigns (Campaign > Ad Group > Ad)Yes
Website conversion ads (TikTok Pixel optimization)Yes
TikTok Pixel management, pixel events, Business Center sharing and Shopify / WordPress install (Pixels)Yes
Spark Ads (boost organic videos)Yes
Spark Ad custom destination URL and 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 an existing ad group on /v1/ads/createYes
Creative swap on PUT /v1/ads/{adId}Yes
Targeting updates after creationYes
Agency Business Centers (multi-advertiser plus a BC list endpoint)Yes
Custom Audiences (customer list)Yes
Age, gender, location and interest targetingYes
Video creative from URLYes
Insights (spend, views, CTR, CPM)Yes
Live reports and de-duplicated reach on GET /v1/ads/insightsYes
Catalog and TikTok Shop adsRoadmap
Chunked video upload and async transcodeRoadmap

Before you start

TikTok Ads requires a tiktokads account with its own token: the TikTok Business API is a separate OAuth from TikTok posting. It does not use per-request OAuth scopes; when the user authorizes the connection, permissions are granted at the app level (ad account management, reporting, creative management) rather than as individual scopes in the consent URL. You also need the advertiser id, from List ad accounts.

Connect

Call GET /v1/connect/tiktok/ads with profileId and, optionally, the accountId of a TikTok posting account (Connect ads). With accountId, the new tiktokads account links to that posting account, so Spark Ads and standalone ads with the real @username identity become available. Without it, ads-only mode applies: standalone ads use a synthetic Brand Identity (CUSTOMIZED_USER, configured with PATCH /v1/connect/tiktok-ads or inline through brandIdentity on create) and Spark Ads are unavailable, because TikTok requires a posting account for them.

curl "https://zernio.com/api/v1/connect/tiktok/ads?profileId=66a1f0c2a4b9d3e8f1a2b3c4&accountId=66b2e19d8c3f5a7e9d0b1c2d" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"

Response (200):

{
  "authUrl": "https://business-api.tiktok.com/portal/auth?app_id=...",
  "state": "..."
}

Send the user to authUrl, as in the connecting accounts guide. Once the flow completes, the same call returns alreadyConnected: true with the tiktokads accountId, which every sample below uses.

Create ads

Spark Ads (boost)

Call POST /v1/ads/boost with postId, accountId, adAccountId, goal and budget. Spark Ads keep the creator's identity, organic engagement signals and follower handle.

import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: boosted } = await zernio.adcampaigns.boostPost({
  body: {
    postId: '65f1c0a9e2b5af0012ab34cd',
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    adAccountId: '7123456789012345678',
    name: 'Boost viral video',
    goal: 'traffic',
    budget: { amount: 50, type: 'daily' },
    schedule: { startDate: '2027-01-04T09:00:00Z', endDate: '2027-01-11T23:59:00Z' },
    targeting: { countries: ['US'] },
    linkUrl: 'https://example.com/landing',
    callToAction: 'SHOP_NOW',
    bidStrategy: 'COST_CAP',
    bidAmount: 0.5
  }
});

console.log(boosted.ad._id);

Response (201):

{
  "ad": {
    "_id": "66f0a1b2c3d4e5f6a7b8c9d0",
    "name": "Boost viral video",
    "platform": "tiktok",
    "status": "pending_review",
    "adType": "boost",
    "goal": "traffic",
    "budget": { "amount": 50, "type": "daily" },
    "bidStrategy": "COST_CAP",
    "bidAmount": 0.5,
    "platformAdId": "1802345678901234567",
    "platformCampaignId": "1802345678901234001",
    "platformAdSetId": "1802345678901234002"
  },
  "message": "Ad created"
}

linkUrl and callToAction are the Spark Ad creative overrides, mapped to landing_page_url and call_to_action on TikTok's /v2/ad/create/. Pass them for traffic and conversion goals: without linkUrl the Spark Ad has no clickable destination distinct from the organic video. targeting.countries is required on TikTok boosts (the ad group needs location_ids). bidStrategy and bidAmount are optional Cost Cap bidding (Bid strategy). Every sample on this page reuses the zernio and client constructors from this one.

Standalone campaign

Call POST /v1/ads/create. TikTok's ads endpoint is video-only, so the video URL goes in imageUrl (the field keeps its name for cross-platform consistency), body is the video caption, and headline is ignored because TikTok creatives have no headline slot. Valid goal values are engagement, traffic, awareness, video_views, lead_generation, conversions and app_promotion.

const { data: created } = await zernio.adcampaigns.createStandaloneAd({
  body: {
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    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
  }
});

console.log(created.ad.platformAdSetId);

Response (201), same shape as the boost with "adType": "standalone":

{
  "ad": {
    "_id": "66f0a1b2c3d4e5f6a7b8c9d1",
    "name": "Spring launch",
    "platform": "tiktok",
    "status": "pending_review",
    "adType": "standalone",
    "goal": "traffic",
    "platformAdId": "1802345678901234568",
    "platformCampaignId": "1802345678901234003",
    "platformAdSetId": "1802345678901234004"
  },
  "message": "Ad created"
}

platformAdSetId is the new TikTok ad group.

Conversion campaigns

A 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 through promotedObject, the same cross-platform field Meta uses:

promotedObject fieldMaps to TikTokRequired
pixelIdad group pixel_id (numeric)Yes
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 or LEAD vocabulary and not PascalCase), or the exact event name shown in TikTok Events Manager. 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

pixelId must be the numeric TikTok Pixel id, not the alphanumeric Pixel Code shown in Events Manager (for example D00IKHRC77UE0J0RTNHG). TikTok's ad-group API rejects the code with 40002: pixel_id ... is not a valid integer string. The numeric id is the pixel_id that TikTok's GET /pixel/list/ returns. If Ads Manager only shows the alphanumeric code for your pixel, list your pixels: each tag's id is the numeric id and siteTagId is the code.

Find the pixel and the events it tracks in TikTok Ads Manager under Assets, then Events. The request is the standalone create plus promotedObject:

{
  "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
  "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": "7987654321098765432", "customEventType": "ON_WEB_ORDER" }
}

If a conversion ad group already exists in TikTok Ads Manager with the pixel and 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 is not needed (Attach to an existing ad group).

Spark Code for cross-creator boosts

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 as sparkAuthCode on POST /v1/ads/boost, with the creator's video as platformPostId:

{
  "platformPostId": "7310234567890123456",
  "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
  "adAccountId": "7123456789012345678",
  "name": "Cross-creator boost",
  "goal": "traffic",
  "budget": { "amount": 50, "type": "daily" },
  "targeting": { "countries": ["US"] },
  "linkUrl": "https://example.com",
  "callToAction": "SHOP_NOW",
  "sparkAuthCode": "BCAQAAAA..."
}

Without sparkAuthCode, boosts are limited to videos owned by the same TikTok account that runs the ads. The value maps to auth_code on TikTok's AdcreateCreatives.

Bid strategy

Pass bidStrategy (the 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 and deep_bid_type:

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

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

Media requirements

TypeFormatMax sizeNotes
VideoMP4, MOV, MPEG500 MB9:16 vertical, 720p or better, 5 to 60 seconds
ImageJPEG, PNG30 MB1080 x 1920 for full-screen

Videos are fetched from the URL you pass; the media uploads guide covers hosting them on Zernio.

Target

Custom Audiences

Create a customer-file Custom Audience with POST /v1/ads/audiences (type: "customer_list"), then upload members with POST /v1/ads/audiences/{audienceId}/users. adAccountId is the TikTok advertiser id. TikTok matches on email only (any phone is ignored); values are SHA-256 hashed server-side.

TikTok needs the member file at creation time, so the audience is created lazily on the first member upload: the create records it with status pending (no platform id yet), and the first users call provisions it on TikTok. A new audience takes up to 48 hours to finish processing before it is targetable.

const { data: audience } = await zernio.adaudiences.createAdAudience({
  body: {
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    adAccountId: '7123456789012345678',
    type: 'customer_list',
    name: 'Cart abandoners'
  }
});

const { data: upload } = await zernio.adaudiences.addUsersToAdAudience({
  path: { audienceId: audience.audience.id },
  body: { users: [{ email: 'jane@example.com' }, { email: 'sam@example.com' }] }
});

console.log(upload.numReceived);

Response (201):

{
  "audience": { "id": "66e5b2c3d4f5a6b7c8d9e0f1", "name": "Cart abandoners", "type": "customer_list", "platform": "tiktok", "status": "pending" },
  "message": "Audience created"
}

Response (200):

{ "message": "Users added", "numReceived": 2, "numInvalid": 0 }

Targeting fields

countries, ageMin, ageMax, gender, languages (ISO 639-1; zh_TW targets traditional Chinese), interests (from GET /v1/ads/interests), behaviors, regions, cities, zips, metros and incomeTier apply on create; audienceInclude and audienceExclude take the platformAudienceId of a processed Custom Audience.

behaviors are TikTok's video and creator interaction categories, from GET /v1/ads/targeting/search?dimension=behavior. Ids look like video:1913101 (people who watched to the end, liked, commented on or shared videos in that category in the last 15 days) or creator:24001 (people who followed or viewed the profile of creators in that category). TikTok only accepts them with the TikTok placement, so the ad group runs on TikTok alone when behaviors is set. To change targeting after creation, see Targeting update after creation.

Measure

Call GET /v1/ads/{adId}/analytics with the ad's _id for spend, impressions, clicks, CTR, CPM and video metrics, and breakdowns for a split by gender, age, country_code, platform, ac or language.

curl "https://zernio.com/api/v1/ads/66f0a1b2c3d4e5f6a7b8c9d0/analytics?fromDate=2027-01-04&toDate=2027-01-11&breakdowns=age" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"

Response (200), trimmed:

{
  "ad": { "id": "66f0a1b2c3d4e5f6a7b8c9d0", "name": "Boost viral video", "platform": "tiktok", "status": "active", "currency": "USD" },
  "analytics": {
    "summary": { "spend": 312.4, "impressions": 128400, "clicks": 2110, "ctr": 1.64, "cpc": 0.15, "cpm": 2.43, "videoPlayActions": 96200, "videoP100WatchedActions": 18400 },
    "daily": [ { "date": "2027-01-04", "spend": 44.1, "impressions": 18200, "clicks": 301 } ],
    "breakdowns": { "age": [ { "value": "AGE_18_24", "spend": 140.2, "impressions": 61000, "clicks": 1080 } ] }
  }
}

GET /v1/ads/tree rolls the same metrics up per campaign and ad group (Get campaign tree). For offline conversions (in-store, CRM, call-center), POST /v1/ads/conversions on a tiktokads account sends them to TikTok's Offline Events API: destinationId is the Offline Event Set id, each event must carry an email or phone, and the connection must have granted the Offline Events permission (older grants must reconnect). Web-pixel events are not sent this way (Send conversions).

Live reports and de-duplicated reach

GET /v1/ads/insights runs TikTok's synchronous report live, for questions the synced metrics cannot answer. The most common one is reach across a set: adding the reach of two campaigns counts a person who saw both twice, and the synced numbers only hold per-entity reach.

Send accountId (a tiktok or tiktokads connection), adAccountId (the advertiser id), dataLevel (AUCTION_ADVERTISER, AUCTION_CAMPAIGN, AUCTION_ADGROUP or AUCTION_AD), dimensions (1 to 4 TikTok dimensions, comma-separated), metrics (TikTok metric names, comma-separated) and fromDate/toDate (TikTok caps the span at 365 days). reportType is BASIC (the default) or AUDIENCE. filtering takes the same JSON array as Meta, [{"field", "operator", "value"}], with TikTok's field names (campaign_ids, adgroup_ids, ad_ids) and TikTok's filter type as operator, for example IN.

For de-duplicated reach, filter the set and group by country_code instead of the entity id. TikTok then counts each person once across the whole set and date range. Two campaigns over a month:

curl -G "https://zernio.com/api/v1/ads/insights" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  --data-urlencode "accountId=66b2e19d8c3f5a7e9d0b1c2d" \
  --data-urlencode "adAccountId=7123456789012345678" \
  --data-urlencode "dataLevel=AUCTION_CAMPAIGN" \
  --data-urlencode "dimensions=country_code" \
  --data-urlencode "metrics=reach,impressions,frequency" \
  --data-urlencode "fromDate=2027-01-01" \
  --data-urlencode "toDate=2027-01-31" \
  --data-urlencode 'filtering=[{"field":"campaign_ids","operator":"IN","value":["1876574798182050","1876575504573650"]}]'

Response (200), trimmed:

{
  "adAccountId": "7123456789012345678",
  "data": [
    { "dimensions": { "country_code": "US" }, "metrics": { "reach": "17770000", "impressions": "41210000", "frequency": "2.32" } }
  ],
  "paging": { "page": 1, "pageSize": 100, "totalRows": 1, "totalPages": 1 }
}

Rows come back verbatim from TikTok, with metric values as strings; page with page and pageSize (up to 1,000). For a set of ads, use dataLevel=AUCTION_AD with ad_ids, and for ad groups AUCTION_ADGROUP with adgroup_ids.

  • Reach is per country. When the set delivers in several countries, summing the rows counts a person reached in two countries twice.
  • The filter must match the data level or a coarser one. A finer filter, such as ad_ids at AUCTION_CAMPAIGN, answers 400: TikTok would silently widen it to every campaign containing those ads.
  • AUCTION_ADVERTISER takes no entity filter.
  • A 400 carries TikTok's own message when TikTok rejects the query (an unknown metric, a bad dimension combination), a 403 means the connection cannot read that advertiser, and a 429 is TikTok's rate limit.

Asynchronous report runs (POST /v1/ads/insights/reports) stay Meta only.

Pixels

Manage TikTok Pixels through the platform-neutral tracking tags API on your tiktokads connection: list, read, create, rename and configure a pixel, manage its events, share it through Business Center, read its event counts, and install it on a connected Shopify store or WordPress site.

FieldValue
tagIdThe numeric pixel_id: what promotedObject.pixelId takes on conversion campaigns
siteTagIdThe alphanumeric pixel_code (for example D00IKHRC77UE0J0RTNHG) that ttq.load() and Events Manager use
adAccountIdThe advertiser id. Optional on per-pixel calls: without it Zernio searches every advertiser on the connection, and a pixel shared into several advertisers resolves to its owner's copy
eventsOn GET .../tracking-tags/{tagId} and GET .../events: the pixel's events with TikTok's type (SHOPPING, ON_WEB_CART, ...), the matching siteEvent, and siteEventId, the name ttq.track() fires (Purchase, AddToCart, ...)
const { data } = await zernio.trackingtags.createTrackingTag({
  path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d' },
  body: { adAccountId: '7301234567890123456', name: 'Storefront pixel' }
});

Response (201):

{
  "platform": "tiktokads",
  "tag": {
    "id": "7412345678901234567",
    "siteTagId": "D00IKHRC77UE0J0RTNHG",
    "name": "Storefront pixel",
    "platform": "tiktokads",
    "kind": "pixel",
    "status": "active",
    "creationTime": 1804064400,
    "ownerAdAccountId": "7301234567890123456",
    "code": "<script>!function (w, d, t) {...}</script>",
    "events": []
  }
}

Pixel names are at most 40 characters with no emojis. The create is not idempotent and TikTok has no pixel delete, so never retry it blindly; list the pixels after a timeout.

Creating an event is not idempotent either, and TikTok returns no id for it and refreshes a pixel's event list only every few hours. When the new event is not listed yet, the response carries id: "" and status: "pending"; read the events again later for its id.

OperationBehavior
Update (PATCH .../tracking-tags/{tagId})name, enableAutomaticMatching, automaticMatchingFields (Meta's codes folded onto TikTok's email, phone, name, address and external id switches; gender and date of birth answer 400), and first-party cookies as enableFirstPartyCookies (boolean) or firstPartyCookieStatus. Other fields answer 400.
Stats (GET .../stats)Per-event counts from TikTok's /pixel/event/stats, 7 days by default and at most 30 per request, bucketed by whole UTC days. TikTok reports no last-fired time.
Share (.../shared-accounts)Business Center pixel links: list, link and unlink advertisers. The pixel must be an asset of a Business Center the connection manages (its owner Business Center, or one of the connection's); otherwise 400 asking you to transfer it into Business Center (Assets, Events). sharedAccounts[].businessId is the Business Center.
Conversion events (.../events)List, create and update the pixel's events. Create takes type (TikTok's event type) or siteEvent, plus optional name, defaultValue and currency (USD, JPY or INR). Update changes name, defaultValue and currency only: TikTok cannot change an event's type, so create a new event instead. DELETE answers 501 platform_not_supported: TikTok's delete call reports success without removing the event, so delete events in TikTok Events Manager.
Diagnostics501: TikTok has no pixel diagnostics endpoint (/pixel/diagnostic/, /pixel/diagnostics/ and /pixel/health/ answer 404). Use the stats or Events Manager.
Users and partners501.
DeleteNot offered: TikTok has no pixel delete endpoint (/pixel/delete/ answers 404, and it is not in TikTok's SDK specs).
InstallShopify and WordPress. WordPress gets TikTok's base code (ttq.load plus ttq.page); Shopify sends each storefront event to analytics.tiktok.com/api/v2/pixel, the endpoint TikTok's own pixel script calls (undocumented), with the ttclid click id kept first-party, the _ttp browser id reused and an event id for dedup with the Events API.

A connection authorized before TikTok's Pixel Management permission answers every pixel call with 403: reconnect TikTok Ads to grant it.

Operate

Attach to an existing ad group

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

Ad comments

GET /v1/ads/{adId}/comments returns the comments on a TikTok ad in the same normalized shape it returns for Meta, and TikTok also supports moderation: reply, hide or restore, and delete. Comments on organic posts are read and moderated through the inbox instead; this endpoint is the one that reaches comments on ad creatives, dark posts included.

TikTok reads comments over a date window rather than all of history, so the window is at most 30 days and defaults to the last 30 days. See Ad comments for the endpoints, the window rules and the canReply / canDelete / canHide flags.

Listing uses the stored ad group without resolving identity or looking up the video item ID. meta.tiktokItemId: null does not block it, and there is no ad-level identity or item-ID check to perform first. canReply and canDelete are per comment and false when the identity or item needed to moderate is unknown. Hiding does not require an identity. A direct reply or delete can resolve missing fields lazily; use the returned capabilities to guide the UI without blocking comment reads.

Creative swap

PUT /v1/ads/{adId} accepts a creative object that replaces the live creative on an existing TikTok ad, patch-style: only the fields you supply are touched. headline is ignored (no slot on TikTok), body becomes ad_text, linkUrl becomes landing_page_url, and videoUrl triggers a fresh upload.

curl -X PUT "https://zernio.com/api/v1/ads/66f0a1b2c3d4e5f6a7b8c9d0" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "creative": { "body": "Spring drop, last week", "videoUrl": "https://cdn.example.com/launch-v2.mp4" } }'

Response (200):

{
  "ad": { "_id": "66f0a1b2c3d4e5f6a7b8c9d0", "platform": "tiktok", "status": "pending_review", "creative": { "body": "Spring drop, last week" } },
  "message": "Ad updated"
}

Targeting update after creation

PUT /v1/ads/{adId} accepts targeting for TikTok ads with the same field set as create; Zernio forwards it to /v2/adgroup/update/. The other networks are narrower: Pinterest and X return 501, Google accepts keyword and device bid adjustments only, and LinkedIn accepts countries only.

Campaign duplication

POST /v1/ads/campaigns/{campaignId}/duplicate with platform: "tiktok" works through a manual graph walk: Zernio reads the source campaign, ad groups and ads, then recreates each entity with 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; the response carries copiedCampaignId.

Agency Business Centers

Connecting a TikTok account that owns one or more Business Centers enumerates every advertiser under those BCs. There is no per-call cap: GET /v1/ads/accounts walks /v2/bc/asset/get/ (paginated server-side), chunks the /v2/advertiser/info/ lookup underneath, 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 a BC). The advertiser list is cached on the connection for 1 hour and refreshed on the next call after expiry. GET /v1/ads/business-centers?accountId= lists the BCs themselves with an advertiserCount each, for agency-style pickers.

What you cannot do

TikTok Ads through Zernio does not support:

  • Catalog and TikTok Shop ads. goal has no catalog option on TikTok, and a catalog campaign built in Ads Manager syncs back as conversions.
  • TikTok Instant Forms. goal: "lead_generation" builds a Smart+ lead campaign that sends the click to your own form on linkUrl.
  • Spark Ads on an ads-only connection, because TikTok requires a posting account for them. Connect the TikTok account itself, not the ad account alone.
  • Chunked video upload and async transcode. A video creative transfers in one request, under the 500 MB ceiling in media requirements.
  • Creating an ad group on its own. POST /v1/ads/ad-sets is Google Ads only and answers 501 everywhere else.
  • The Meta-only reads and tools. Ad previews, the creative library, reach and frequency, the change log, ad labels, A/B studies, account finance, the image and video libraries and asynchronous insights reports all answer 501 on TikTok.

Common errors

ErrorCauseFix
422 with code ads_connection_requiredNo tiktokads account for the profile, or the TikTok user is not authorized as an Identity on the advertiserConnect TikTok Ads, or authorize the identity in TikTok Ads Manager.
40002: Please select a pixelgoal: "conversions" without promotedObject.pixelIdPass the numeric pixel id, or attach to an existing conversion ad group with adSetId.
40002: pixel_id ... is not a valid integer stringThe alphanumeric Pixel Code was sent instead of the numeric idUse the pixel's id from GET /v1/accounts/{accountId}/tracking-tags (Pixels).
400 on a boostcountries missing, or callToAction sent without linkUrlSend targeting.countries, and always pair the CTA with a destination.
Budget rejectedBelow TikTok's minimumTikTok's minimum is $20 per ad set.

A 422 on any ads call means the profile has no TikTok Ads connection:

{
  "error": "TikTok Ads is not connected for this profile",
  "type": "invalid_request_error",
  "code": "ads_connection_required",
  "param": "accountId"
}

Error handling covers the envelope.

Related

  • Connecting accounts: the OAuth flow behind authUrl.
  • TikTok: the posting account Spark Ads boost from.
  • Boost post and Create standalone ad: every field.
  • Meta Ads: the same endpoints on Meta, where adSetId is an ad set.
  • Pixels and tracking tags: the pixel API across platforms.
Was this page helpful?

Media & Limits

LinkedIn Ads media requirements per format, what the API cannot do, and the errors LinkedIn returns most often.

Pinterest Ads

Create Promoted Pin campaigns, promote Pins you already published, upload customer list audiences and read ad metrics on a pinterestads account.

On this page

Quick referenceBefore you startConnectCreate adsSpark Ads (boost)Standalone campaignConversion campaignsSpark Code for cross-creator boostsBid strategyMedia requirementsTargetCustom AudiencesTargeting fieldsMeasureLive reports and de-duplicated reachPixelsOperateAttach to an existing ad groupAd commentsCreative swapTargeting update after creationCampaign duplicationAgency Business CentersWhat you cannot doCommon errorsRelated