diff --git a/_data/sidebar.yml b/_data/sidebar.yml index 4a10ea3ab0..b1d0048a71 100644 --- a/_data/sidebar.yml +++ b/_data/sidebar.yml @@ -500,7 +500,7 @@ subgroup: 8 - sbSecId: 1 - title: Prebid Ad Slot + title: Prebid Ad Slot and GPID link: /features/pbAdSlot.html isHeader: 0 isSectionHeader: 0 diff --git a/dev-docs/modules/gpt-pre-auction.md b/dev-docs/modules/gpt-pre-auction.md index 504b648e59..189e5e1dae 100644 --- a/dev-docs/modules/gpt-pre-auction.md +++ b/dev-docs/modules/gpt-pre-auction.md @@ -19,19 +19,25 @@ sidebarType : 1 ## Overview -This module enables targeting and tracking at the ad server adunit level. +This module enables bidder targeting and tracking at the ad server ad slot level. -Enabled by default if compiled into your package, this module will add the [Prebid Ad Slot](/features/pbAdSlot.html) and matching GAM ad unit name to each ad unit's first-party data before bid requests are sent to the adapters. +This module is enabled by default if it's compiled into your PBJS package. It will add the [Prebid Ad Slot and GPID](/features/pbAdSlot.html) along with the matching GAM ad unit name to each ad unit's first-party data before bid requests are sent to the adapters. * **Prebid.js Adapters** - will be able to utilize these values as: + * AdUnit.ortb2Imp.ext.gpid="/1111/home-left" * AdUnit.ortb2Imp.ext.data.adserver.name="gam" * AdUnit.ortb2Imp.ext.data.adserver.adslot="/1111/home" * AdUnit.ortb2Imp.ext.data.pbadslot="/1111/home-left" * **Prebid Server Adapters** - will see the OpenRTB as: + * imp[].ext.gpid * imp[].ext.data.adserver.name * imp[].ext.data.adserver.adslot * imp[].ext.data.pbadslot +{: .alert.alert-info :} +The Prebid Ad Slot didn't get broad adoption, so it's likely that +someday we'll deprecate it in favor of the more standard GPID. + ## Configuration {: .alert.alert-info :} @@ -67,23 +73,87 @@ pbjs.setConfig({ ## How It Works -When this module is on, it uses the BEFORE_REQUEST_BIDS event to insert functionality that: +When this module is turned on, it uses the BEFORE_REQUEST_BIDS event to insert functionality that: - loops through each adunit in the auction -- maps the adunit to the GPT slot using the same algorithm as setTargetingForGPTAsync including customGptSlotMatching +- maps the PBJS adunit to the GPT slot using the same algorithm as setTargetingForGPTAsync including customGptSlotMatching + +### Defining the AdServer name and adslot If GPT slot matching succeeds: - it sets the Adunit ortb2Imp.ext.data.adserver.name to 'gam' - it copies the resulting GPT slot name to ortb2Imp.ext.data.adserver.adslot +### Defining Prebid Ad Slot + The customPbAdSlot function is called if it was specified, writing the results to ortb2Imp.ext.data.pbadslot. -If there's no customPbAdSlot, a default algorithm is used to determine ortb2Imp.ext.data.pbadslot: + +If there's no customPbAdSlot function, a default algorithm is used to determine ortb2Imp.ext.data.pbadslot: - first use the AdUnit's ortb2Imp.ext.data.pbadslot if defined -- else, see if the AdUnit.code corresponds to a div and if so, try to retrieve a data element from the div called data-adslotid. +- else, see if the AdUnit.code corresponds to a div-id and if so, try to retrieve a data element from the div called data-adslotid. - else if the GPT slot matching succeeded, use the GPT slot name -- else, just use the AdUnit.code, assuming that that's the ad unit slot +- else, just use the AdUnit.code + +### Defining GPID + +Here's what the module does to define GPID: + +1. If AdUnit.ortb2Imp.ext.gpid already exists, don't do anything. Assume the publisher or another module has provided the value. +2. Otherwise, if a customPbAdSlot function was defined by the publisher and the result is not empty, then copy that value to AdUnit.ortb2Imp.ext.gpid. +3. Otherwise, if a value was found for GAM AdSlot, copy that to AdUnit.ortb2Imp.ext.gpid + +## Example customPbAdSlot function + +The following customPbAdSlot function will work for many publishers. Assumptions: +- AdUnits have been registered with [pbjs.addAdUnits](/dev-docs/publisher-api-reference/addAdUnits.html). +- AdUnit.code is either the GPT slot name or the div-id. +- The site has unique (non-random) div-ids. + +If either of these isn't the case, you'll need to supply your own function. + +``` +// Use adunit.ortb2Imp.ext.data.pbadslot if it exists. +// compare adunit.code to find a single matching slot in GPT +// if there is a single slot match, just use that slot name +// finally, there must be multiple slots that match. Define pbadslot as slot#div + +pbjs.setConfig({ + gptPreAuction: { + enabled: true, // enabled by default + customPbAdSlot: function(adUnitCode, adServerAdSlot) { + // get adunit object + au=pbjs.adUnits.filter(au => au.code==adUnitCode); + if (au.length==0) { + return; + } + + // use pbadslot if supplied + if (au[0].ort2bImp && au[0].ort2bImp.ext && au[0].ort2bImp.ext.data && au[0].ort2bImp.ext.data.pbadslot) { + return au[0].ort2bImp.ext.data.pbadslot; + } + + // confirm that GPT is set up + if (!(googletag && googletag.apiReady)) { + return; + } + // find all GPT slots with this name + var gptSlots = googletag.pubads().getSlots().filter(function(gpt) { + return gpt.getAdUnitPath() == adServerAdSlot; + }); + if (gptSlots.length==0) { + return; // should never happen + } + if (gptSlots.length==1) { + return adServerAdSlot; + } + // else the adunit code must be div id. append it. + return adServerAdSlot+"#"+adUnitCode; + } + }); +}; +``` # Further Reading -- [Prebid Ad Slot](/features/pbAdSlot.html) +- [Prebid Ad Slot and GPID](/features/pbAdSlot.html) diff --git a/features/pbAdSlot.md b/features/pbAdSlot.md index 05478a0f76..8da35d6380 100644 --- a/features/pbAdSlot.md +++ b/features/pbAdSlot.md @@ -1,114 +1,151 @@ --- layout: page_v2 -title: Prebid Ad Slot -description: The Prebid Ad Slot +title: Prebid Ad Slot and GPID +description: Prebid Ad Slot and GPID sidebarType: 1 --- -# Prebid Ad Slot +# The Prebid Ad Slot and the GPID +{:.no_toc} -The Prebid AdUnit 'code' is a mixed attribute that's generally either the GPT slot name or the HTML div ID. The undecided nature of the 'code' makes it harder to utilize for reporting and auction targeting. +* TOC +{:toc} -The `Prebid Ad Slot` is an optional inventory management convention allowing publishers to supply a descriptive and stable label for each ad on the page. This makes it possible to have more granular reporting and better deal targeting. +Prebid Ad Slot and the Global Placement ID (GPID) are overlapping conventions that allow publishers to identify ad inventory on their pages so bidders and reporting systems can better deal with their sites. + +## Background + +It all starts with how publishers decide to label their ad slots: the places on their pages +where ads can be served. In some ad servers like GAM, these things are called "ad units". +Most publishers use unique ad slot names, but some publishers utilize the same name for every ad slot on their page. e.g. "/homepage" might be the name for 5 different slots. + +It's the case of 'same ad slot names' that Prebid Ad Slot and GPID are +meant to address. + +### The Prebid.js AdUnit + +When Prebid.js was developed in 2015, they needed a data structure that would link each ad slot to the bidders and parameters involved in the auction for that slot. Thus was born the Prebid.js [AdUnit](/dev-docs/adunit-reference.html). The AdUnit 'code' is what links this object to the adserver's ad slot. Because some pubs use the same ad slot name everywhere, AdUnit.code is a mixed attribute that can be either the ad slot name **or** the HTML div ID. The undecided nature of AdUnit.code makes it hard to utilize for reporting and auction targeting. + +### The Prebid Ad Slot + +The 'Prebid Ad Slot' was developed in Prebid.js v3 as an optional inventory management convention allowing publishers to supply a descriptive and stable label for each ad on the page. This makes it possible to have more granular reporting and better deal targeting. +However, the PB ad slot is not an industry standard convention, so didn't gain +much traction. + +### The GPID + +The Global Placement ID (GPID) was an initiative in the Fall of 2021 led +by the TradeDesk to solve the problem of inventory identification in an industry-wide way. i.e. Buyers want to be able to identify ad slots in a unique way even +when the publisher uses the same ad slot name multiple times. + +The original suggestion for GPID was to simply append the HTML div element id (aka the 'div-id') to the ad slot name. But some publishers generate div-ids randomly, so the definition of GPID has become: + +``` +imp[].ext.gpid: ADSLOTNAME#UNIQUIFIER +``` +Where ADSLOTNAME is the ad server's slot name (e.g. /1111/homepage) and UNIQUIFIER is something that makes the ADSLOTNAME different from others. Normally it's a +div-id, but if div-ids are random, it can be something else. The "#UNIQUIFIER" is only required if the ADSLOTNAME isn't unique enough on its own. {: .alert.alert-info :} -The Prebid Ad Slot was introduced with Prebid.js 3.x. +The Prebid Ad Slot didn't ever get broad adoption, so it's likely that +someday we'll deprecate it in favor of the more standard GPID. -## A Scenario +## Defining Prebid Ad Slot and GPID -1. The publisher utilizes the same 'slotname' in the page for multiple holes-in-the-page, differentiating in the ad server by size. e.g. -- defineSlot('/1111/homepage', [[300,250]], 'div-293rj893p9wje9we9fj'); -- defineSlot('/1111/homepage', [[728,90]], 'div-j98s9u9usj987665da'); -- defineSlot('/1111/homepage', [[160,600]], 'div-B2q3s4gseshekhsei9sh'); -2. In order to be able to display the right ad in the right hole, the Prebid AdUnit therefore sets the 'code' to the div ID instead of the slotname. -3. The div ID in this case is a random number, not very useful for reporting. -4. Therefore, to get a stable ID that's useful from a business perspective to identify a hole-in-the-page, the publisher -decides to add another identifier... the Prebid Ad Slot. -5. The publisher adds a function to the page that annotates each Prebid AdUnit in the auction with the `pbadslot`. -6. Participating bid adapters read the `pbadslot` and can target deals to them. -7. Participating analytics adapters read the `pbadslot` for more granular reporting. - -Example page function: -{% highlight js %} - -// Use adunit.ortb2Imp.ext.data.pbadslot if it exists. Otherwise, if the -// the adunit.code is a div ID, then look for a data-adslotid attribute, then look a matching slot in GPT -// Otherwise, just use the AdUnit.code -var setPbAdSlot = function setPbAdSlot(adUnits) { - // set pbadslot for all ad units - adUnits.forEach(function (adUnit) { - if (!adUnit.ortb2Imp) { - adUnit.ortb2Imp = {} - } - if (!adUnit.ortb2Imp.ext) { - adUnit.ortb2Imp.ext = {}; - } - if (!adUnit.ortb2Imp.ext.data) { - adUnit.ortb2Imp.ext.data = {}; - } - - // use existing pbadslot if it is already set - if (adUnit.ortb2Imp.ext.data.pbadslot) { - return; - } - - // check if AdUnit.code has a div with a matching id value - const adUnitCodeDiv = document.getElementById(adUnit.code); - if (adUnitCodeDiv) { - // try to retrieve a data element from the div called data-adslotid. - if (adUnitCodeDiv.dataset.adslotid) { - adUnit.ortb2Imp.ext.data.pbadslot = adUnitCodeDiv.dataset.adslotid; - return; - } - // Else if AdUnit.code matched a div and it's a banner mediaType and googletag is present - if (adUnit.mediaTypes && typeof adUnit.mediaTypes === 'object' && adUnit.mediaTypes.banner && adUnit.mediaTypes.banner.sizes && window.googletag && googletag.apiReady) { - var gptSlots = googletag.pubads().getSlots(); - // look up the GPT slot name from the div. - var linkedSlot = gptSlots.find(function (gptSlot) { - return (gptSlot.getSlotElementId() === adUnitCodeDiv.id); - }); - if (linkedSlot) { - adUnit.ortbImp.ext.data.pbadaslot = linkedSlot.getAdUnitPath(); - return; - } - } - } - // Else, just use the AdUnit.code, assuming that it's an ad unit slot - adUnit.ortb2Imp.ext.data.pbadslot = adUnit.code; - }); -}; +There are two ways a publisher can inject these values into the header bidding auctions: -pbjs.onEvent('beforeRequestBids', setPbAdSlot); +1. Supply them manually on the PBJS AdUnits +2. Install the [GPT Pre-Auction module](/dev-docs/modules/gpt-pre-auction.html) -{% endhighlight %} +### Defining them on the PBJS Ad Unit -## How It Works +#### Example 1 - unique ad slot names -The Prebid Ad Slot is just a convention -- it's a form of adunit-specific first party data -stored under `adunit.ortb2Imp.ext.data.pbadslot`. -It can be utilized by any code ready to look for it. +In this example, there's no need for the "UNIQUIFIER" string because every ad slot +on the publisher page is already unique. -It's intended to be specified via Prebid.js in one of two ways: +``` +pbjs.addAdUnits({ + code: '/1111/homepage-leftnav', + ortb2Imp: { + ext: { + gpid: "/1111/homepage-leftnav", + data: { + pbadslot: "/1111/homepage-leftnav" + } + } + }, + mediaTypes: ... + bids: ... +}); +``` + +#### Example 2 - duplicate ad slots + +In this example, the publisher's ad slots all have the same name, but at least + the div-ids are unique. + +``` +pbjs.addAdUnits({ + code: 'div-leftnav', + ortb2Imp: { + ext: { + gpid: "/1111/homepage#div-leftnav", + data: { + pbadslot: "/1111/homepage#div-leftnav" + } + } + }, + mediaTypes: ... + bids: ... +}); +``` -1. Either directly on the AdUnit itself -2. Or defined during the run of a function before the auction +#### Example 3 - duplicate ad slots, random div IDs -The function could determine the pbadslot in any way that produces a stable value useful for targeting and reporting. -Some scenarios that could be supported: +In this example, the publisher utilizes the same 'slotname' in the page for multiple holes-in-the-page, differentiating in the ad server by size. They also use random div-ids. e.g. +- defineSlot('/1111/homepage', [[300,250]], 'div-293rj893p9wje9we9fj'); +- defineSlot('/1111/homepage', [[728,90]], 'div-j98s9u9usj987665da'); -- parse a substring of the ad server's slot name -- use a custom div data element ID, else the AdUnit.code -- use the AdUnit.ortb2Imp.ext.data.pbadslot as a default rather than primary -- support a different ad server +``` +pbjs.addAdUnits({ + code: 'div-293rj893p9wje9we9fj', + ortb2Imp: { + ext: { + gpid: "/1111/homepage#300x250", + data: { + pbadslot: "/1111/homepage#300x250" + } + } + }, + mediaTypes: ... + bids: ... +},{ + code: 'div-j98s9u9usj987665da', + ortb2Imp: { + ext: { + gpid: "/1111/homepage#728x90", + data: { + pbadslot: "/1111/homepage#728x90" + } + } + }, + mediaTypes: ... + bids: ... +}); +``` ## Prebid Server -The OpenRTB location for the Prebid Ad Slot is `imp[].ext.data.pbadslot`: +The Prebid Server Bid Adapter just sends the values to the conventional OpenRTB locations: +- Prebid Ad Slot is `imp[].ext.data.pbadslot` +- GPID is `imp[].ext.gpid` + +Mobile and AMP Stored Requests should place the values there as desired. -- The Prebid SDK will place the value there. -- AMP Stored Requests should place the value there if desired. -- Server-side bid and anlytics adapters may be modified to read the value. +Server-side bid and anlytics adapters may be modified to read the value. ## Further Reading -- The [onEvent()](/dev-docs/publisher-api-reference/onEvent.html) function +- [GPT Pre-Auction Module](/dev-docs/modules/gpt-pre-auction.html) +- [Ad Unit Reference](/dev-docs/adunit-reference.html)