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

Quora Ads Server-Side vs Client-Side

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

Overview

Quora Ads supports both client-side (browser-based) Pixel tracking and server-side Conversions API. Understanding when to use each approach is critical for accurate attribution, privacy compliance, and measurement accuracy.

Client-Side Tracking

How It Works

Implementation

<!-- Client-Side Quora Pixel -->
<script>
!function(q,e,v,n,t,s){if(q.qp) return; n=q.qp=function(){n.qp?n.qp.apply(n,arguments):n.queue.push(arguments);}; n.queue=[];t=document.createElement(e);t.async=!0;t.src=v; s=document.getElementsByTagName(e)[0]; s.parentNode.insertBefore(t,s);}(window, 'script', 'https://a.quora.com/qevents.js');
qp('init', 'PIXEL_ID');
qp('track', 'ViewContent');

// Purchase event
qp('track', 'Purchase', {
  'value': '99.99',
  'currency': 'USD',
  'order_id': 'ORDER_12345'
});
</script>

Advantages

  • Easy implementation: Simple script tag
  • Automatic tracking: Browser handles user identification
  • Real-time data: Events appear immediately in dashboard
  • Device data captured: User agent, IP automatically included
  • No server infrastructure: Works standalone
  • Dynamic remarketing: Product-level retargeting

Limitations

  • Ad blockers: 20-40% of events may be blocked
  • Third-party cookie restrictions: Safari ITP, Firefox ETP limit tracking
  • Privacy regulations: GDPR/CCPA compliance required
  • Client-side delays: Page load affects tracking
  • Limited control: Cannot modify data post-firing
  • Fraud vulnerability: Easier to spoof client-side

Server-Side Tracking (Conversions API)

How It Works

  • Server makes API call to Quora Conversions endpoint
  • No browser involvement - server-to-server
  • User identification via hashed email or external ID
  • Full control over data sent
  • Privacy-preserving implementation

Implementation

Python Example

import requests
import hashlib
import time

def hash_email(email):
    """Hash email for privacy"""
    return hashlib.sha256(email.lower().strip().encode()).hexdigest()

def track_quora_conversion_server(pixel_id, access_token, event_data):
    """Track conversion via Quora Conversions API"""
    url = 'https://conversion-event.quora.com/conversion/event'

    headers = {
        'Content-Type': 'application/json'
    }

    payload = {
        'pixel_id': pixel_id,
        'access_token': access_token,
        'event_name': event_data['event_name'],
        'event_time': int(time.time()),
        'user_data': {
            'em': hash_email(event_data['email']),
            'client_ip_address': event_data.get('ip_address'),
            'client_user_agent': event_data.get('user_agent')
        },
        'custom_data': {
            'value': event_data.get('value'),
            'currency': event_data.get('currency', 'USD'),
            'order_id': event_data.get('order_id')
        }
    }

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

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

    except Exception as e:
        print(f"✗ Failed to track conversion: {e}")
        return False

# Usage
track_quora_conversion_server(
    pixel_id='PIXEL_ID',
    access_token='ACCESS_TOKEN',
    event_data={
        'event_name': 'Purchase',
        'email': 'user@example.com',
        'value': 99.99,
        'currency': 'USD',
        'order_id': 'ORDER_12345',
        'ip_address': '192.168.1.1',
        'user_agent': 'Mozilla/5.0...'
    }
)

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 trackQuoraConversionServer(pixelId, accessToken, eventData) {
    const url = 'https://conversion-event.quora.com/conversion/event';

    const payload = {
        pixel_id: pixelId,
        access_token: accessToken,
        event_name: eventData.eventName,
        event_time: Math.floor(Date.now() / 1000),
        user_data: {
            em: hashEmail(eventData.email),
            client_ip_address: eventData.ipAddress,
            client_user_agent: eventData.userAgent
        },
        custom_data: {
            value: eventData.value,
            currency: eventData.currency || 'USD',
            order_id: eventData.orderId
        }
    };

    try {
        const response = await axios.post(url, payload, {
            headers: { 'Content-Type': 'application/json' },
            timeout: 10000
        });

        if (response.status === 200) {
            console.log(`✓ Quora conversion tracked: ${eventData.orderId}`);
            return true;
        }
    } catch (error) {
        console.error(`✗ Failed to track conversion:`, error.message);
        return false;
    }
}

// Usage
trackQuoraConversionServer('PIXEL_ID', 'ACCESS_TOKEN', {
    eventName: 'Purchase',
    email: 'user@example.com',
    value: 99.99,
    currency: 'USD',
    orderId: 'ORDER_12345',
    ipAddress: '192.168.1.1',
    userAgent: 'Mozilla/5.0...'
});

PHP Example

<?php
function hash_email($email) {
    return hash('sha256', strtolower(trim($email)));
}

function track_quora_conversion_server($pixel_id, $access_token, $event_data) {
    $url = 'https://conversion-event.quora.com/conversion/event';

    $payload = [
        'pixel_id' => $pixel_id,
        'access_token' => $access_token,
        'event_name' => $event_data['event_name'],
        'event_time' => time(),
        'user_data' => [
            'em' => hash_email($event_data['email']),
            'client_ip_address' => $event_data['ip_address'] ?? null,
            'client_user_agent' => $event_data['user_agent'] ?? null
        ],
        'custom_data' => [
            'value' => $event_data['value'] ?? null,
            'currency' => $event_data['currency'] ?? 'USD',
            'order_id' => $event_data['order_id'] ?? null
        ]
    ];

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($http_code === 200) {
        error_log("✓ Quora conversion tracked: {$event_data['order_id']}");
        return true;
    } else {
        error_log("✗ API error: $http_code");
        return false;
    }
}

