Register Bid Response
Viewability tracking and impression counting via dlApi.registerBidResponse() — applies to all Ring DAS SSR ad formats
Register Bid Response
dlApi.registerBidResponse() is the SDK call that activates viewability tracking and counts an ad impression. It must be called in the browser after the ad is rendered — not on the server side.
Applies to all backend (SSR) formats: Sponsored Single Tile, Sponsored Product Slider, Brand Store, Branded Products. The rules on this page apply identically to all formats.
Filter before registering
Always ensure exactly one bid is passed to
registerBidResponse(). Only the firstseatbid[0].bid[0]entry is processed. Passing the full response with multiple seatbids registers one impression per seatbid, not one per rendered slot.
There are two ways to handle this:
- Format A (full OpenRTB response) — filter the response on the server side before injecting it into the page, so only the seatbid for the rendered slot is passed:
/**
* Filter the raw response to only the seatbid containing the given impid.
* Pass this filtered response to registerBidResponse() — not the full rawResponse.
*/
function filterResponse(rawResponse, impid) {
return {
...rawResponse,
seatbid: rawResponse.seatbid.filter(seat =>
seat.bid.some(b => b.impid === impid)
),
};
}- Format B (single bid object) — extract just the bid object on the server side and pass it directly. This eliminates the need for
filterResponse()entirely and reduces the injected payload size.
For single-slot formats (Brand Store, Branded Products), the full raw response contains only one seatbid, so filtering is not required with Format A. You may still filter as a defensive practice, or switch to Format B.
What it does
When called, registerBidResponse() instructs the DL API SDK to:
- Start observing the ad container for viewability (using IntersectionObserver)
- Fire an impression event once the ad is visible on screen
- Count the impression in Ring DAS campaign reporting
The SDK must be loaded on the page for this to work. Even for SSR formats where the server fetches the ad and renders it, the frontend SDK must still be present to handle the viewability measurement.
When to call it
Call registerBidResponse() only when the ad tile is actually rendered.
| Scenario | Call registerBidResponse()? |
|---|---|
| Ad fetched and rendered | Yes |
| Ad fetched but ad no rendered — slot stays empty | No |
| Shop business logic prevents displaying the ad | No |
| Server returned 204 (no ad) | No |
Calling registerBidResponse() for an empty slot counts a false impression, inflating campaign metrics incorrectly.
Per-slot rules for multi-slot formats
For formats that support multiple ad slots (Sponsored Product Slider, Sponsored Single Tile with multiple positions):
| Rule | Description |
|---|---|
| One call per slot | Each rendered slot requires its own registerBidResponse() call with its own container element ID |
| Filter before registering | Pass only the seatbid for the specific slot, not the full bid response |
| Skip unrendered slots | Do not call registerBidResponse() for slots that were not rendered — see When to call it |
The server injects a filtered response per slot into the page (or null if the slot was not rendered). The browser calls registerBidResponse() only for slots that were actually rendered:
// filteredResponseSlot1/2/3: injected by the server — null if slot was not rendered
dlApi.cmd.push(function (dlApi) {
if (filteredResponseSlot1) {
dlApi.registerBidResponse(filteredResponseSlot1, 'ad-sponsored-1');
}
if (filteredResponseSlot2) {
dlApi.registerBidResponse(filteredResponseSlot2, 'ad-sponsored-2');
}
if (filteredResponseSlot3) {
dlApi.registerBidResponse(filteredResponseSlot3, 'ad-sponsored-3');
}
});Passing
nullor an empty object is not a safe no-op — skip the call entirely for unrendered slots.
SSR pattern
The server resolves the ad, renders the HTML, and injects the bid data into the page. The browser SDK then picks it up after load.
Server side (Node.js / EJS example):
// After fetching and resolving the bid response, inject data into the page
// filteredResponse: the seatbid filtered for this specific slot
// containerId: the HTML element ID of the rendered ad container
const slotData = {
filteredResponse: filterResponse(bidResponse, matchedBidImpid),
containerId: 'ad-product-slider-slot-1',
};<script>
// Injected by server
const adSlotData = <%- JSON.stringify(slotData) %>;
</script>Browser side (runs after SDK loads):
dlApi.cmd.push(function (dlApi) {
// Pass filteredResponse — only the seatbid for this specific slot.
// Do NOT pass the full rawResponse with all seatbids.
dlApi.registerBidResponse(adSlotData.filteredResponse, adSlotData.containerId);
});Multi-slot pattern:
// adSlotsData: array of { filteredResponse, containerId } injected by the server
// filteredResponse is null for slots that were not rendered
dlApi.cmd.push(function (dlApi) {
adSlotsData.forEach(function (slot) {
if (slot.filteredResponse) {
dlApi.registerBidResponse(slot.filteredResponse, slot.containerId);
}
});
});API reference
dlApi.registerBidResponse(bidResponse, containerId)
| Parameter | Type | Description |
|---|---|---|
bidResponse | object | Full OpenRTB response or a single bid object (see formats below) |
containerId | string | The HTML id attribute of the element containing the rendered ad |
Accepted input formats
registerBidResponse() auto-detects the input format based on whether adm is present at the top level.
Format A — full OpenRTB response (or filtered subset):
{
seatbid: [{
bid: [{
adm: string | object, // Required — JSON string or pre-parsed object
}]
}]
}Format B — single bid object:
{
adm: string | object, // Required — JSON string or pre-parsed object
}Extract the bid object for the rendered slot on the server side and inject only that — no filterResponse() needed, and the injected payload is smaller.
adm payload
The adm field may be a JSON string or an already-parsed object — the SDK accepts both formats automatically. All other fields from the original bidder adm are optional — if payload size is a concern, you may strip them on the server side before injecting into the page.
{
adserver_meta: { // Required — pass whole object
...
},
meta: { // Required — pass whole object
...
},
gctx: string, // Required
lctx: string // Required
}Related
- Ad Preview & Debug Mode — Test SSR integrations without running live campaigns
- Sponsored Product Slider — Multi-slot SSR format with per-slot filtering
- Sponsored Single Tile — SSR format for search and category pages
- Brand Store (Web) — SSR format for product detail pages
- Branded Products — SSR format for homepage banners
Updated about 1 hour ago
