Branded Products
A homepage display banner showing brand logo and curated products
Branded Products
A homepage display banner showing the brand logo and a curated set of products selected by the brand.
How It Works
Important: This is a hybrid format. The ad server provides the brand logo and a list of product IDs. Your shop must fetch product details (image, price, name, URL) for each product ID from your own system.
| Source | Data |
|---|---|
| Ad Server | Brand logo (bannerImage), list of product IDs (offers[].product_id), click tracking, DSA info |
| Your Shop | Product details for each ID: image, price, name, URL |
Data flow:
- Ad server returns
bannerImage(brand logo) andoffers[]array withproduct_idfor each product - Your shop extracts each
product_idfrom the offers - Your shop fetches product details (image, price, name, URL) from your product database
- Your shop renders the banner with combined data
Number of products is configurable. The examples in this documentation show 5 products, but your shop decides how many to display (e.g., 2, 5, 7, or more). Simply adjust the limit in your rendering code.
Live Demo
See the working implementation:
Frontend Integration (CSR)
Step 1: Load the SDK
Configure the SDK with your Ring DAS credentials. You will receive these values from your Ring DAS account manager.
| Parameter | Description |
|---|---|
target | Your site identifier in Ring DAS |
tid | Your Ring DAS tenant ID (format: EA-XXXXXXX) |
dsainfo | Set to 1 to enable DSA (Digital Services Act) transparency info in responses |
<script>
dlApi = {
target: "demo_page", // your site ID - provided by Ring DAS
tid: 'EA-1746213', // your tenant ID - provided by Ring DAS
dsainfo: 1, // enable DSA transparency info
cmd: [] // command queue
};
</script>
<!-- SDK URL - provided by your Ring DAS account manager -->
<script src="https://das-cdn.idealo.com/build/dlApi/minit.boot.min.js" async></script>The SDK script URL (
minit.boot.min.js) may differ depending on your integration. Your Ring DAS account manager will provide the correct URL for your setup.
Step 2: Configure Consent
As soon as possible after embedding the script, you should pass consent information. This determines whether personalized ads are served and controls the script's behavior.
dlApi.cmd.push(function (dlApi) {
dlApi.consent({npa: 0}); // 0 = consent given, 1 = no consent
});| Parameter | Value | Description |
|---|---|---|
npa | 0 | Consent given - personalized ads enabled |
npa | 1 | No consent - non-personalized ads only |
Step 3: Fetch the Ad
Use fetchNativeAd() to fetch the ad. The ad server returns the brand logo and product IDs - you must fetch product details from your shop.
| Parameter | Description |
|---|---|
slot | Fixed value branded_products for this format |
div | Container element ID - required for viewability measurement |
tplCode | Fixed value 1746213/Sponsored-Product-Plus for Branded Products |
asyncRender | Set to true to manually control when impression is counted |
offers_limit | Maximum number of products returned from ad server |
dlApi.cmd.push(function (dlApi) {
// Fetch ad using fetchNativeAd
dlApi.fetchNativeAd({
slot: 'branded_products', // fixed value for Branded Products
div: 'branded-products-container', // container ID for viewability tracking
opts: {
offers_limit: 5 // maximum number of products returned from ad server
},
tplCode: '1746213/Sponsored-Product-Plus', // fixed value for Branded Products
asyncRender: true, // manually count impression with ad.render()
}).then(async function (ad) {
if (!ad) {
console.log('[Branded Products] No ad available');
return;
}
console.log('[Branded Products] Ad response:', ad);
// 1. Extract data from ad server
const bannerImage = ad.fields?.bannerImage || ''; // Brand logo URL
const offers = ad.fields?.feed?.offers || [];
const dsaInfo = ad.dsa;
// 2. Extract product IDs from offers
const productIds = offers.map(offer => offer.product_id);
console.log('[Branded Products] Product IDs to fetch:', productIds);
// 3. Fetch product details from YOUR shop (implement this function)
const productDetails = await fetchProductDetailsFromShop(productIds);
// 4. Combine shop data with ad server tracking URLs
// Construct click tracking URLs (base URL + offer parameter)
// If meta.adclick is empty, use offer_url directly (without encoding)
const productsWithTracking = productDetails.map((product, index) => ({
...product,
trackingUrl: ad.meta.adclick
? ad.meta.adclick + encodeURIComponent(offers[index]?.offer_url || '')
: (offers[index]?.offer_url || '')
}));
// 5. Render the ad with combined data
renderBrandedProducts(bannerImage, productsWithTracking, dsaInfo);
// 6. Count impression after rendering
ad.render();
}).catch(function (err) {
console.error('[Branded Products] Ad could not be loaded:', err);
});
// Trigger ad fetch
dlApi.fetch();
});Step 4: Fetch Product Details from Your Shop
Implement a function to fetch product details based on product IDs. This connects to YOUR product database/API.
/**
* Fetch product details from your shop's product API
* @param {string[]} productIds - Array of product IDs from ad server
* @returns {Promise<Array>} - Product details with image, price, name, URL
*/
async function fetchProductDetailsFromShop(productIds) {
// Example: Call your shop's product API
const products = await Promise.all(
productIds.map(async (productId) => {
// Replace with your actual product API endpoint
const response = await fetch(`/api/products/${productId}`);
const product = await response.json();
return {
product_id: productId,
image: product.imageUrl,
name: product.title,
price: product.price,
url: product.detailPageUrl
};
})
);
return products.filter(p => p !== null); // Filter out any failed fetches
}Implementation note: The onclick handler shown below is an example. Your shop is responsible for implementing click tracking - simply fire a tracking pixel on product click using the constructed tracking URL.
Example code: The rendering function below is provided as a documentation example. Adapt it to your shop's templating system and styling requirements.
Step 5: Render the Ad
Render the brand logo and product tiles with the combined data:
function renderBrandedProducts(bannerImage, products, dsaInfo) {
const container = document.getElementById('branded-products-container');
const MAX_PRODUCTS = 5;
// Build products HTML using shop data + ad server tracking URLs
let productsHtml = '';
products.slice(0, MAX_PRODUCTS).forEach(function(product) {
const price = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
}).format(product.price);
productsHtml += `
<a href="${product.url}" class="product-tile"
target="_blank" rel="noopener nofollow sponsored"
onclick="new Image().src='${product.trackingUrl}';">
<img src="${product.image}" alt="${escapeHtml(product.name)}">
<div class="product-name">${escapeHtml(product.name)}</div>
<div class="product-price">${price}</div>
</a>
`;
});
// Build DSA info HTML (Digital Services Act transparency)
let dsaHtml = '';
if (dsaInfo) {
dsaHtml = `
<div class="dsa-info">
<span class="dsa-icon" title="Ad transparency">i</span>
<div class="dsa-tooltip">
<div>Advertiser: ${dsaInfo.behalf || 'N/A'}</div>
<div>Paid by: ${dsaInfo.paid || 'N/A'}</div>
</div>
</div>
`;
}
// Render complete format
// bannerImage = from ad server
// products = from YOUR shop + trackingUrl from ad server
container.innerHTML = `
<div class="sponsored-header">
<span class="sponsored-badge">Sponsored</span>
${dsaHtml}
</div>
<div class="brand-section">
<img class="brand-logo" src="${bannerImage}" alt="Brand Logo">
</div>
<div class="products-section">
${productsHtml}
</div>
`;
}
// Helper function to escape HTML special characters
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}Backend Integration (SSR)
For server-side rendering, fetch ad data from the bidder, then fetch product details from your shop before rendering the page.
Required: The SDK from Step 1 (Frontend) must still be loaded on the page for viewability tracking via
registerBidResponse().
Step 1: Fetch Ad from Bidder
Make a GET request to the Ring DAS bidder endpoint. The bidder URL will be provided by your Ring DAS account manager.
| Parameter | Description |
|---|---|
BIDDER_URL | Bidder endpoint URL - provided by your Ring DAS account manager |
NETWORK_ID | Your Ring DAS network ID (e.g., 1746213 for Branded Products) |
site.id | Your site identifier in Ring DAS |
imp[].tagid | Fixed value branded_products for this format |
user.ext.npa | Consent flag: false = consent given, true = no consent |
regs.gdpr | GDPR applies flag: 1 = GDPR applies, 0 = does not apply |
regs.gpp | GPP consent string (optional) - TCF-compliant consent string from your CMP |
regs.ext.dsa | Set to 1 to request DSA transparency info in response |
imp[].ext.no_redirect | Set to true if adclick URLs should only fire tracking pixel without redirect (shop handles redirect via href) |
tmax | Request timeout in milliseconds |
Consent handling: You can pass consent via
user.ext.npa(simple flag) OR viaregs.gpp(TCF-compliant string). If you have a TCF-compliant CMP, use the GPP string for more granular consent information.
// Node.js backend
const NETWORK_ID = '1746213'; // Branded Products network ID
const BIDDER_URL = `https://das.idealo.com/${NETWORK_ID}/bid`;
const requestBody = {
id: Math.random().toString(16).substring(2, 15),
imp: [{
id: 'imp-1',
tagid: 'branded_products', // fixed value for Branded Products
secure: 1,
native: { request: "{}" },
ext: {
offers_limit: 5, // maximum number of products returned from ad server
no_redirect: true // adclick URLs fire tracking only, no redirect
}
}],
site: { id: 'demo_page' }, // your site ID
user: {
ext: {
npa: false // false = consent given, true = no consent
}
},
ext: {
network: NETWORK_ID,
keyvalues: {}, // optional targeting parameters
is_non_prebid_request: true
},
regs: {
gdpr: 1, // 1 = GDPR applies, 0 = does not apply
gpp: 'YOUR_GPP_STRING', // optional: TCF-compliant consent string from CMP
ext: {
dsa: 1 // request DSA transparency info
}
},
tmax: 1000 // request timeout in milliseconds
};
// Send as GET request with body in data= parameter
const encodedData = encodeURIComponent(JSON.stringify(requestBody));
const response = await fetch(`${BIDDER_URL}?data=${encodedData}`);
// Check for no ad available (204 No Content)
if (response.status === 204) {
console.log('No ad available');
}
const bidResponse = await response.json();No ad available: When no ad matches the request criteria, the server returns HTTP
204 No Contentwith an empty response body. Always check the status code before parsing JSON.
Step 2: Parse Response and Fetch Product Details
Extract the brand logo and product IDs, then fetch product details from your shop:
const bid = bidResponse?.seatbid?.[0]?.bid?.[0];
if (!bid) {
return null; // No bid in response
}
// Parse adm (it's a JSON string)
const adm = JSON.parse(bid.adm);
// 1. Extract data from ad server
const bannerImage = adm.fields?.bannerImage || null; // Brand logo URL
const offers = adm.fields?.feed?.offers || []; // Array with product_id
// 2. Extract product IDs
const productIds = offers.map(offer => offer.product_id);
console.log('Product IDs from ad server:', productIds);
// 3. Fetch product details from YOUR shop database
const shopProducts = await fetchProductsFromDatabase(productIds);
// 4. Combine shop data with ad server tracking URLs (base URL + offer parameter)
// If meta.adclick is empty, use offer_url directly (without encoding)
const products = shopProducts.map((product, index) => ({
...product,
trackingUrl: adm.meta.adclick
? adm.meta.adclick + encodeURIComponent(offers[index]?.offer_url || '')
: (offers[index]?.offer_url || '')
}));
// 5. Extract DSA transparency info
const dsaInfo = bid.ext?.dsa ? {
advertiser: bid.ext.dsa.behalf,
payer: bid.ext.dsa.paid
} : null;
// 6. Prepare data for rendering
const adData = {
bannerImage, // From ad server
products, // From YOUR shop + trackingUrl from ad server
dsaInfo,
rawResponse: bidResponse,
containerId: 'branded-products-container'
};
/**
* Fetch product details from your database
* @param {string[]} productIds - Product IDs from ad server
*/
async function fetchProductsFromDatabase(productIds) {
// Replace with your actual database query
const products = await db.products.findMany({
where: { id: { in: productIds } },
select: {
id: true,
imageUrl: true,
title: true,
price: true,
detailPageUrl: true
}
});
return products.map(p => ({
product_id: p.id,
image: p.imageUrl,
name: p.title,
price: p.price,
url: p.detailPageUrl
}));
}Step 3: Render HTML + Register Response
Render the branded products banner combining ad server data (logo) with your shop data (products):
<!-- Render branded products banner -->
<div id="branded-products-container" class="branded-products">
<div class="sponsored-header">
<span class="sponsored-badge">Sponsored</span>
<% if (adData.dsaInfo) { %>
<div class="dsa-info">
<span>Advertiser: <%= adData.dsaInfo.advertiser %></span>
<span>Paid by: <%= adData.dsaInfo.payer %></span>
</div>
<% } %>
</div>
<!-- Brand logo from AD SERVER -->
<div class="brand-section">
<img class="brand-logo" src="<%= adData.bannerImage %>" alt="Brand Logo">
</div>
<!-- Product details from YOUR SHOP + tracking URLs from AD SERVER -->
<div class="products-section">
<% adData.products.slice(0, 5).forEach(function(product) { %>
<a href="<%= product.url %>" class="product-tile"
target="_blank" rel="noopener nofollow sponsored"
onclick="new Image().src='<%= product.trackingUrl %>';">
<img src="<%= product.image %>" alt="<%= product.name %>">
<div class="product-name"><%= product.name %></div>
<div class="product-price">
<%= new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(product.price) %>
</div>
</a>
<% }); %>
</div>
</div>
<!-- Register for viewability and impression tracking -->
<script>
dlApi.cmd.push(function () {
dlApi.registerBidResponse(<%- JSON.stringify(adData.rawResponse) %>, 'branded-products-container');
});
</script>What the Ad Server Returns
| Field | Description |
|---|---|
ad.fields.bannerImage | Brand logo URL (render directly) |
ad.fields.feed.offers | Array of product objects |
ad.fields.feed.offers[].product_id | Product ID - use to fetch details from your shop |
ad.fields.feed.offers[].offer_url | Click tracking parameter - append to meta.adclick for full tracking URL |
ad.dsa.behalf | Advertiser name (DSA compliance, requires dsainfo: 1) |
ad.dsa.paid | Entity that paid for the ad (DSA compliance) |
ad.meta.adclick | Click tracking base URL |
ad.meta.adid | Ad identifier |
Key difference from Brand Store: The ad server provides the brand logo AND tells you which products to display (via
product_id). Your shop must fetch the actual product details (image, price, name, URL) from your own product database.
Related
- Format Overview - Compare available ad formats
- Brand Store - Button format for product detail pages
- Ad Delivery Overview - Learn about Ring DAS ad delivery
Updated about 16 hours ago