// Usage
track_quora_conversion_server('PIXEL_ID', 'ACCESS_TOKEN', [
    'event_name' => 'Purchase',
    'email' => 'user@example.com',
    'value' => 99.99,
    'currency' => 'USD',
    'order_id' => 'ORDER_12345',
    'ip_address' => $_SERVER['REMOTE_ADDR'],
    'user_agent' => $_SERVER['HTTP_USER_AGENT']
]);
?>

Advantages

  • Bypasses ad blockers: Server requests not blocked
  • Privacy-friendly: No third-party cookies in browser
  • Reliable tracking: Not affected by client-side issues
  • Data control: Full control over data sent
  • Fraud prevention: Harder to spoof server-side
  • Offline conversions: Track phone orders, in-store sales
  • Data enrichment: Add server-side data before sending

Limitations

  • Implementation complexity: Requires backend development
  • User identification: Must collect and hash email
  • No automatic device data: Must capture and send manually
  • API authentication: Requires access token management
  • Delayed reporting: May take longer to appear in dashboard
  • More infrastructure: Backend dependencies required

Combine both methods for maximum coverage and reliability:

Implementation Strategy

  1. Client-side: Primary tracking for real-time attribution
  2. Server-side: Backup for critical conversions
  3. Deduplication: Use order ID to prevent double-counting

Hybrid Implementation

// Client-side: Fire pixel immediately
function trackConversionClientSide(orderData) {
  qp('track', 'Purchase', {
    'value': orderData.value,
    'currency': 'USD',
    'order_id': orderData.orderId
  });

  // Also send to server for backup
  sendToServerForBackup(orderData);
}

function sendToServerForBackup(orderData) {
  fetch('/api/quora/conversion/backup', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      orderId: orderData.orderId,
      value: orderData.value,
      currency: 'USD',
      email: orderData.email,
      timestamp: Date.now()
    })
  });
}
# Server-side: Backup conversion tracking
from flask import Flask, request
import time

app = Flask(__name__)

@app.route('/api/quora/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'],
        'client_timestamp': data['timestamp'],
        'server_timestamp': time.time(),
        'api_fired': False
    })

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

    return {'status': 'queued'}

def schedule_api_call(order_id, delay):
    """Fire Conversions API after delay if client-side didn't succeed"""
    time.sleep(delay)

    conversion = db.conversions.find_one({'order_id': order_id})

    if not conversion.get('api_fired'):
        # Client-side didn't fire - use server-side backup
        track_quora_conversion_server(
            pixel_id='PIXEL_ID',
            access_token='ACCESS_TOKEN',
            event_data={
                'event_name': 'Purchase',
                'email': conversion['email'],
                'value': conversion['value'],
                'currency': 'USD',
                'order_id': order_id
            }
        )

        db.conversions.update_one(
            {'order_id': order_id},
            {'$set': {'api_fired': True, 'method': 'server_side_backup'}}
        )

User Identification

Client-Side: Automatic

// Quora Pixel handles automatically via cookies
qp('track', 'Purchase', {
  'value': '99.99',
  'currency': 'USD',
  'order_id': 'ORDER_12345'
});

Server-Side: Hashed Email Required

# Must provide hashed email for attribution
track_quora_conversion_server(
    pixel_id='PIXEL_ID',
    access_token='ACCESS_TOKEN',
    event_data={
        'event_name': 'Purchase',
        'email': 'user@example.com',  # Will be hashed
        'value': 99.99,
        'order_id': 'ORDER_12345'
    }
)

Use Case Recommendations

E-commerce

Recommended: Hybrid approach

  • Client-side: Real-time purchase tracking
  • Server-side: High-value orders, returns/refunds

Lead Generation

Recommended: Client-side primary

  • Client-side: Form submissions
  • Server-side: Qualified lead updates

Subscription Services

Recommended: Server-side

  • Server-side: Subscription events, renewals
  • Scheduled: Recurring revenue tracking

Mobile Apps

Recommended: Server-side via MMP

  • Use mobile measurement partner (AppsFlyer, Adjust)
  • Server-side Conversions API for purchases

Offline Sales

Recommended: Server-side only

  • Server-side: Upload offline conversions
  • CRM integration: Phone orders, in-store sales

Decision Matrix

Scenario Client-Side Server-Side Hybrid
Basic website tracking
High ad blocker audience
Privacy-first approach
Offline conversions
Real-time attribution
Critical conversions
Email available
Dynamic retargeting

Performance Comparison

Metric Client-Side Server-Side Hybrid
Implementation Time 1-2 days 3-5 days 4-7 days
Tracking Accuracy (with blockers) 60-80% 95-99% 95-99%
Real-time Attribution Yes Slight delay Yes
Privacy Compliance Moderate High High
Maintenance Low Medium Medium
Cost Low Medium Medium

Best Practices

  1. Start with client-side, add server-side for critical conversions
  2. Always hash emails server-side, never send plaintext
  3. Use order IDs to prevent duplicate conversion counting
  4. Implement retry logic for failed server-side API calls
  5. Monitor both methods to identify tracking gaps
  6. Test in staging before production deployment
  7. Document setup for team reference
  8. Set up alerts for API failures
  9. Respect user privacy - only collect necessary data
  10. Regular audits of tracking accuracy

Troubleshooting

Client-Side Issues

  • Pixel not loading: Check browser console for errors
  • Events not firing: Verify Pixel Helper shows events
  • Ad blockers: Test in incognito mode

Server-Side Issues

  • API authentication fails: Verify access token is valid
  • Events not appearing: Check event_time is in correct format (Unix timestamp)
  • User matching fails: Ensure email is properly hashed (SHA-256, lowercase, trimmed)

Hybrid Issues

  • Duplicate conversions: Verify order ID deduplication logic
  • Inconsistent data: Validate data format across both methods