Amazon Advertising Server-Side vs Client-Side | OpsBlu Docs

Amazon Advertising Server-Side vs Client-Side

Comparison of server-side and client-side tracking approaches for Amazon Ads with implementation guidance.

Tracking Architecture Overview

Amazon Advertising supports both client-side (browser-based) and server-side (API-based) conversion tracking. Each approach has distinct advantages, limitations, and ideal use cases.

Client-Side Tracking

JavaScript pixel fires from user's browser directly to Amazon's servers.

Mechanism: Amazon JavaScript tag loads on page, executes in browser, sends data to amazon-adsystem.com

Data Collection: Browser automatically captures user agent, IP address, referrer, cookies

Use Cases: Standard e-commerce conversions, lead generation, content engagement

Server-Side Tracking

Backend server sends conversion events to Amazon via API.

Mechanism: Application server makes HTTP POST request to Amazon Conversions API endpoint

Data Collection: Server provides conversion data, user identifiers, and attribution context

Use Cases: Offline conversions, subscription events, high-value transactions, mobile app conversions

Client-Side Implementation

Advantages

Browser Context Collection

Client-side tracking automatically captures rich browser context:

  • User agent (browser, device, OS)
  • IP address for geographic attribution
  • Referrer URL and landing page
  • Amazon cookies for user matching
  • JavaScript-enabled behavioral signals

Simple Implementation

Minimal technical requirements:

<!-- Basic client-side pixel -->
<script>
!function(e,n,t,o,c,r){e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},
e[o].l=1*new Date,r=n.createElement(t),r.async=1,
r.src="https://s.amazon-adsystem.com/iu3/conversion/ADVERTISER_ID.js",
n.head.appendChild(r)}(window,document,"script","aw");

aw('conversion', {
  transactionId: 'ORDER-123',
  value: 49.99,
  currency: 'USD'
});
</script>

Real-Time Firing

Events fire immediately as user completes action, minimizing data loss from browser closures or navigation.

Tag Manager Compatibility

Easy integration with Google Tag Manager, Adobe Launch, Tealium for centralized tag management.

Disadvantages

Browser Dependency

Tracking fails if:

  • JavaScript disabled
  • Ad blockers active
  • Network errors prevent script loading
  • User navigates away before pixel fires
  • Browser privacy settings block third-party requests

Limited Offline Support

Cannot track conversions that occur outside browser context (phone orders, in-store purchases, subscription renewals).

Client-Side Data Limitations

Difficult to include sensitive or server-only data (customer lifetime value, internal customer segments, backend transaction details).

Privacy Restrictions

Increasingly affected by browser privacy features:

Implementation Example

Complete client-side implementation with error handling:

(function() {
  // Pixel loader with error handling
  function loadAmazonPixel() {
    try {
      !function(e,n,t,o,c,r){
        e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},
        e[o].l=1*new Date,r=n.createElement(t),r.async=1,
        r.src="https://s.amazon-adsystem.com/iu3/conversion/YOUR_ID.js",
        r.onerror=function(){console.error('Amazon pixel failed to load')},
        n.head.appendChild(r)
      }(window,document,"script","aw");
    } catch (e) {
      console.error('Amazon pixel initialization error:', e);

      // Fallback: send to server-side endpoint
      sendToServerSideBackup();
    }
  }

  // Fire conversion with retry logic
  function trackConversion(data) {
    var maxRetries = 3;
    var retryCount = 0;

    function attemptTracking() {
      try {
        if (typeof aw !== 'function') {
          throw new Error('Amazon pixel not loaded');
        }

        aw('conversion', data);
      } catch (e) {
        retryCount++;

        if (retryCount < maxRetries) {
          setTimeout(attemptTracking, 1000 * retryCount);
        } else {
          // Final fallback to server-side
          sendToServerSideBackup(data);
        }
      }
    }

    attemptTracking();
  }

  // Server-side backup for failed client-side tracking
  function sendToServerSideBackup(data) {
    fetch('/api/amazon-conversion-backup', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
  }

  loadAmazonPixel();

  // Expose tracking function
  window.trackAmazonConversion = trackConversion;
})();

Server-Side Implementation

Advantages

Reliability

Not affected by:

  • Ad blockers
  • JavaScript errors
  • Browser privacy settings
  • Network interruptions in user's browser
  • Page navigation before pixel fires

Complete Data Access

Can include server-only information:

{
  "transactionId": "ORDER-123",
  "value": 49.99,
  "currency": "USD",
  # Server-side enrichment
  "customerLifetimeValue": 1299.99,
  "customerSegment": "vip_gold",
  "profitMargin": 15.50,
  "inventorySource": "warehouse_east",
  "fulfillmentMethod": "next_day"
}

