Server-Side vs Client-Side Tracking - Snapchat Ads | Blue Frog Docs

Server-Side vs Client-Side Tracking - Snapchat Ads

Compare Snap Pixel (client-side) and Conversions API (server-side) to choose the right tracking approach.

Tracking Methods Overview

Snapchat offers two primary methods for tracking conversions:

  1. Client-Side: Snap Pixel (JavaScript in browser)
  2. Server-Side: Conversions API (CAPI - Server to Snapchat)

Comparison Table

Feature Client-Side (Pixel) Server-Side (CAPI)
Implementation JavaScript snippet Server API calls
Data Source Browser Your server
Ad Blockers Can be blocked Immune
Privacy Restrictions Affected by browser policies Not affected
Setup Complexity Simple Moderate to complex
Data Quality Depends on browser Full control
Real-time Yes Yes
Attribution Browser cookies UUID matching
PII Handling Client-side hashing Server-side hashing
Reliability ~70-85% ~95-100%

Client-Side Tracking (Snap Pixel)

How It Works

User clicks ad → Lands on website → Pixel loads in browser
                                  ↓
                     Pixel sets cookie (_scid)
                                  ↓
                     User takes action (purchase)
                                  ↓
                     Pixel sends event to Snapchat
                                  ↓
                     Snapchat attributes conversion

Advantages

1. Easy Implementation

<!-- Simple JavaScript snippet -->
<script type='text/javascript'>
(function(e,t,n){if(e.snaptr)return;var a=e.snaptr=function()
{a.handleRequest?a.handleRequest.apply(a,arguments):a.queue.push(arguments)};
a.queue=[];var s='script';var r=t.createElement(s);r.async=!0;
r.src=n;var u=t.getElementsByTagName(s)[0];
u.parentNode.insertBefore(r,u);})(window,document,
'https://sc-static.net/scevent.min.js');

snaptr('init', 'YOUR_PIXEL_ID');
snaptr('track', 'PAGE_VIEW');
</script>
  • Snapchat handles cookie creation and management
  • Click ID automatically captured
  • Attribution window automatically applied

3. Real-Time User Behavior

// Capture micro-interactions
snaptr('track', 'VIEW_CONTENT', { /* ... */ });
snaptr('track', 'ADD_CART', { /* ... */ });
snaptr('track', 'START_CHECKOUT', { /* ... */ });

4. No Server Infrastructure Required

  • Works on static sites
  • No backend code needed
  • No API credentials to manage

Disadvantages

1. Ad Blocker Impact

// Ad blockers prevent pixel from loading
// Result: No tracking, lost conversions

Impact: ~15-30% of users may block pixels

2. Browser Restrictions

3. Limited Data Access

// Can only access browser-visible data
// Cannot access:
// - Server-side calculations
// - Database information
// - Offline conversions

4. Client-Side Vulnerabilities

  • JavaScript errors can break tracking
  • Page load delays affect tracking
  • User can disable JavaScript

Best Use Cases

  • Simple websites and blogs
  • Lead generation forms
  • Content sites
  • Fast implementation needed
  • No development resources available

Server-Side Tracking (Conversions API)

How It Works

User clicks ad → Lands on website → Pixel sets cookie
                                  ↓
                     User completes purchase
                                  ↓
                     Your server processes order
                                  ↓
                     Server sends event to Snapchat API
                                  ↓
                     Snapchat attributes conversion

Implementation

Node.js Example

const axios = require('axios');
const crypto = require('crypto');

// Hash function for PII
function hashSHA256(data) {
  return crypto.createHash('sha256')
    .update(data.toLowerCase().trim())
    .digest('hex');
}

