Prebid Server extensions in ext.prebid and imp.ext.prebid
Prebid Server takes an OpenRTB 2.x request and reads its own instructions from ext.prebid and imp[].ext.prebid. Bidder parameters go in imp.ext.prebid.bidder, keyed by bidder code. Before it calls the adapters, the server splits the request so each bidder sees only its own imps, with its parameters moved to imp.ext.bidder and most of ext.prebid removed.
Where Prebid Server reads its instructions
| Path | Purpose |
|---|---|
imp[].ext.prebid.bidder.{bidder} | Bidder parameters. Naming a bidder here is what offers it the imp. |
imp[].ext.prebid.imp.{bidder} | Imp fields for one bidder, merged into that bidder's copy of the imp |
imp[].ext.prebid.storedrequest | Merge a stored imp by id |
imp[].ext.prebid.storedauctionresponse, storedbidresponse | Return canned responses instead of calling bidders |
ext.prebid.bidderparams.{bidder} | Parameters merged into every imp for that bidder |
ext.prebid.aliases | Extra codes that point at an existing adapter |
ext.prebid.targeting | Ad server key-values in the response |
ext.prebid.cache | Cache bids or VAST and return cache IDs |
ext.prebid.storedrequest | Merge a stored request by id |
ext.prebid.debug, ext.prebid.trace | Debug and trace output in the response |
ext.prebid.data.bidders, ext.prebid.bidderconfig | Which bidders get first-party data, and bidder-specific ortb2 |
ext.prebid.biddercontrols.{bidder}.prefmtype | The one media type to send a bidder that cannot take multi-format imps |
{
"id": "0d9e3c51-6a7f-4b28-9c40-81e2f5a6b7c3",
"tmax": 1000,
"cur": ["USD"],
"imp": [
{
"id": "1",
"banner": { "format": [{ "w": 300, "h": 250 }] },
"ext": {
"prebid": {
"bidder": {
"bidderA": { "placementId": 88213 },
"bidderB": { "siteId": "4410", "zoneId": "12" }
}
}
}
}
],
"site": {
"page": "https://trailguide.example/gear/winter-boots",
"publisher": { "id": "pub-208" }
},
"ext": {
"prebid": {
"targeting": {
"pricegranularity": {
"precision": 2,
"ranges": [{ "max": 20, "increment": 0.1 }]
},
"includewinners": true,
"includebidderkeys": true
},
"cache": { "bids": { "returnCreative": true } },
"debug": true
}
}
}Bidder codes and parameter names here are illustrative. Each adapter documents its own. The rest of the request is ordinary OpenRTB, and the auction documentation says Prebid Server passes OpenRTB 2.x fields through to adapters. Two exceptions: it returns HTTP 400 for a request with wseat or bseat, and it treats only the first entry in cur as the ad server currency. The buyer blocking guide explains the seat rule.
Bidder parameters
imp.ext.prebid.bidder is an object keyed by bidder code or alias. The older form, imp.ext.{bidder}, still works: when imp.ext.prebid.bidder is absent, Prebid Server (Go) promotes those keys into it. Reserved imp.ext keys such as gpid, data, and tid are not treated as bidders.
Prebid Server (Go) validates each bidder's parameters against a JSON schema that ships with the server, and a failure is a request error naming the imp and the bidder. An unknown code is an error that asks whether an alias is missing from ext.prebid.aliases. Every imp must name at least one bidder, unless a stored request or stored response supplies them. Parameters in ext.prebid.bidderparams are merged into each imp for that bidder, and the imp-level values win a conflict.
Targeting and cache
ext.prebid.targeting asks the server to return ad server key-values on each bid in seatbid[].bid[].ext.prebid.targeting. The documented options are pricegranularity, mediatypepricegranularity, includewinners (default true), includebidderkeys (default true), includeformat, preferdeals, and alwaysincludedeals (each default false). The winning bid gets hb_pb, hb_bidder, and hb_size, plus hb_deal on a deal and hb_format when requested. Bidder keys add the bidder code as a suffix, such as hb_pb_bidderA. hb_pb is the price rounded down to the granularity, and keys longer than 20 characters are truncated. The Prebid.js Prebid Server adapter sends includewinners true and includebidderkeys false unless configured otherwise.
ext.prebid.cache stores whole bids through bids, or VAST XML through vastxml, in Prebid Cache. Each takes an optional ttlseconds and returnCreative (default true). The documentation says caching has no effect unless targeting is also requested. A cached bid carries ext.prebid.cache.bids with a url and cacheId, and its targeting gains hb_cache_id, hb_cache_host, and hb_cache_path. Cached VAST gets hb_uuid. RTBlint's Prebid Server profile flags a cache object with neither bids nor vastxml.
Stored requests and stored responses
{
"id": "0d9e3c51-6a7f-4b28-9c40-81e2f5a6b7c3",
"imp": [
{
"id": "1",
"ext": { "prebid": { "storedrequest": { "id": "trailguide-article-mid" } } }
}
],
"site": { "page": "https://trailguide.example/gear/winter-boots" },
"ext": { "prebid": { "storedrequest": { "id": "trailguide-web" } } }
}A stored request lets a short HTTP request pull in configuration kept on the server. ext.prebid.storedrequest.id names a stored request merged into the top level, and imp[].ext.prebid.storedrequest.id names a stored imp merged into one imp. Prebid Server (Go) applies the incoming request as a JSON Merge Patch over the stored data. Incoming values win, and an array in the incoming request, such as imp, replaces the stored array whole. That is why imps are stored and merged separately.
Stored responses are for testing and debugging. With storedauctionresponse on an imp, no adapter is called for that imp. With storedbidresponse, an array of bidder, id, and replaceimpid entries, the listed bidders answer from storage and every other bidder is called as usual.
Debug and test
ext.prebid.debug: true adds debug output to the response without test behavior, including ext.debug.httpcalls with each bidder's request and response. test set to 1 tells bidders the auction may not be real or billable, and only then does the response carry ext.debug.resolvedrequest, the request after stored data was merged. ext.prebid.trace takes verbose or basic. Per-bidder errors and response times come back in ext.errors and ext.responsetimemillis.
What each bidder receives
{
"id": "0d9e3c51-6a7f-4b28-9c40-81e2f5a6b7c3",
"cur": ["USD"],
"imp": [
{
"id": "1",
"banner": { "format": [{ "w": 300, "h": 250 }] },
"ext": {
"bidder": { "placementId": 88213 }
}
}
],
"site": {
"page": "https://trailguide.example/gear/winter-boots",
"publisher": { "id": "pub-208" }
},
"ext": {
"prebid": { "debug": true }
}
}This is the request above as bidderA sees it, abridged. In Prebid Server (Go) the split works like this:
- Only imps that name the bidder are sent. In each copy, the bidder's entry moves to
imp.ext.bidder, other bidders' parameters are dropped, andimp.ext.prebidkeeps onlyis_rewarded_inventoryandoptions. Otherimp.extkeys stay. imp.ext.prebid.imp.{bidder}is merged into that bidder's copy and then removed.- Request-level
ext.prebidis rebuilt with a short list: the bidder's ownbidderparams, alias, andmultibidentry, plusalternatebiddercodes,channel,currency,debug,integration,sdk,server, andtargeting.includebrandcategory. The host adds theserverblock itself. Cache settings, stored request IDs, and the rest of the targeting options never reach an adapter. user.buyeruidholds that bidder's own user ID.ext.prebid.data.bidderslimits who receivessite.ext.data,app.ext.data, anduser.ext.data, andeidpermissionslimits which ID sources each bidder gets.- For a bidder that supports only OpenRTB 2.5, the documentation lists 2.6 fields moved to
ext(regs.gdpr,user.consent,user.eids,source.schain) and others removed, among themcattaxand the GPP fields. - Media types the bidder does not support are removed from the imp, as the multi-format guide describes.
What comes back
{
"id": "0d9e3c51-6a7f-4b28-9c40-81e2f5a6b7c3",
"cur": "USD",
"seatbid": [
{
"seat": "bidderA",
"bid": [
{
"id": "a7c1",
"impid": "1",
"price": 1.42,
"adm": "<div class=\"ad\">...</div>",
"adomain": ["northpeak-outfitters.com"],
"crid": "cr-5521",
"w": 300,
"h": 250,
"mtype": 1,
"ext": {
"prebid": {
"type": "banner",
"targeting": {
"hb_pb": "1.40",
"hb_bidder": "bidderA",
"hb_size": "300x250",
"hb_cache_id": "6e0b2f5c-1d3a-4c8e-9b7f-2a4d6c8e0f13",
"hb_cache_host": "pbs.example.com",
"hb_cache_path": "/cache",
"hb_pb_bidderA": "1.40",
"hb_bidder_bidderA": "bidderA",
"hb_size_bidderA": "300x250",
"hb_cache_id_bidderA": "6e0b2f5c-1d3a-4c8e-9b7f-2a4d6c8e0f13"
},
"cache": {
"bids": {
"url": "https://pbs.example.com/cache?uuid=6e0b2f5c-1d3a-4c8e-9b7f-2a4d6c8e0f13",
"cacheId": "6e0b2f5c-1d3a-4c8e-9b7f-2a4d6c8e0f13"
}
}
}
}
}
]
}
],
"ext": {
"responsetimemillis": { "bidderA": 112, "bidderB": 140 }
}
}In a Prebid Server response, SeatBid.seat is the bidder code. Each bid carries ext.prebid.type, the media type the adapter assigned, alongside any Bid.mtype the bidder sent. RTBlint's profile reports a type that contradicts mtype, or that names a media object the imp never had. The Prebid.js ortb2 guide covers how the browser builds the request before it gets here.
The Prebid Server profile page lists every check RTBlint runs for this contract. Open the tester with that profile selected to validate a request with its ext.prebid block.
Sources
Questions people ask
What does a bidder adapter see instead of imp.ext.prebid.bidder?
Its own parameters at imp.ext.bidder. Prebid Server splits the request per bidder, copies each imp that names the bidder, moves that bidder's entry from imp.ext.prebid.bidder to imp.ext.bidder, and drops the other bidders' parameters. Most of request.ext.prebid is removed as well.
Which side wins when a stored request and the HTTP request disagree?
The HTTP request. Prebid Server (Go) applies the incoming request as a JSON Merge Patch over the stored request, so incoming values win and arrays such as imp are replaced whole. Stored imps are merged one imp at a time, again with the incoming imp taking precedence.
When does the response include resolvedrequest?
Only when the request sets test to 1. Setting ext.prebid.debug to true returns debug output, including each bidder's HTTP call, without test mode. resolvedrequest, the request after stored data was merged in, needs test set to 1.