Pixels
TikTok Pixel Setup for South African Stores.
TikTok has exploded in South Africa since 2023 and the Pixel is how you feed Spark Ads and Shop Ads the conversion signal they need. This guide installs the TikTok Pixel via the Shopify app or a custom install, fires Purchase in ZAR, adds the TikTok Events API for server-side reliability, and handles the POPIA consent flow that most TikTok docs skip because they assume US / EU.
Prerequisites
- A TikTok For Business account with a Pixel created in Events Manager
- TikTok Pixel ID (format: a 20 character alphanumeric)
- Shopify TikTok sales channel app (optional but easiest)
- POPIA consent banner on the storefront
- Access Token for Events API if doing server-side
Step 1. Create the Pixel in TikTok Events Manager
Go to TikTok Events Manager > Connect a data source > Web. Name it (e.g. "yourstore_za"). Choose "Developer mode" for a custom install or "Shopify" for the native channel. Copy the Pixel ID and Access Token (for Events API).
Step 2. Install via the TikTok Shopify app (fastest)
Install "TikTok" from the Shopify App Store > Settings > Data sharing > "Maximum" or "Enhanced". Paste the Pixel ID. Enable Events API with the Access Token. Shopify fires Pixel events automatically.
Step 3. Manual install via Shopify Custom Pixel
If you want consent control, install the base Pixel via a Shopify Custom Pixel. Do not paste the TikTok snippet into theme.liquid directly — Shopify checkout routes through a different domain and the Pixel will miss Purchase.
const PIXEL_ID = 'YOUR_TIKTOK_PIXEL_ID';
const loadTikTokPixel = () => {
if (window.ttq) return;
!function (w, d, t) {
w.TiktokAnalyticsObject=t;var ttq=w[t]=w[t]||[];ttq.methods=["page","track","identify","instances","debug","on","off","once","ready","alias","group","enableCookie","disableCookie"];
ttq.setAndDefer=function(t,e){t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}};
for(var i=0;i<ttq.methods.length;i++)ttq.setAndDefer(ttq,ttq.methods[i]);
ttq.instance=function(t){for(var e=ttq._i[t]||[],n=0;n<ttq.methods.length;n++)ttq.setAndDefer(e,ttq.methods[n]);return e};
ttq.load=function(e,n){var i="https://analytics.tiktok.com/i18n/pixel/events.js";ttq._i=ttq._i||{};ttq._i[e]=[];ttq._i[e]._u=i;ttq._t=ttq._t||{};ttq._t[e]=+new Date;ttq._o=ttq._o||{};ttq._o[e]=n||{};
var o=document.createElement("script");o.type="text/javascript";o.async=!0;o.src=i+"?sdkid="+e+"&lib="+t;
var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(o,a)};
ttq.load(PIXEL_ID);
ttq.page();
}(window, document, 'ttq');
};
analytics.subscribe('page_viewed', () => {
if (window.__marketingConsent) loadTikTokPixel();
}); Step 4. Fire TikTok standard events
Use the exact TikTok event names: ViewContent, AddToCart, InitiateCheckout, CompletePayment. Include value, currency "ZAR", and contents array. TikTok uses these for Smart Creative and Shop Ads signal.
analytics.subscribe('product_viewed', (event) => {
const p = event.data.productVariant;
window.ttq?.track('ViewContent', {
contents: [{ content_id: p.sku, content_name: p.title, quantity: 1, price: Number(p.price.amount) }],
value: Number(p.price.amount),
currency: 'ZAR'
});
});
analytics.subscribe('product_added_to_cart', (event) => {
const l = event.data.cartLine;
window.ttq?.track('AddToCart', {
contents: [{ content_id: l.merchandise.sku, content_name: l.merchandise.title, quantity: l.quantity, price: Number(l.merchandise.price.amount) }],
value: Number(l.cost.totalAmount.amount),
currency: 'ZAR'
});
});
analytics.subscribe('checkout_completed', (event) => {
const c = event.data.checkout;
window.ttq?.track('CompletePayment', {
contents: c.lineItems.map((li) => ({ content_id: li.variant.sku, content_name: li.title, quantity: li.quantity, price: Number(li.variant.price.amount) })),
value: Number(c.totalPrice.amount),
currency: c.totalPrice.currencyCode,
description: 'order ' + c.order.id
});
}); Step 5. Add the TikTok Events API server-side
iOS Safari blocks the TikTok Pixel on first load. The Events API from your server (Cloudflare Worker or Shopify webhook) fills the gap. Send a matching event_id so TikTok dedupes. Check current TikTok Business API docs for the latest endpoint version.
export interface Env { TIKTOK_ACCESS_TOKEN: string; TIKTOK_PIXEL_ID: string; }
async function sha256(v: string) {
const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(v.trim().toLowerCase()));
return [...new Uint8Array(buf)].map(b => b.toString(16).padStart(2, '0')).join('');
}
export default {
async fetch(req: Request, env: Env): Promise<Response> {
const ev = await req.json<any>();
const body = {
event_source: 'web',
event_source_id: env.TIKTOK_PIXEL_ID,
data: [{
event: ev.event_name, // 'CompletePayment'
event_time: Math.floor(Date.now() / 1000),
event_id: ev.event_id,
user: {
email: ev.email ? await sha256(ev.email) : undefined,
phone: ev.phone ? await sha256(ev.phone) : undefined,
ip: req.headers.get('cf-connecting-ip'),
user_agent: req.headers.get('user-agent'),
ttclid: ev.ttclid,
ttp: ev.ttp,
},
properties: {
contents: ev.contents,
currency: 'ZAR',
value: ev.value,
},
}],
};
const res = await fetch('https://business-api.tiktok.com/open_api/v1.3/event/track/', {
method: 'POST',
headers: { 'Access-Token': env.TIKTOK_ACCESS_TOKEN, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
return new Response(await res.text(), { status: res.status });
}
}; Step 6. Gate behind POPIA marketing consent
TikTok tracks aggressively. Hold loadTikTokPixel() behind marketing consent. Do not fire page() or track() until the shopper opts in.
Step 7. Verify with TikTok Pixel Helper
Install the TikTok Pixel Helper Chrome extension. Walk through the funnel with a R1 test order. Events Manager > Test Events should show CompletePayment with value 1, currency ZAR. If TikTok logs it as USD, the currency string is missing or wrong case.
Step 8. Mark CompletePayment as the optimisation event
In TikTok Ads Manager, when creating a campaign, pick "Website Conversions" > Event: CompletePayment. Smart Creative needs at least 50 CompletePayment events in 7 days to leave learning. SA merchants usually hit this in the first week of an engaged launch.
SA gotchas
- TikTok Pixel is blocked by iOS Safari ITP on first-party cookies after 7 days. Events API is effectively mandatory for iOS-heavy SA audiences.
- TikTok uses "CompletePayment" not "Purchase" — reusing the Meta event name silently fails to attribute.
- ZAR currency code must be uppercase. "zar" or "R" gets rejected and falls back to USD.
- The TikTok Shopify app overwrites your Custom Pixel install if both are active. Pick one source.
- SA teen demographics drive TikTok traffic — POPIA is stricter about under-18 data. Disclose age check in your flow.
Frequently asked questions
Is TikTok Pixel POPIA compliant?
Only with marketing consent. TikTok is a US controller with data processing in the US / Ireland. Disclose the cross-border transfer and sign the TikTok Business Terms.
Do I need the TikTok Events API or is the Pixel enough?
For iOS-heavy SA stores (typically 50 percent+ of traffic), Events API is required to keep Smart Bidding profitable. Pixel alone misses too many iPhone conversions.
Will TikTok Pixel slow down my Shopify store?
The base Pixel is ~20KB and loads async. Gate it behind consent and defer until after first contentful paint to avoid PageSpeed penalties.
TikTok sales channel app or custom install for a SA store?
Shopify app for non-technical teams. Custom install if you need consent ordering control or want to pair with a self-built Events API.