// Send conversion event
async function sendConversionEvent(orderData) {
  const payload = {
    data: [{
      event_type: 'PURCHASE',
      event_conversion_type: 'WEB',
      event_tag: 'event_tag',
      timestamp: Date.now(),

      // User identification
      hashed_email: hashSHA256(orderData.customerEmail),
      hashed_phone_number: hashSHA256(orderData.customerPhone),
      hashed_ip_address: hashSHA256(orderData.ipAddress),
      user_agent: orderData.userAgent,
      uuid_c1: orderData.snapClickId,  // From _scid cookie

      // Event data
      price: orderData.total.toString(),
      currency: orderData.currency,
      transaction_id: orderData.orderId,
      item_ids: orderData.items.map(item => item.sku),
      number_items: orderData.items.length.toString(),

      // Deduplication
      client_dedup_id: `${orderData.orderId}_${Date.now()}`,

      // Additional context
      page_url: orderData.pageUrl,
      referrer_url: orderData.referrer
    }]
  };

  try {
    const response = await axios.post(
      'https://tr.snapchat.com/v2/conversion',
      payload,
      {
        headers: {
          'Authorization': `Bearer ${process.env.SNAP_ACCESS_TOKEN}`,
          'Content-Type': 'application/json'
        },
        timeout: 5000
      }
    );

    console.log('CAPI Success:', response.data);
    return response.data;

  } catch (error) {
    console.error('CAPI Error:', error.response?.data || error.message);
    throw error;
  }
}

// Usage in order processing
app.post('/api/orders', async (req, res) => {
  // Process order
  const order = await createOrder(req.body);

  // Send to Snapchat CAPI
  await sendConversionEvent({
    customerEmail: order.email,
    customerPhone: order.phone,
    ipAddress: req.ip,
    userAgent: req.headers['user-agent'],
    snapClickId: req.cookies._scid,
    total: order.total,
    currency: 'USD',
    orderId: order.id,
    items: order.items,
    pageUrl: `${req.protocol}://${req.get('host')}/confirmation`,
    referrer: req.headers.referer
  });

  res.json({ success: true, orderId: order.id });
});

Python Example

import requests
import hashlib
import time
import os

def hash_sha256(data):
    """Hash PII with SHA256"""
    if not data:
        return ''
    return hashlib.sha256(data.lower().strip().encode()).hexdigest()

def send_conversion_event(order_data):
    """Send conversion event to Snapchat CAPI"""

    url = 'https://tr.snapchat.com/v2/conversion'

    headers = {
        'Authorization': f"Bearer {os.environ['SNAP_ACCESS_TOKEN']}",
        'Content-Type': 'application/json'
    }

    payload = {
        'data': [{
            'event_type': 'PURCHASE',
            'event_conversion_type': 'WEB',
            'event_tag': 'event_tag',
            'timestamp': int(time.time() * 1000),

            # User identification
            'hashed_email': hash_sha256(order_data['customer_email']),
            'hashed_phone_number': hash_sha256(order_data['customer_phone']),
            'hashed_ip_address': hash_sha256(order_data['ip_address']),
            'user_agent': order_data['user_agent'],
            'uuid_c1': order_data.get('snap_click_id', ''),

            # Event data
            'price': str(order_data['total']),
            'currency': order_data['currency'],
            'transaction_id': order_data['order_id'],
            'item_ids': [item['sku'] for item in order_data['items']],
            'number_items': str(len(order_data['items'])),

            # Deduplication
            'client_dedup_id': f"{order_data['order_id']}_{int(time.time())}",

            # Additional context
            'page_url': order_data['page_url']
        }]
    }

    try:
        response = requests.post(url, json=payload, headers=headers, timeout=5)
        response.raise_for_status()

        print(f"CAPI Success: {response.json()}")
        return response.json()

    except requests.exceptions.RequestException as e:
        print(f"CAPI Error: {e}")
        raise

# Usage
def process_order(order_data):
    # ... process order logic ...

    # Send to Snapchat CAPI
    send_conversion_event({
        'customer_email': order_data['email'],
        'customer_phone': order_data['phone'],
        'ip_address': request.remote_addr,
        'user_agent': request.headers.get('User-Agent'),
        'snap_click_id': request.cookies.get('_scid'),
        'total': order_data['total'],
        'currency': 'USD',
        'order_id': order_data['id'],
        'items': order_data['items'],
        'page_url': f"{request.scheme}://{request.host}/confirmation"
    })

