Server-Side GTM + TikTok Ads Setup Guide
Most TikTok Ads tracking setups I audit are leaking 20-30% of conversions, and it’s always the same culprit: relying entirely on TikTok’s pixel firing from the browser. Ad blockers, iOS tracking restrictions, and slow page loads kill your conversion data before it ever reaches TikTok’s servers. Server-side tracking fixes this by sending conversion data directly from your server to TikTok’s API, bypassing all the browser-based tracking limitations.
I’ve set up server-side TikTok tracking for over 60 accounts in the last two years, and the data quality improvement is consistently dramatic — usually 15-25% more conversions showing up in TikTok Ads Manager within the first week.
What You’ll Have Working By The End
- TikTok pixel firing from both browser and server for maximum data capture
- Conversion data flowing directly from your server to TikTok’s Events API
- Enhanced match quality with server-side user identifiers (email, phone)
- Real-time conversion tracking that bypasses ad blockers and iOS restrictions
- Proper event deduplication to prevent double-counting
Prerequisites
- Server-side GTM container running (Google Cloud Run, AWS, or your own server)
- TikTok Ads account with admin access
- TikTok pixel ID and access token from TikTok Events Manager
- Client-side GTM container already sending data to your server container
- Basic understanding of GTM server container architecture
Step 1: Get Your TikTok API Credentials
You need two pieces from TikTok Events Manager before you can send server-side events.
In TikTok Ads Manager, go to Assets → Events → Manage. Find your pixel and click “Set up Events API”:
- Generate an Access Token (this authenticates your server requests)
- Copy your Pixel ID (this tells TikTok which pixel to attribute events to)
- Note your Test Event Code (you’ll need this for testing)
The access token expires every 365 days, so set a calendar reminder to regenerate it. I’ve seen accounts lose 3-4 weeks of data because someone forgot to update an expired token.
Store these credentials securely. I typically add them as environment variables in Cloud Run or as GTM server container variables.
Step 2: Configure the TikTok Server-Side Tag
In your server-side GTM container, create a new TikTok tag:
Tag Type: TikTok (if available) or HTTP Client for custom implementation
Basic Configuration:
- Pixel ID: Your TikTok pixel ID
- Access Token: Your access token (use a GTM variable, don’t hardcode it)
- Test Event Code: Your test code (only during setup/testing)
Event Data Mapping:
{
"event_name": "{{Event Name}}", // CompletePayment, ViewContent, etc.
"event_time": "{{Event Timestamp}}", // Unix timestamp
"event_id": "{{Event ID}}", // For deduplication
"event_source_url": "{{Page URL}}",
"user_data": {
"email": "{{User Email Hashed}}", // SHA256 hash
"phone": "{{User Phone Hashed}}", // SHA256 hash, E.164 format
"external_id": "{{User ID}}"
},
"custom_data": {
"value": "{{Purchase Value}}",
"currency": "{{Currency}}",
"content_id": "{{Product ID}}",
"content_type": "product"
}
}
Critical: TikTok requires email and phone to be SHA256 hashed before sending. If you’re sending unhashed PII, TikTok will reject the events. Use GTM’s built-in SHA256 variables or hash them client-side before sending to your server.
Step 3: Set Up Event Deduplication
This is where 40% of setups break. You’re now sending the same conversion from both browser (TikTok pixel) and server (Events API). Without proper deduplication, TikTok counts each conversion twice.
Generate Consistent Event IDs:
Client-side (send with your dataLayer event):
// Generate unique ID for each conversion
const eventId = 'txn_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
// Send to both client GTM and your server
dataLayer.push({
event: 'purchase',
event_id: eventId,
transaction_id: 'order_12345',
value: 99.99,
currency: 'USD'
});
In both your client-side pixel and server-side tag, use the same event_id:
Client-side TikTok pixel configuration:
ttq.track('CompletePayment', {
content_id: 'product_123',
content_type: 'product',
value: 99.99,
currency: 'USD'
}, {
event_id: '{{Event ID}}'
});
Server-side tag configuration includes the same event_id field in your JSON payload.
TikTok automatically deduplicates events with the same event_id within a 48-hour window, keeping the first event received (usually the browser event for speed, server event for accuracy).
Step 4: Configure Transport URL (If Using Custom Implementation)
If you’re not using the built-in TikTok tag in server GTM, you’ll send events via HTTP Client tag:
Transport URL: https://business-api.tiktok.com/open_api/v1.3/pixel/track/
Headers:
Content-Type: application/json
Access-Token: {{TikTok Access Token}}
Request Body:
{
"pixel_code": "{{Pixel ID}}",
"event": "{{Event Name}}",
"event_id": "{{Event ID}}",
"timestamp": "{{Event Timestamp}}",
"context": {
"user_agent": "{{User Agent}}",
"ip": "{{User IP}}",
"page": {
"url": "{{Page URL}}",
"referrer": "{{Referrer}}"
}
},
"properties": {
"email": "{{Hashed Email}}",
"phone": "{{Hashed Phone}}",
"external_id": "{{User ID}}",
"value": "{{Purchase Value}}",
"currency": "{{Currency}}",
"content_id": "{{Product ID}}"
}
}
Event Name Mapping:
- Purchase →
CompletePayment - Add to Cart →
AddToCart - Begin Checkout →
InitiateCheckout - Page View →
ViewContent - Lead →
SubmitForm
Step 5: Enhanced Data Quality Setup
Server-side tracking lets you send much richer user data than browser-based pixels. This improves TikTok’s ability to match conversions back to users and optimize your campaigns.
Required Fields:
event_name(must match TikTok’s standard event names)event_time(Unix timestamp, not more than 7 days old)- At least one user identifier (email, phone, or external_id)
Recommended Fields for Better Match Quality:
"user_data": {
"email": "hash_of_user_email", // SHA256
"phone": "hash_of_phone_e164", // SHA256, E.164 format (+1234567890)
"external_id": "your_user_id",
"ip_address": "user_ip", // Don't hash this one
"user_agent": "full_user_agent_string"
}
Purchase Event Custom Data:
"custom_data": {
"value": 99.99, // Required for purchase events
"currency": "USD", // Required for purchase events
"content_id": ["product_123", "product_456"], // Product IDs
"content_type": "product",
"content_name": "Product Name",
"content_category": "Electronics",
"quantity": 2
}
The more accurate user identifiers you send, the better TikTok can optimize your campaigns. I typically see a 10-15% improvement in ROAS when accounts start sending hashed email and phone numbers server-side.
Testing & Verification
1. Server Container Debug Mode: Enable debug mode in your server-side GTM container and trigger a test conversion. Check that your TikTok tag fires and the request payload looks correct.
2. TikTok Events Manager Testing: In TikTok Events Manager, use your Test Event Code to send test events:
curl -X POST "https://business-api.tiktok.com/open_api/v1.3/pixel/track/" \
-H "Access-Token: YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pixel_code": "YOUR_PIXEL_ID",
"test_event_code": "YOUR_TEST_EVENT_CODE",
"event": "CompletePayment",
"timestamp": "'$(date +%s)'",
"context": {"ip": "127.0.0.1"},
"properties": {"value": 99.99, "currency": "USD"}
}'
Events should appear in the Test Events tab within 5 minutes.
3. Live Event Verification: After removing the test event code, trigger real conversions and check:
- Events appear in TikTok Events Manager within 15-30 minutes
- Event count matches your internal conversion count (within 5-10%)
- No duplicate events (check that event_id deduplication is working)
4. Campaign Performance Check: Within 24-48 hours, check that your TikTok campaigns are receiving conversion data. The “Events Received” column in campaign reporting should show your server-side events.
Acceptable Variance: 5-15% difference between your internal conversion count and TikTok’s received events is normal due to user consent, bot filtering, and attribution windows.
Troubleshooting
Problem: Events showing in test mode but not in live mode
Solution: Remove the test_event_code from your production requests. TikTok ignores events sent with test codes in live campaigns.
Problem: Getting 401 “Invalid Access Token” errors Solution: Your access token expired or is incorrect. Generate a new one in TikTok Events Manager. Access tokens expire every 365 days.
Problem: Events received but showing “Poor Match Quality” Solution: Your email/phone hashing is broken. Verify you’re SHA256 hashing emails in lowercase and phone numbers in E.164 format (+1234567890) before hashing.
Problem: Seeing 2x conversion counts in TikTok campaigns
Solution: Event deduplication isn’t working. Check that your client-side pixel and server-side tag are sending identical event_id values for the same conversion.
Problem: Server requests timing out or failing Solution: TikTok’s API can be slow (2-5 second response times). Increase your HTTP Client tag timeout to 10 seconds and consider implementing retry logic for failed requests.
Problem: Purchase events missing value/currency data
Solution: TikTok requires value and currency for CompletePayment events. These must be numbers (not strings) and currency must be a valid 3-letter ISO code.
What To Do Next
Server-side TikTok tracking is just one piece of a complete tracking infrastructure. For maximum data quality, you should also set up:
- Server-Side GTM for Facebook Ads to capture cross-platform attribution
- Server-Side GTM for Google Ads for complete Google ecosystem tracking
- Form-specific tracking setups for your lead generation funnels
Ready to audit your current TikTok tracking setup? Get a free tracking audit — I’ll identify exactly what conversion data you’re missing and how much it’s costing your campaigns.
This guide is part of the Server-Side GTM Hub — complete guides for implementing server-side tracking across all major ad platforms.