Over the years, the Prebid Server team has gotten requests to support "loss notifications" so bidders can refine their bidding algorithms. Whenever the request comes up, we decline with 3 objections:
- Prebid Server does not define the winner or loser of an auction and is not in the notification path. The ad server could choose a non-Prebid bid to win, there could be bid caching, there could be client-side price adjustments, etc. Bottom line is that Prebid cannot commit to knowing the "min bid to win".
- Hosting Prebid Server is expensive already - we do not want to support a 'real-time' notification because that adds to outbound network traffic.
- Putting some kind of info in a subsequent auction request would be ok, but the IAB has not seen fit to define a location to put it, and Prebid does not want to be in the business of defining OpenRTB extensions used as important conventions by other parties.
But the issue comes up again periodically, so in the spirit of trying to move the conversation forward, here's an option for Prebid Server to offer a relatively inexpensive way to provide the info that is available. Bidders need to be willing to (A) read from a Prebid extension, (B) accept incomplete info (C) be ready to join data on their side, and (D) be flexible about the timing of getting the data.
There are absolutely no guarantees about receiving "min bid to win" info at all. PBS will take reasonable efforts to piggy-back previous auction info on a future auction. It may come significantly delayed or might not come at all. Bidders will need to decide on their lookback period for joining into their data set.
- Define a way for bidders to opt-in. This feature creates compute burden, so bidders not ready to use it should not add to that burden.
It's ok if this is a module config that has to be updated for each bidder on each host company. Perhaps ask bidders that support this feature to update their Prebid docs with a flag.
- Define a prebid-specific "previous auction info" object on outgoing bid requests for which the bidder had actually successfully bid above the floor. The idea is that we need to avoid sending info to bidders are weren't serious about actually bidding. If there was a failure, that signal would be ok, but they can't get the actual highest bid without a successful bid.
Case of a successful bid:
$.ext.prebid.previousauctioninfo: [{
source: "pbs",
bidderRequestId: "123abc", // never specified by PBS - no such thing
auctionId: STRING, // $.id of the previous auction
impId: STRING, // $.imp.id of the previous auction
bidId: "456def", // seatbid[].bid[].id of the previous auction
rendered: 1, // default is 0 // never specified by PBS, will be forwarded from PBJS if present
adUnitCode: "div-gpt-ad-123-0", // imp.ext.gpid or imp.id
highestBidCpm: 0.052275935, // the highest bid for the imp this bid was a part of in the auction currency.
highestBidCurrency: "USD", // the auction currency
bidderCpm: 0.04, // default is null. // this bidder's bid in the auction currency
bidderOriginalCpm: 0.04, // this bidder's original bid
bidderOriginalCurrency: "USD", // this bidder's original bid currency
rejectionReason: -1, // not specified by PBS for successful bids
timestamp: 1739400860310
}]
Case of a rejected bid:
$.ext.prebid.previousauctioninfo: [{
source: "pbs",
bidderRequestId: "123abc", // never specified by PBS - no such thing
auctionId: STRING, // $.id of the previous auction
impId: STRING, // $.imp.id of the previous auction
bidId: "456def", // seatbid[].bid[].id of the previous auction
adUnitCode: "div-gpt-ad-123-0", // imp.ext.gpid or imp.id
rejectionReason: SEATNONBID, // only successful bids will ever get highestBidCpm
timestamp: 1739400860310
}]
If Prebid.js sent previous auction info on the request for this bidder, append it to anything produced by PBS.
{
ext.prebid.bidderconfig:
bidders: ["bidderA"],
config: {
ortb2: {
ext.prebid.previousauctioninfo: [{...}, {...}]
}
}
}
Note: this is going to require an update to bidderconfig because currently it only supports site/app/user, and soon to be device.
- Create a module that caches auction results and forwards them on the next auction
Runs at 2 stages:
- All Processed Bid Response Stage: if a bidder provides one or more bids, append them to a data structure:
However this is efficiently implemented in the code, logically, it's just a queue for each bidder:
{
"bidderA": [{ // supply highestBidCpm only if the bid was not rejected
auctionId: STRING, // $.id of the previous auction
impId: STRING, // $.imp.id of the previous auction
bidId: "456def", // seatbid[].bid[].id of the previous auction
adUnitCode: "div-gpt-ad-123-0", // imp.ext.gpid or imp.id
highestBidCpm: 0.052275935, // the highest bid for the imp this bid was a part of in the auction currency.
highestBidCurrency: "USD", // the auction currency
bidderCpm: 0.04, // this bidder's bid in the auction currency
bidderOriginalCpm: 0.04, // this bidder's original bid
bidderOriginalCurrency: "USD", // this bidder's original bid currency
timestamp: 1739400860310
},{
auctionId: STRING, // $.id of the previous auction
impId: STRING, // $.imp.id of the previous auction
bidId: "456def", // seatbid[].bid[].id of the previous auction
adUnitCode: "div-gpt-ad-123-0", // imp.ext.gpid or imp.id
bidderCpm: 0.04, // this bidder's bid in the auction currency
bidderOriginalCpm: 0.04, // this bidder's original bid
bidderOriginalCurrency: "USD", // this bidder's original bid currency
rejectionReason: 301, // did not meet floor
timestamp: 1739400860310
}],
"bidderB" : [{
...
}]
}
NoBids (seatnonbid code 0) are not added to the data structure.
- Bidder Request Stage: when a request is about to go out to a bidder, pull all previous auction info blocks off the bidder's queue and add them to the ORTB request.
That's it. Configuration of the module would define which bidders to do this for and max queue length.
To minimize mutex contention, I would propose very loose timing requirements. It would be ok for every thread to have it's own copy of the bidresponse queue, or for there to be a configurable number of queues per server that are processed round-robin. The implication is that there's absolutely no guaranteed about if/when the bidder would receive the additional information. e.g:
- if there are 1000 threads in a large server and traffic is declining, the previous auction info in thread 999 might get stuck there until traffic rises the next day.
- if the server is restarted, any cached info is lost with no attempt to save or transmit
- Make sure there's a configurable max size. Optionally there could also be a max amount of time that the previous auction info can stay on the queue.
Discuss.
Over the years, the Prebid Server team has gotten requests to support "loss notifications" so bidders can refine their bidding algorithms. Whenever the request comes up, we decline with 3 objections:
But the issue comes up again periodically, so in the spirit of trying to move the conversation forward, here's an option for Prebid Server to offer a relatively inexpensive way to provide the info that is available. Bidders need to be willing to (A) read from a Prebid extension, (B) accept incomplete info (C) be ready to join data on their side, and (D) be flexible about the timing of getting the data.
There are absolutely no guarantees about receiving "min bid to win" info at all. PBS will take reasonable efforts to piggy-back previous auction info on a future auction. It may come significantly delayed or might not come at all. Bidders will need to decide on their lookback period for joining into their data set.
It's ok if this is a module config that has to be updated for each bidder on each host company. Perhaps ask bidders that support this feature to update their Prebid docs with a flag.
Case of a successful bid:
Case of a rejected bid:
If Prebid.js sent previous auction info on the request for this bidder, append it to anything produced by PBS.
Note: this is going to require an update to bidderconfig because currently it only supports site/app/user, and soon to be device.
Runs at 2 stages:
However this is efficiently implemented in the code, logically, it's just a queue for each bidder:
NoBids (seatnonbid code 0) are not added to the data structure.
That's it. Configuration of the module would define which bidders to do this for and max queue length.
To minimize mutex contention, I would propose very loose timing requirements. It would be ok for every thread to have it's own copy of the bidresponse queue, or for there to be a configurable number of queues per server that are processed round-robin. The implication is that there's absolutely no guaranteed about if/when the bidder would receive the additional information. e.g:
Discuss.