Advantages

1. Immune to Ad Blockers

// Server-side: Ad blockers have no effect
// Direct server-to-Snapchat communication

2. Complete Data Access

// Access to full order data, customer data, inventory, etc.
await sendConversionEvent({
  customerLifetimeValue: 5000,
  customerTier: 'premium',
  inventoryData: productStock,
  marginData: orderMargin
  // Any server-side data
});

3. Enhanced Privacy Control

// Hash PII server-side (more secure)
// Control exactly what data is sent
// No client-side data exposure

4. Higher Reliability

  • Not affected by JavaScript errors
  • Not affected by page load issues
  • Guaranteed execution in server environment

5. Offline Conversions

// Track phone orders, in-store purchases, etc.
await sendConversionEvent({
  event_type: 'PURCHASE',
  event_conversion_type: 'OFFLINE',
  // ... offline conversion data
});

Disadvantages

1. More Complex Setup

  • Requires server-side development
  • API authentication setup
  • Error handling and logging
  • Infrastructure for API calls

2. Attribution Challenges

// Must manually capture and pass Snap click ID
const snapClickId = req.cookies._scid;  // May be missing

if (!snapClickId) {
  console.warn('No Snap click ID - attribution may fail');
}

3. Missing Behavioral Data

// Cannot track:
// - Scroll depth
// - Time on page
// - Click patterns
// - Video watch percentage

4. Development Resources Required

  • Backend developers needed
  • Testing and QA
  • Ongoing maintenance
  • Monitoring and alerting

Best Use Cases

  • E-commerce with server-side processing
  • High-value conversions (enterprise SaaS)
  • Subscription businesses
  • Offline conversion tracking
  • Markets with high ad blocker usage
  • GDPR/privacy-focused implementations

Why Use Both?

Combining Pixel and CAPI provides:

  1. Maximum Coverage - Pixel catches what CAPI misses, and vice versa
  2. Redundancy - Backup if one method fails
  3. Better Attribution - More data points for Snapchat
  4. Behavioral + Conversion - Pixel for micro-conversions, CAPI for macro

Implementation Architecture

User Journey:
1. Click Snapchat ad → Pixel sets cookie (_scid)
2. Browse products → Pixel tracks VIEW_CONTENT, ADD_CART
3. Start checkout → Pixel tracks START_CHECKOUT
4. Complete purchase → BOTH Pixel and CAPI track PURCHASE
                     ↓
            Event Deduplication prevents double-counting

Hybrid Implementation

Client-Side (Pixel)

// In browser - track all events including purchase
const eventId = `ORDER_${orderId}_${Date.now()}`;

snaptr('track', 'PURCHASE', {
  'price': orderTotal,
  'currency': 'USD',
  'transaction_id': orderId,
  'client_dedup_id': eventId,  // Deduplication key
  'item_ids': itemSkus
});

// Also send eventId to server for CAPI
fetch('/api/track-purchase', {
  method: 'POST',
  body: JSON.stringify({
    orderId: orderId,
    eventId: eventId  // Same ID for deduplication
  })
});

Server-Side (CAPI)

// On server - track same purchase with same deduplication ID
app.post('/api/track-purchase', async (req, res) => {
  const { orderId, eventId } = req.body;
  const order = await getOrder(orderId);

  await sendConversionEvent({
    client_dedup_id: eventId,  // MUST match client-side
    event_type: 'PURCHASE',
    transaction_id: orderId,
    price: order.total.toString(),
    currency: 'USD',
    // ... other data
  });

  res.json({ success: true });
});

Event Deduplication

Critical: Use same client_dedup_id in both Pixel and CAPI to prevent double-counting.

// Generate consistent deduplication ID
function generateDedupId(orderId) {
  const timestamp = Date.now();
  return `ORDER_${orderId}_${timestamp}`;
}