Offline Conversion Support

Track events that occur outside browser:

  • Phone orders
  • In-store purchases
  • Subscription renewals
  • Payment processing callbacks
  • CRM-triggered conversions

Enhanced Security

Sensitive data never exposed to client-side code or browser developer tools.

Disadvantages

Reduced Attribution Context

Server cannot automatically capture:

  • User agent details
  • Client IP address (sees server IP instead)
  • Browser cookies for user matching
  • Referrer and landing page URL

Mitigation: Pass these values from client to server, then include in API call.

Implementation Complexity

Requires:

  • Backend development resources
  • API credential management
  • Error handling and retry logic
  • Logging and monitoring infrastructure

Attribution Matching Challenges

Amazon relies on click IDs or user identifiers for attribution. Server-side tracking requires:

  • Capturing Amazon click ID (amzn_click_id) from URL on client
  • Storing in session or database
  • Retrieving and including in server-side API call

Implementation Example

Python Server-Side Implementation

import requests
import json
import logging
from datetime import datetime

class AmazonConversionAPI:
    def __init__(self, advertiser_id, access_token):
        self.advertiser_id = advertiser_id
        self.access_token = access_token
        self.endpoint = 'https://advertising-api.amazon.com/conversion'

    def track_conversion(self, conversion_data):
        """Send conversion to Amazon via server-side API"""

        payload = {
            'advertiserId': self.advertiser_id,
            'transactionId': conversion_data['order_id'],
            'value': float(conversion_data['total']),
            'currency': conversion_data['currency'],
            'timestamp': datetime.utcnow().isoformat() + 'Z',

            # Attribution context (captured from client)
            'clickId': conversion_data.get('amazon_click_id'),
            'userAgent': conversion_data.get('user_agent'),
            'ipAddress': conversion_data.get('ip_address'),

            # Server-side enrichment
            'products': conversion_data.get('products', []),
            'customData': {
                'customerSegment': conversion_data.get('segment'),
                'lifetimeValue': conversion_data.get('ltv')
            }
        }

        headers = {
            'Authorization': f'Bearer {self.access_token}',
            'Content-Type': 'application/json',
            'Amazon-Advertising-API-ClientId': self.advertiser_id
        }

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

            if response.status_code == 200:
                logging.info(f'Amazon conversion tracked: {conversion_data["order_id"]}')
                return {'success': True, 'response': response.json()}
            else:
                logging.error(f'Amazon API error: {response.status_code} - {response.text}')
                return {'success': False, 'error': response.text}

        except requests.exceptions.RequestException as e:
            logging.error(f'Amazon API request failed: {str(e)}')

            # Queue for retry
            self.queue_for_retry(payload)

            return {'success': False, 'error': str(e)}

    def queue_for_retry(self, payload):
        """Add failed conversion to retry queue"""
        # Implementation depends on your infrastructure
        # (Redis queue, database table, message queue, etc.)
        pass

# Usage
amazon_api = AmazonConversionAPI(
    advertiser_id='YOUR_ADVERTISER_ID',
    access_token='YOUR_ACCESS_TOKEN'
)

# Track conversion after order completion
conversion_result = amazon_api.track_conversion({
    'order_id': 'ORDER-123',
    'total': 49.99,
    'currency': 'USD',
    'amazon_click_id': request.session.get('amazon_click_id'),
    'user_agent': request.headers.get('User-Agent'),
    'ip_address': request.remote_addr,
    'segment': 'vip',
    'ltv': 1299.99
})

Node.js Server-Side Implementation

const axios = require('axios');
const logger = require('./logger');

class AmazonConversionAPI {
  constructor(advertiserId, accessToken) {
    this.advertiserId = advertiserId;
    this.accessToken = accessToken;
    this.endpoint = 'https://advertising-api.amazon.com/conversion';
  }

  async trackConversion(conversionData) {
    const payload = {
      advertiserId: this.advertiserId,
      transactionId: conversionData.orderId,
      value: parseFloat(conversionData.total),
      currency: conversionData.currency,
      timestamp: new Date().toISOString(),

      // Attribution context
      clickId: conversionData.amazonClickId,
      userAgent: conversionData.userAgent,
      ipAddress: conversionData.ipAddress,

      // Product data
      products: conversionData.products || []
    };

    try {
      const response = await axios.post(this.endpoint, payload, {
        headers: {
          'Authorization': `Bearer ${this.accessToken}`,
          'Content-Type': 'application/json',
          'Amazon-Advertising-API-ClientId': this.advertiserId
        },
        timeout: 10000
      });

      logger.info(`Amazon conversion tracked: ${conversionData.orderId}`);
      return { success: true, data: response.data };

    } catch (error) {
      logger.error(`Amazon API error: ${error.message}`);

      // Retry logic
      if (error.response && error.response.status >= 500) {
        await this.queueForRetry(payload);
      }

      return { success: false, error: error.message };
    }
  }

