Event tracking is essential for Criteo's retargeting and optimization algorithms. This guide covers all Criteo OneTag events with implementation examples and best practices.
Event Types Overview
Criteo uses different events to track user behavior throughout the customer journey:
| Event | Purpose | Required | Page Type |
|---|---|---|---|
viewHome |
Track homepage visits | Yes | Homepage |
viewItem |
Track product views | Yes | Product pages |
viewList |
Track category browsing | Recommended | Category/listing pages |
viewBasket |
Track cart contents | Recommended | Cart page |
trackTransaction |
Track purchases | Yes | Order confirmation |
viewSearch |
Track search queries | Optional | Search results |
Core Account Setup Events
These events must be included on every page before any other events.
setAccount
Required first event that identifies your Criteo account:
window.criteo_q.push({
event: "setAccount",
account: 12345 // Replace with your account ID
});
Important:
- Must be the first event in the queue
- Account ID must be a number, not a string
- Same account ID across all pages
setSiteType
Identifies the device type for proper ad targeting:
var deviceType = /iPad/.test(navigator.userAgent) ? "t" :
/Mobile|iP(hone|od)|Android|BlackBerry|IEMobile|Silk/.test(navigator.userAgent) ? "m" : "d";
window.criteo_q.push({
event: "setSiteType",
type: deviceType
});
Device Type Values:
d= Desktopm= Mobilet= Tablet
Enhanced Device Detection
For better accuracy:
function getCriteoDeviceType() {
const ua = navigator.userAgent.toLowerCase();
const width = window.innerWidth || document.documentElement.clientWidth;
// Tablet detection
if (/(ipad|tablet|(android(?!.*mobile)))/.test(ua) ||
(width >= 768 && width <= 1024)) {
return 't';
}
// Mobile detection
if (/(mobi|iphone|ipod|android.*mobile|blackberry|opera mini)/.test(ua) ||
width < 768) {
return 'm';
}
// Desktop
return 'd';
}
window.criteo_q.push({
event: "setSiteType",
type: getCriteoDeviceType()
});
User Identification Events
setEmail
Capture user email for enhanced retargeting:
// Plain email (Criteo will hash it)
window.criteo_q.push({
event: "setEmail",
email: "user@example.com",
hash_method: "sha256" // Optional: specify hash method
});
setHashedEmail
Send pre-hashed emails for privacy:
// Hash email client-side
async function hashEmail(email) {
const normalized = email.toLowerCase().trim();
const msgBuffer = new TextEncoder().encode(normalized);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
// Send hashed email
const userEmail = 'User@Example.com';
const hashedEmail = await hashEmail(userEmail);
window.criteo_q.push({
event: "setHashedEmail",
email: hashedEmail
});
Best Practices:
- Always normalize emails (lowercase, trim whitespace)
- Hash on server-side when possible
- Only send for logged-in users
- Respect user privacy preferences
setCustomerId
Link to your internal customer ID:
window.criteo_q.push({
event: "setCustomerId",
id: "CUST_12345"
});
Homepage Event
viewHome
Track visits to your homepage:
window.criteo_q = window.criteo_q || [];
window.criteo_q.push(
{ event: "setAccount", account: 12345 },
{ event: "setSiteType", type: deviceType },
{ event: "viewHome" }
);
Complete Homepage Example:
<!DOCTYPE html>
<html>
<head>
<title>Your Store - Homepage</title>
</head>
<body>
<!-- Homepage content -->
<script type="text/javascript" src="//dynamic.criteo.com/js/ld/ld.js" async="true"></script>
<script type="text/javascript">
window.criteo_q = window.criteo_q || [];
var deviceType = /iPad/.test(navigator.userAgent) ? "t" :
/Mobile|iP(hone|od)|Android|BlackBerry|IEMobile|Silk/.test(navigator.userAgent) ? "m" : "d";
window.criteo_q.push(
{ event: "setAccount", account: 12345 },
{ event: "setSiteType", type: deviceType },
{ event: "viewHome" }
);
</script>
</body>
</html>
Product View Event
viewItem
Track when users view a single product:
window.criteo_q.push({
event: "viewItem",
item: "PROD_123" // Product ID matching your product feed
});
Product Page Example:
<script type="text/javascript" src="//dynamic.criteo.com/js/ld/ld.js" async="true"></script>
<script type="text/javascript">
window.criteo_q = window.criteo_q || [];
var deviceType = /iPad/.test(navigator.userAgent) ? "t" :
/Mobile|iP(hone|od)|Android|BlackBerry|IEMobile|Silk/.test(navigator.userAgent) ? "m" : "d";
// Get product ID from page
var productId = document.querySelector('[data-product-id]').dataset.productId;
window.criteo_q.push(
{ event: "setAccount", account: 12345 },
{ event: "setSiteType", type: deviceType },
{ event: "viewItem", item: productId }
);
</script>
Dynamic Implementation:
// Extract product ID from URL
function getProductIdFromUrl() {
const pathParts = window.location.pathname.split('/');
return pathParts[pathParts.indexOf('product') + 1] ||
new URLSearchParams(window.location.search).get('id');
}
// Or from data layer
function getProductIdFromDataLayer() {
return window.dataLayer?.[0]?.productId;
}
// Or from page content
function getProductIdFromPage() {
const skuElement = document.querySelector('[itemprop="sku"]');
const dataElement = document.querySelector('[data-product-id]');
return skuElement?.content || dataElement?.dataset.productId;
}
const productId = getProductIdFromDataLayer() ||
getProductIdFromPage() ||
getProductIdFromUrl();
window.criteo_q.push({
event: "viewItem",
item: productId
});
Important:
- Product ID must exactly match the ID in your product feed
- Fire only on product detail pages, not on category pages
- Include variant ID if you have product variants
Category/Listing Event
viewList
Track product listing pages with up to 3 products:
window.criteo_q.push({
event: "viewList",
item: ["PROD_123", "PROD_456", "PROD_789"] // Array of up to 3 product IDs
});
Category Page Example:
// Get visible products from category page
function getVisibleProducts(limit = 3) {
const productElements = document.querySelectorAll('.product-item');
const productIds = [];
for (let i = 0; i < Math.min(productElements.length, limit); i++) {
const productId = productElements[i].dataset.productId;
if (productId) {
productIds.push(productId);
}
}
return productIds;
}
window.criteo_q.push({
event: "viewList",
item: getVisibleProducts()
});
Search Results Page:
// Track search results
const searchResults = Array.from(
document.querySelectorAll('.search-result')
).slice(0, 3).map(el => el.dataset.productId);
if (searchResults.length > 0) {
window.criteo_q.push({
event: "viewList",
item: searchResults,
keywords: document.querySelector('#search-query').value // Optional
});
}
Shopping Cart Event
viewBasket
Track cart contents with product details:
window.criteo_q.push({
event: "viewBasket",
item: [
{ id: "PROD_123", price: 99.99, quantity: 1 },
{ id: "PROD_456", price: 49.99, quantity: 2 }
]
});
Complete Cart Event:
// Get cart data from page or API
function getCartItems() {
const cartItems = [];
const items = document.querySelectorAll('.cart-item');
items.forEach(item => {
cartItems.push({
id: item.dataset.productId,
price: parseFloat(item.dataset.price),
quantity: parseInt(item.dataset.quantity)
});
});
return cartItems;
}
// Fire on cart page
window.criteo_q.push({
event: "viewBasket",
item: getCartItems()
});
E-commerce Platform Integration:
// Shopify cart example
{% if template == 'cart' %}
<script>
window.criteo_q = window.criteo_q || [];
window.criteo_q.push({
event: "viewBasket",
item: [
{% for item in cart.items %}
{
id: "{{ item.product_id }}",
price: {{ item.final_price | money_without_currency }},
quantity: {{ item.quantity }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
});
</script>
{% endif %}
WooCommerce Example:
<!-- In cart.php template -->
<script>
window.criteo_q = window.criteo_q || [];
window.criteo_q.push({
event: "viewBasket",
item: [
<?php
foreach (WC()->cart->get_cart() as $cart_item) {
$product = $cart_item['data'];
echo '{';
echo 'id: "' . $product->get_id() . '",';
echo 'price: ' . $product->get_price() . ',';
echo 'quantity: ' . $cart_item['quantity'];
echo '},';
}
?>
]
});
</script>
React/SPA Example:
// React component
import { useEffect } from 'react';
import { useCart } from '../hooks/useCart';
export default function CartPage() {
const { items } = useCart();
useEffect(() => {
if (items.length > 0) {
window.criteo_q = window.criteo_q || [];
window.criteo_q.push({
event: "viewBasket",
item: items.map(item => ({
id: item.productId,
price: item.price,
quantity: item.quantity
}))
});
}
}, [items]);
return (
// Cart UI
);
}
Purchase/Conversion Event
trackTransaction
Track completed purchases - the most important event for ROI tracking:
window.criteo_q.push({
event: "trackTransaction",
id: "ORDER_12345", // Unique order ID
item: [
{ id: "PROD_123", price: 99.99, quantity: 1 },
{ id: "PROD_456", price: 49.99, quantity: 2 }
]
});
Complete Transaction Tracking:
// Order confirmation page
function trackCriteoTransaction() {
// Prevent duplicate firing
const transactionId = "ORDER_12345";
const firedTransactions = JSON.parse(
sessionStorage.getItem('criteo_transactions') || '[]'
);
if (firedTransactions.includes(transactionId)) {
console.log('Transaction already tracked');
return;
}
// Get order details
const orderItems = [
{ id: "PROD_123", price: 99.99, quantity: 1 },
{ id: "PROD_456", price: 49.99, quantity: 2 }
];
// Fire transaction event
window.criteo_q = window.criteo_q || [];
window.criteo_q.push({
event: "trackTransaction",
id: transactionId,
item: orderItems,
// Optional: Add new customer flag
new_customer: 1 // 1 for new customers, 0 for returning
});
// Mark as tracked
firedTransactions.push(transactionId);
sessionStorage.setItem('criteo_transactions',
JSON.stringify(firedTransactions));
}
// Execute on page load
if (document.readyState === 'complete') {
trackCriteoTransaction();
} else {
window.addEventListener('load', trackCriteoTransaction);
}
Platform-Specific Examples:
{% if first_time_accessed %}
<script>
window.criteo_q = window.criteo_q || [];
window.criteo_q.push({
event: "trackTransaction",
id: "{{ order.order_number }}",
item: [
{% for line_item in order.line_items %}
{
id: "{{ line_item.product_id }}",
price: {{ line_item.final_price | money_without_currency }},
quantity: {{ line_item.quantity }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
],
new_customer: {% if customer.orders_count == 1 %}1{% else %}0{% endif %}
});
</script>
{% endif %}
WooCommerce:
<!-- In thankyou.php template -->
<?php
// Prevent duplicate tracking
$order_tracked = get_post_meta($order->get_id(), '_criteo_tracked', true);
if (!$order_tracked) {
update_post_meta($order->get_id(), '_criteo_tracked', 'yes');
?>
<script>
window.criteo_q = window.criteo_q || [];
window.criteo_q.push({
event: "trackTransaction",
id: "<?php echo $order->get_order_number(); ?>",
item: [
<?php
foreach ($order->get_items() as $item) {
$product = $item->get_product();
echo '{';
echo 'id: "' . $product->get_id() . '",';
echo 'price: ' . $item->get_total() / $item->get_quantity() . ',';
echo 'quantity: ' . $item->get_quantity();
echo '},';
}
?>
]
});
</script>
<?php
}
?>
Important for Transaction Tracking:
- Fire only once per transaction
- Use unique, consistent order IDs
- Include all purchased items
- Fire only on order confirmation page
- Implement deduplication logic
Search Event
viewSearch
Track search queries for better retargeting:
window.criteo_q.push({
event: "viewSearch",
keywords: "wireless headphones", // Search query
item: ["PROD_123", "PROD_456", "PROD_789"] // Up to 3 result product IDs (optional)
});
Search Results Implementation:
// Get search query from URL or form
const searchQuery = new URLSearchParams(window.location.search).get('q') ||
document.querySelector('#search-input')?.value;
// Get top search results
const searchResults = Array.from(
document.querySelectorAll('.search-result-item')
).slice(0, 3).map(el => el.dataset.productId);
if (searchQuery) {
window.criteo_q.push({
event: "viewSearch",
keywords: searchQuery,
item: searchResults.length > 0 ? searchResults : undefined
});
}
Event Sequencing
Events should fire in this order on each page:
window.criteo_q = window.criteo_q || [];
// 1. Account setup (required, must be first)
window.criteo_q.push({ event: "setAccount", account: 12345 });
// 2. Device type (required, must be second)
window.criteo_q.push({ event: "setSiteType", type: deviceType });
// 3. User identification (optional, if user is logged in)
if (userEmail) {
window.criteo_q.push({ event: "setEmail", email: userEmail });
}
// 4. Page-specific event (required)
window.criteo_q.push({ event: "viewItem", item: productId });
Data Layer Integration
Standard Data Layer Pattern
// Set up data layer first
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
pageType: 'product',
productId: 'PROD_123',
productPrice: 99.99,
userId: 'USER_456',
userEmail: 'user@example.com'
});
// Then fire Criteo events using data layer
window.criteo_q = window.criteo_q || [];
const pageData = window.dataLayer[0];
window.criteo_q.push(
{ event: "setAccount", account: 12345 },
{ event: "setSiteType", type: deviceType }
);
if (pageData.userEmail) {
window.criteo_q.push({ event: "setEmail", email: pageData.userEmail });
}
switch (pageData.pageType) {
case 'home':
window.criteo_q.push({ event: "viewHome" });
break;
case 'product':
window.criteo_q.push({ event: "viewItem", item: pageData.productId });
break;
case 'cart':
window.criteo_q.push({ event: "viewBasket", item: pageData.cartItems });
break;
case 'confirmation':
window.criteo_q.push({
event: "trackTransaction",
id: pageData.orderId,
item: pageData.orderItems
});
break;
}
Testing and Validation
Console Testing
// Log all Criteo events
console.log('Criteo Events:', window.criteo_q);
// Verify specific events
window.criteo_q.forEach((event, index) => {
console.log(`Event ${index}:`, event.event, event);
});
// Monitor new events
const originalPush = window.criteo_q.push;
window.criteo_q.push = function(...args) {
console.log('New Criteo Event:', args);
return originalPush.apply(this, args);
};
Event Validation Checklist
-
setAccountis first event with correct account ID -
setSiteTypeis second event with correct device type - Page-specific event fires (viewHome, viewItem, etc.)
- Product IDs match product feed exactly
- Transaction event fires only once per order
- User email captured for logged-in users
- No JavaScript errors in console
- Network requests to criteo.com succeed
Debug Mode
// Enable Criteo debug logging
window.criteo_q.push({
event: "setAccount",
account: 12345,
debug: true
});
Best Practices
Product ID Consistency
- Use exact same IDs as in product feed
- Include variant IDs when applicable
- Avoid changing ID format
Event Deduplication
- Prevent transaction events from firing multiple times
- Use session storage or server-side tracking
- Implement page reload detection
Performance
- Load OneTag asynchronously
- Fire events after critical page content
- Minimize event payload size
Privacy
- Hash emails before sending
- Respect user consent preferences
- Implement opt-out mechanisms
Testing
Next Steps
- Data Layer Setup - Configure comprehensive product data
- Cross-Domain Tracking - Track users across domains
- Troubleshooting - Debug event tracking issues