// Client-side
const dedupId = generateDedupId(orderId);
document.cookie = `snap_dedup_id=${dedupId}; max-age=300`;  // 5 minutes

snaptr('track', 'PURCHASE', {
  'client_dedup_id': dedupId,
  // ... event data
});

// Server-side
const dedupId = req.cookies.snap_dedup_id;

await sendConversionEvent({
  client_dedup_id: dedupId,  // Same ID
  // ... event data
});

Decision Matrix

Choose Client-Side Only If:

  • Simple website (blog, portfolio, landing page)
  • No backend development resources
  • Quick implementation needed
  • Budget constraints
  • Low ad blocker usage in target audience

Choose Server-Side Only If:

  • High ad blocker usage (>30%)
  • Privacy-first market (EU)
  • Offline conversions needed
  • Enterprise security requirements
  • Server infrastructure already exists

Choose Hybrid If:

  • E-commerce business
  • High conversion value
  • Need maximum accuracy
  • Have development resources
  • Want behavioral tracking + conversion tracking
  • Operating in multiple markets

Performance Comparison

Data Quality Score

Typical event quality scores by implementation:

Method Event Quality Score Coverage
Pixel Only 6-7/10 70-85%
CAPI Only 8-9/10 95-100%
Hybrid 9-10/10 98-100%

Attribution Accuracy

Method Attribution Accuracy
Pixel Only ~75%
CAPI Only ~85%
Hybrid ~95%

Migration Guide

From Pixel to Hybrid

Phase 1: Keep existing Pixel (Week 1)

  • Document current pixel setup
  • Identify conversion events
  • Test pixel still works

Phase 2: Implement CAPI (Week 2-3)

  • Set up server infrastructure
  • Implement CAPI for PURCHASE event
  • Add deduplication logic
  • Test in development

Phase 3: Deploy and Monitor (Week 4)

  • Deploy CAPI to production
  • Monitor Events Manager for duplicates
  • Verify deduplication working
  • Compare conversion counts

Phase 4: Expand (Week 5+)

  • Add more events to CAPI
  • Implement offline conversions
  • Optimize based on Event Quality Score

Monitoring and Validation

Check Events Manager

## Hybrid Implementation Checklist

### Events Manager
- [ ] Events appear from both sources (Pixel & CAPI)
- [ ] Event details show "Web (Direct & Server)"
- [ ] No duplicate purchases (deduplication working)
- [ ] Event Quality Score improved

### Client-Side
- [ ] Pixel loads successfully
- [ ] All micro-conversions tracked (VIEW_CONTENT, ADD_CART)
- [ ] Deduplication ID generated

### Server-Side
- [ ] CAPI events send successfully
- [ ] API returns success (200 status)
- [ ] Deduplication ID matches client-side
- [ ] Error handling in place

Debug Both Methods

// Client-side debug
snaptr('debug', true);  // Enable console logging

// Server-side debug
console.log('CAPI Request:', {
  event_type: 'PURCHASE',
  client_dedup_id: dedupId,
  transaction_id: orderId
});

console.log('CAPI Response:', response.data);

Best Practices

  1. Always use deduplication when implementing hybrid
  2. Hash PII server-side when possible (more secure)
  3. Monitor event quality in Events Manager
  4. Test thoroughly before production deployment
  5. Implement error handling for CAPI calls
  6. Log failed events for retry or analysis
  7. Keep pixel as backup even with CAPI

Cost Considerations

Aspect Pixel CAPI Hybrid
Implementation Time 1-2 days 1-2 weeks 2-3 weeks
Developer Resources Junior Senior Senior
Infrastructure Costs $0 $10-50/mo $10-50/mo
Maintenance Low Medium Medium
Total Value Medium High Highest

Next Steps

  1. Assess your needs using the decision matrix
  2. Start with Pixel if unsure (can add CAPI later)
  3. Plan CAPI implementation if needed
  4. Implement deduplication from the start
  5. Monitor and optimize using Events Manager

Resources

// SYS.FOOTER