  async queueForRetry(payload) {
    // Add to retry queue (Redis, database, etc.)
  }
}

module.exports = AmazonConversionAPI;

Combine client-side and server-side tracking for maximum coverage and reliability.

Implementation Strategy

  1. Client-Side Primary: Use JavaScript pixel for immediate browser-based tracking
  2. Server-Side Backup: Send conversion to server-side API as fallback
  3. Deduplication: Use unique transaction ID to prevent double-counting

Hybrid Implementation

Client-Side Component

// Track conversion client-side
function trackConversionHybrid(data) {
  // Attempt client-side tracking
  try {
    aw('conversion', {
      transactionId: data.orderId,
      value: data.value,
      currency: data.currency
    });
  } catch (e) {
    console.error('Client-side tracking failed:', e);
  }

  // Always send to server-side backup
  fetch('/api/amazon-conversion', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      orderId: data.orderId,
      value: data.value,
      currency: data.currency,
      amazonClickId: getCookie('amazon_click_id'),
      userAgent: navigator.userAgent,
      // Server will use its IP, or can send client IP via header
    })
  });
}

Server-Side Component

@app.route('/api/amazon-conversion', methods=['POST'])
def track_amazon_conversion():
    """Server-side backup for Amazon conversions"""

    data = request.json

    # Small delay to allow client-side pixel to fire first
    time.sleep(2)

    # Send to Amazon API
    # Amazon's deduplication will handle if client-side already tracked
    amazon_api.track_conversion({
        'order_id': data['orderId'],
        'total': data['value'],
        'currency': data['currency'],
        'amazon_click_id': data.get('amazonClickId'),
        'user_agent': data.get('userAgent') or request.headers.get('User-Agent'),
        'ip_address': request.headers.get('X-Forwarded-For') or request.remote_addr
    })

    return jsonify({'status': 'queued'})

Deduplication Logic

Amazon automatically deduplicates based on:

  • transactionId (must be unique and identical across client/server calls)
  • timestamp (within 5-minute window)

Ensure both client-side and server-side implementations use the same transaction ID.

Decision Matrix

Factor Client-Side Server-Side Hybrid
Implementation Complexity Low High Medium
Browser Compatibility Required N/A Flexible
Ad Blocker Resistance Low High High
Attribution Accuracy High Medium* High
Offline Conversion Support No Yes Yes
Data Security Medium High High
Real-Time Tracking Yes Yes Yes
Development Resources Minimal Moderate Moderate
Recommended For Standard web Apps, subscriptions E-commerce

*Server-side attribution requires passing client context (click IDs, user agent, IP)

Best Practices

For Client-Side Implementations

  1. Load asynchronously to avoid blocking page rendering
  2. Implement error handling for pixel load failures
  3. Test across browsers including privacy-focused options
  4. Monitor pixel health via Amazon Ads console
  5. Have server-side backup for critical conversions

For Server-Side Implementations

  1. Capture attribution context on client (click IDs, user agent, IP)
  2. Store in session/database for later server-side API call
  3. Implement retry logic for failed API requests
  4. Log all API calls for debugging and reconciliation
  5. Monitor API error rates and latency

For Hybrid Implementations

  1. Use consistent transaction IDs across both methods
  2. Client-side fires first for immediate tracking
  3. Server-side as backup with small delay
  4. Amazon deduplicates automatically based on transaction ID
  5. Monitor both channels to ensure neither is consistently failing

Testing Recommendations

Client-Side Testing

  • Verify pixel fires in browser Network tab
  • Test with ad blockers enabled
  • Validate across desktop, mobile, tablet
  • Check browser console for errors
  • Use Amazon's debugging tools

Server-Side Testing

  • Test API calls in development environment
  • Verify payload format matches Amazon's schema
  • Check authentication and authorization
  • Monitor API response codes and latency
  • Validate deduplication with test transactions

End-to-End Testing

  • Create test conversion with both tracking methods
  • Verify single conversion appears in Amazon reporting
  • Confirm transaction ID matches across systems
  • Test failure scenarios (network errors, API timeouts)
  • Validate attribution data accuracy