X Ads Server-Side vs Client-Side | OpsBlu Docs

X Ads Server-Side vs Client-Side

Comparison and implementation guidance for server-side and client-side X Pixel tracking.

Overview

X Ads supports both client-side Pixel tracking and server-side Conversions API for reliable conversion measurement.

Client-Side Tracking

Implementation

<script>
!function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments);
},s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='https://static.ads-twitter.com/uwt.js',
a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script');
twq('config','PIXEL_ID');

// Track purchase
twq('event', 'tw-PIXEL_ID-Purchase', {
  value: '99.99',
  currency: 'USD',
  transaction_id: 'ORDER_12345'
});
</script>

Advantages

  • Easy implementation
  • Automatic user tracking
  • Real-time attribution
  • No server infrastructure needed

Limitations

  • Ad blockers (20-40% blocked)
  • Third-party cookie restrictions
  • Privacy compliance required

Server-Side Tracking (Conversions API)

Implementation

import requests
import hashlib
import time

def hash_email(email):
    return hashlib.sha256(email.lower().strip().encode()).hexdigest()

def track_x_conversion_server(pixel_id, access_token, event_data):
    url = 'https://ads-api.twitter.com/12/measurement/conversions'

    headers = {
        'Authorization': f'Bearer {access_token}',
        'Content-Type': 'application/json'
    }

    payload = {
        'pixel_id': pixel_id,
        'event_type': 'Purchase',
        'event_time': int(time.time()),
        'identifiers': [{
            'hashed_email': hash_email(event_data['email'])
        }],
        'conversion_data': {
            'value': event_data['value'],
            'currency': event_data.get('currency', 'USD'),
            'transaction_id': event_data['order_id']
        }
    }

    response = requests.post(url, json=payload, headers=headers)

    if response.status_code == 200:
        print(f"✓ X conversion tracked: {event_data['order_id']}")
        return True
    else:
        print(f"✗ API error: {response.status_code}")
        return False

# Usage
track_x_conversion_server(
    pixel_id='PIXEL_ID',
    access_token='ACCESS_TOKEN',
    event_data={
        'email': 'user@example.com',
        'value': 99.99,
        'currency': 'USD',
        'order_id': 'ORDER_12345'
    }
)

Node.js Example

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

function hashEmail(email) {
    return crypto
        .createHash('sha256')
        .update(email.toLowerCase().trim())
        .digest('hex');
}

async function trackXConversionServer(pixelId, accessToken, eventData) {
    const url = 'https://ads-api.twitter.com/12/measurement/conversions';

    const payload = {
        pixel_id: pixelId,
        event_type: 'Purchase',
        event_time: Math.floor(Date.now() / 1000),
        identifiers: [{
            hashed_email: hashEmail(eventData.email)
        }],
        conversion_data: {
            value: eventData.value,
            currency: eventData.currency || 'USD',
            transaction_id: eventData.orderId
        }
    };

    try {
        const response = await axios.post(url, payload, {
            headers: {
                'Authorization': `Bearer ${accessToken}`,
                'Content-Type': 'application/json'
            }
        });

        console.log(`✓ X conversion tracked: ${eventData.orderId}`);
        return true;
    } catch (error) {
        console.error(`✗ Failed to track conversion:`, error.message);
        return false;
    }
}

Advantages

  • Bypasses ad blockers
  • Privacy-friendly
  • Reliable tracking
  • Data control
  • Fraud prevention
  • Offline conversions

Limitations

  • Implementation complexity
  • Requires API authentication
  • User email required
  • No automatic device data
// Client-side: Primary tracking
twq('event', 'tw-PIXEL_ID-Purchase', {
  value: '99.99',
  currency: 'USD',
  transaction_id: 'ORDER_12345'
});

// Also send to server
fetch('/api/x/conversion/backup', {
  method: 'POST',
  body: JSON.stringify({
    orderId: 'ORDER_12345',
    value: 99.99,
    email: 'user@example.com'
  })
});
# Server-side: Backup tracking
@app.route('/api/x/conversion/backup', methods=['POST'])
def backup_conversion():
    data = request.json

    # Store in database
    db.conversions.insert_one({
        'order_id': data['order_id'],
        'value': data['value'],
        'email': data['email'],
        'timestamp': time.time(),
        'api_fired': False
    })

    # Schedule server-side API call after 1 hour
    schedule_api_call.delay(data['order_id'], delay=3600)

    return {'status': 'queued'}

Use Case Recommendations

Scenario Client-Side Server-Side Hybrid
Website conversions
High ad blocker audience
Privacy-first
Offline conversions
Critical conversions

Best Practices

  • Start with client-side, add server-side for critical conversions
  • Hash emails server-side
  • Use unique transaction IDs
  • Implement retry logic
  • Monitor both methods
  • Test before production
  • Document setup