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:
- Safari ITP (Intelligent Tracking Prevention)
- Firefox Enhanced Tracking Protection
- Chrome Privacy Sandbox initiatives
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;
Hybrid Approach (Recommended)
Combine client-side and server-side tracking for maximum coverage and reliability.
Implementation Strategy
- Client-Side Primary: Use JavaScript pixel for immediate browser-based tracking
- Server-Side Backup: Send conversion to server-side API as fallback
- 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
- Load asynchronously to avoid blocking page rendering
- Implement error handling for pixel load failures
- Test across browsers including privacy-focused options
- Monitor pixel health via Amazon Ads console
- Have server-side backup for critical conversions
For Server-Side Implementations
- Capture attribution context on client (click IDs, user agent, IP)
- Store in session/database for later server-side API call
- Implement retry logic for failed API requests
- Log all API calls for debugging and reconciliation
- Monitor API error rates and latency
For Hybrid Implementations
- Use consistent transaction IDs across both methods
- Client-side fires first for immediate tracking
- Server-side as backup with small delay
- Amazon deduplicates automatically based on transaction ID
- 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