--- layout: page_v2 page_type: module title: Module - Price Floors description: Configure and enforce minimum bids. module_code : priceFloors display_name : Price Floors enable_download : true sidebarType : 1 --- # Price Floors Module {:.no_toc} * TOC {:toc} ## Overview The Price Floors Module provides an open source framework in Prebid for Publishers to configure Prebid price floors on their own or to work with a vendor who can provide floors. A ‘floor’ is defined as the lowest CPM price a bid will need to meet for each Prebid auction. It’s a way for publishers to signal to bidders the price to beat, thereby protecting the value of their inventory. The module provides several ways for Prebid floors to be defined, that are used by bidder adapters to read floors and enforced on bid responses in any supported currency. The floors utilized by the Price Floors Module are defined by one or more set of rules containing any or all of the following dimensions: * AdUnit * GPT Slot Name * MediaType * Ad Size * Domain * "custom dimensions" (Prebid.js only) {: .alert.alert-warning :} When using GPT Slot name, the GPT library is required to load first. Failing to do so may yield unexpected results and could impact revenue performance. The entire set of floors selected by the Price Floors Module for a given auction is called a "Rule Location". A Rule Location can be any one of: 1. Retrieved from a real-time data service (Dynamic) 2. Within setConfig (Package) 3. Within the AdUnit (AdUnit) {: .alert.alert-info :} Even though floors are defined with five pre-configured dimensions, it’s possible (in Prebid.js) to extend the list of dimensions to attributes of the page, user, auction or other data by supplying a dimension matching function. For example, a publisher can provide a matching function that returns the device type to allow the Floor module to use device type as an attribute within a prebid floor rules file. Note that Prebid Server also supports a [floors feature](/prebid-server/features/pbs-floors.html) that is very similar to the Prebid.js module. They both share Schema 2, and there are PBS-specific notes below. The expectation with the Prebid Server floors feature is that Publishers will use it mainly for mobile app and AMP scenarios. Web sites running Prebid.js will utilize this client-side module. ### Video Overview of Floors
Notes: * [Price Floors in Prebid Server](/prebid-server/features/pbs-floors.html#prebid-server--features--price-floors) * [Prebid Floor Service Providers](/dev-docs/modules/floors.html#floors-providers) * [Transcript of this video](/dev-docs/floors-video-overview.html) ### Simple Static Floor Signaling Some publishers just want to set a simple static floor and don't need enforcement. Please don't use this module for that. This module should only be used when you need to vary the floor by mediatype, size, etc. Here's how you can signal static floors on each Prebid adunit: ```javascript pbjs.addAdUnits({ code: "test-div", mediaTypes: { banner: { sizes: [[300,250]] } }, ortb2Imp: { bidfloor: 1.00, bidfloorcur: "EUR" }, // ... }); ``` ### Simple Static Floor Enforcement If you need a static floor signal and also want to enforce that static floor, here's the quick start: ```javascript var adUnits = [ { code: 'test-div', mediaTypes: { banner: { sizes: [[300,250],[300,600]] } }, floors: { currency: 'USD', schema: { delimiter: '|', fields: [ 'mediaType' ] }, values: { '*': 1.00 // enter your static floor here } }, bids: [ ... ] } ]; ``` {: .alert.alert-warning :} Prebid does not recommend setting static floors. They are blunt tools and you'll forget to update them. ## How it Works There are several places where the Floor module changes the behavior of the Prebid.js auction process. Below is a diagram describing the general flow of the client-side Price Floors Module:  1. When building the Prebid.js package, the Price Floors Module (and any analytics adapters) needs to be included with 'gulp build --modules=priceFloors,...' 2. As soon as the setConfig({floors}) call is initiated, the Price Floors Module will build an internal hash table for each auction derived from a Rule Location (one of Dynamic, setConfig or adUnit) * If an endpoint URL (a Dynamic Floor) is defined, the Price Floors Module will attempt to fetch floor data from the Floor Provider's endpoint. When requestBids is called, the Price Floors Module will delay the auction up to the supplied amount of time in floors.auctionDelay or as soon as the dynamic endpoint returns data, whichever is first. * If the `skipRate` flag is specified, there's an A/B test in progress, so the module will decide for this request whether floors processing should be 'skipped' or not. 3. Bid Adapters are responsible for utilizing the getFloor() from the bidRequest object for each ad slot media type, size combination. The Price Floors Module will perform currency conversion if the bid adapter requests floors in a different currency from the defined floor data currency. 4. Bid Adapters will pass the floor values to their bidding endpoints, to request bids, responding with any bids that meet or exceed the provided floor 5. Bid adapters will submit bids to back to Prebid core, where the Price Floors Module will perform enforcement on each bid 6. The Price Floors Module will mark all bids below the floor as bids rejected. Prebid core will submit all eligible bids to the publisher ad server * The Price Floors Module emits floor event / bid data to Analytics adapters to allow Floor Providers a feedback loop on floor performance for model training ## Defining Floors How price floors are used in the page will depend on how you’re obtaining the floors and how Prebid.js is integrated into the pages. For optimal revenue performance, we strongly recommend an automated flooring method compared to manual, sporadic updates. ### Floors Defined in the AdUnit In this approach, the Publisher configures the floors directly into the Prebid.js AdUnits. This method can be used on simple pages or as part of a content management system that dynamically creates AdUnits. Below are some basic principles of ad unit floor definitions: * Ad unit defined rules only apply to the ad unit they are created in * Setting a rule with a value that does not match the context of that given ad unit will never be used * If multiple ad units have configured floor objects, the first ad unit’s schema would apply to all subsequent ad unit floor definitions * Values can differ between ad units ```javascript var adUnits = [ { code: 'test-div', mediaTypes: { banner: { sizes: [[300,250],[300,600]] }, video: { context: 'outstream', playerSize: [300,250], ... } }, floors: { currency: 'USD', schema: { delimiter: '|', fields: [ 'mediaType', 'size' ] }, values: { 'banner|300x250': 1.10, 'banner|300x600': 1.35, 'video|300x250': 2.00 } }, bids: [ ... ] } ]; ``` {: .alert.alert-info :} When defining floors at the adUnit level, the Price Floors Module requires the floors object to be defined in setConfig, even if the definition is an empty object as shown below: ```javascriptpbjs.setConfig({ floors: {} });``` Floor definitions are set in the “values” object containing one or more rules, where the rule is the criteria that needs to be met for that given ad unit, with an associated CPM floor. In the above example, the floors are enforced when the bid from a bidder matches the “mediaType” and “size” combination. Since many bid adapters are not able to ingest floors per size, a simpler setup can be: ```javascript floors: { currency: 'USD', skipRate: 5, modelVersion: 'Sports Ad Unit Floors', schema: { fields: [ 'mediaType'] }, values: { 'banner': 0.8, 'video': 2.01 } } ``` For more advanced publisher setups, values can accept a “\*” to denote a catch-all when a bid comes back that the Price Floors Module does not have an exact match and for bid adapters who are not able to use a floor per size, the bid adapter will automatically receive the “\*” rule’s floor if available. Example setup can be: ```javascript floors: { currency: 'USD', skipRate: 5, modelVersion: 'Sports Ad Unit Floors', schema: { fields: [ 'mediaType', 'size'] }, values: { 'banner|300x250': 0.8, 'banner|300x600': 1.8, 'banner|728x90': 0.90, 'banner|*': 1.00, 'video|300x250': 2.01, 'video|*': 2.01 } } ``` Alternatively, if there’s only one mediaType in the AdUnit and a single global floor, the syntax gets easier: ```javascript ... floors: { default: 1.00 // default currency is USD }, ... ``` ### Package-Level Floors This approach is intended for scenarios where the Publisher or their Prebid managed service provider periodically appends updated floor data to the Prebid.js package. In this model, there could be more floor data present to cover AdUnits across many pages. ```javascript pbjs.setConfig({ floors: { enforcement: { floorDeals: false, //default to false bidAdjustment: true }, data: { // default if endpoint doesn't return in time currency: 'USD', skipRate: 5, modelVersion: 'some setconfig model version', schema: { fields: [ 'gptSlot', 'mediaType' ] }, values: { '/1111/homepage/top-rect|banner': 0.80, '/1111/homepage/top-rect|video': 2.20, '/1111/homepage/top-rect|*': 1.50, '/1111/homepage/left-nav|banner': 1.20, '/1111/homepage/left-nav|video': 2.20, '/1111/homepage/left-nav|*': 1.50, ... there could be hundreds of these ... '*|banner': 0.98, '*|video': 1.74 } } } }); ``` By defining floor data with setConfig, the Price Floors Module will map GPT ad slots to AdUnits as needed. It does this in the same way as the setTargetingForGPTAsync() function – first looking for an AdUnit.code that matches the slot name, then looking for an AdUnit.code that matches the div id of the named GPT slot. Here’s another example that includes more fields: ```javascript pbjs.setConfig({ floors: { data: { // default if endpoint doesn't return in time currency: 'USD', skipRate: 5, modelVersion: 'some setconfig model version', schema: { fields: [ 'domain', 'gptSlot', 'mediaType', 'size'] }, values: { 'www.examplepub.com|/1111/homepage/top-rect|banner|300x250': 0.80, 'www.examplepub.com|/1111/homepage/top-rect|video|300x250': 2.20, 'www.examplepub.com|/1111/homepage/left-nav|banner|300x250': 1.77, 'www.examplepub.com|/1111/homepage/left-nav|video|300x250': 2.88 ... } } } }); ``` ### Dynamic Floors The final method of obtaining floor data allows the publisher to delay the auction for a certain time period to obtain up-to-date floor data tailored to each page or auction context. The assumed workflow is: 1. The Publisher signs up with a floor data provider 2. The Publisher configures Prebid.js to resolve and enforce the price floor 3. The Floor provider makes a JSON file available with floor data Here’s an example defining a simple GET endpoint: ```javascript pbjs.setConfig({ floors: { enforcement: { floorDeals: true, //default to false }, auctionDelay: 100, // in milliseconds endpoint: { url: 'https://floorprovider.com/a1001-mysite.json' } } }); ``` The Price Floors Module is flexible to handle floors set in multiple locations. Like in the below example a publisher can configure Dynamic floors in addition to Package floors (in setConfig). While the Price Floors Module is only able to use one set of rules (either Package, adUnit or Dynamic) defined as a Floor Location, setting floors in the Package will be utilized when the Dynamic floors fail to return data or another error condition occurs with the Dynamic fetch. ```javascript pbjs.setConfig({ floors: { enforcement: { floorDeals: false, //default to false }, auctionDelay: 100, // in milliseconds endpoint: { url: 'https://floorprovider.com/a1001-mysite.json' }, data: { // default if endpoint doesn't return in time floorProvider: 'floorProviderName', currency: 'USD', skipRate: 5, modelVersion: ‘new model 1.0’ schema: { fields: [ 'mediaType' ] }, values: { 'banner': 0.80, 'video': 1.20 } } } }); ``` ## Floors Syntax The examples above covered several different scenarios where floors can be applied. Below we will cover the syntax and definition of the floors data schema. As of Prebid.js version 3.24, the Price Floors Module supports a second data schema with the ability to add new schemas to future-proof the needs of additional design changes while keeping backwards compatibility. ### Schema 1 Schema 1 restricts floors providers or publishers to applying only one data group. To test more than one floor group, floor providers or publishers are required to reset the data set with new rules after each request bids. {: .alert.alert-info :} Note: if you're a dynamic floor provider service, your response must be a subset that will be merged under the 'data' object. {: .alert.alert-warning :} You **cannot** set the `floorMin` parameter without specifying a `data` object. See the [simple static floor](/dev-docs/modules/floors.html#simple-static-floor-signaling) section above for more info. {: .table .table-bordered .table-striped } | Param | Type | Description | Default | |---+---+---+---+---| | floorMin | float | The mimimum CPM floor used by the Price Floors Module (as of 4.13). The Price Floors Module will take the greater of floorMin and the matched rule CPM when evaluating getFloor() and enforcing floors. **Note**: this is not a method of setting a [static floor](/dev-docs/modules/floors.html#simple-static-floor-signaling). | - | | floorProvider | string | Optional atribute (as of prebid version 4.1) used to signal to the Floor Provider's Analytics adapter their floors are being applied. They can opt to log only floors that are applied when they are the provider. If floorProvider is supplied in both the top level of the floors object and within the data object, the data object's configuration shall prevail.| - | | enforcement | object | Controls the enforcement behavior within the Price Floors Module.| - | | skipRate | integer | skipRate is a random function whose input value is any integer 0 through 100 to determine when to skip all floor logic, where 0 is always use floor data and 100 is always skip floor data. The use case is for publishers or floor providers to learn bid behavior when floors are applied or skipped. Analytics adapters will have access to model version (if defined) when skipped is true to signal the Price Floors Module is in floors mode. If skipRate is supplied in both the root level of the floors object and within the data object, the skipRate configuration within the data object shall prevail. | 0 | | enforcement.enforceJS | boolean | If set to true, the Price Floors Module will provide floors to bid adapters for bid request matched rules and suppress any bids not exceeding a matching floor. If set to false, the Price Floors Module will still provide floors for bid adapters, there will be no floor enforcement.| true | | enforcement.enforceBidders | array of strings | Controls which bidders should have JavaScript floor enforcement applied when `enforcement.enforceJS` is `true`. Use bidder codes or `"*"` for all bidders. | `["*"]` | | enforcement.enforcePBS | boolean | If set to true, the Price Floors Module will signal to Prebid Server to pass floors to it’s bid adapters and enforce floors. If set to false, the pbjs should still pass matched bid request floor data to PBS, however no enforcement will take place. | true | | enforcement.floorDeals | boolean | Enforce floors for deal bid requests. | false | | enforcement.bidAdjustment | boolean | If true, the Price Floors Module will use the bidAdjustment function to adjust the floor per bidder. If false (or no bidAdjustment function is provided), floors will not be adjusted. Note: Setting this parameter to false may have unexpected results, such as signaling a gross floor when expecting net or vice versa. | true | | endpoint | object | Prebid.js only: controls behavior for dynamically retrieving floors. | - | | endpoint.url | string | URL of endpoint to retrieve dynamic floor data. | - | | data | object (required) | Floor data used by the Price Floors Module to pass floor data to bidders and floor enforcement. | - | | data.floorProvider | string | Optional atribute (as of prebid version 4.2) used to signal to the Floor Provider's Analytics adapter their floors are being applied. They can opt to log only floors that are applied when they are the provider. If floorProvider is supplied in both the top level of the floors object and within the data object, the data object's configuration shall prevail.| - | | data.currency | string | Currency of floor data. Floor Module will convert currency where necessary. See Currency section for more details. | 'USD' | | data.skipRate | integer | skipRate is a random function whose input value is any integer 0 through 100 to determine when to skip all floor logic, where 0 is always use floor data and 100 is always skip floor data. The use case is for publishers or floor providers to learn bid behavior when floors are applied or skipped. Analytics adapters will have access to model version (if defined) when skipped is true to signal the Price Floors Module is in floors mode. If skipRate is supplied in both the root level of the floors object and within the data object, the skipRate configuration within the data object shall prevail. | 0 | | data.floorsSchemaVersion | integer | The module supports two versions of the data schema. Version 1 allows for only one model to be applied in a given data set, whereas Version 2 allows you to sample multiple models selected by supplied weights. If no schema version is provided, the module will assume version 1 for the sake of backwards compatibility. For schema version 2 see the next section. | 1 | | data.modelVersion | string | Used by floor providers to train on model version performance. The expectation is a floor provider’s analytics adapter will pass the model verson back for algorithm training. | - | | data.modelTimestamp | integer | Epoch timestamp associated with modelVersion. Can be used to track model creation of floor file for post auction analysis.| - | | data.schema | object |allows for flexible definition of how floor data is formatted. | - | | data.schema.delimiter | string | Character separating the floor keys. | '\|' | | data.schema.fields | array of strings | Supported values are: gptSlot, adUnitCode, mediaType, size, domain | - | | data.values | key / values | A series of attributes representing a hash of floor data in a format defined by the schema object. | - | | data.values."rule key" | string | Delimited field of attribute values that define a floor. | - | | data.values."rule floor value" | float | The floor value for this key. | - | | data.default | float | Floor used if no matching rules are found. | - | | additionalSchemaFields | object | Object contain the lookup function to map custom schema.fields | - | | additionalSchemaFields."custom key" | string | custom key name | - | | additionalSchemaFields."key map function" | function | Function used to lookup the value for that particular custom key | - | {: .alert.alert-info :} When you see 'skipped' in the floors data, it indicates the status of the `skipRate` A/B test for this request. This is just a mechanism to be able to tell if the floor rules are providing value. e.g `skipRate` will often start at a high value like 90%, which means "only apply floors 10% of the time". If skipRate is 90, you would expect to see "skipped: true" 9 times out 10. ### Schema 2 Schema 2 allows floors providers to A/B-test one or more floor groups, determined at auction time. The following principles apply to Schema 2: * These attributes are required: * data.floorsSchemaVersion to be set to 2 * A valid modelGroups object must be set * The field modelGroups.modelWeight is required for each model group * If one of the model weights is missing, no schema 2 floor will be set and the Price Floors Module will look in other locations for floor definitions * If common attributes are set in both the modelGroups and root level of the data object, modelGroups attributes prevail * The Schema 2 data model can only be applied in Package level (i.e. directly in setConfig) or Dynamic level * Sampling weights are applied at the auction level. Each new auction the dice will be rolled. * If the data.modelGroups object and the data.values (schema 1 field) are set, the data.floorsSchemaVersion will dictate what schema version is applied While some attributes are common in both schema versions, for completeness, all valid schema 2 attributes are provided: {: .alert.alert-info :} Note: if you're a dynamic floor provider service, your response must be a subset that will be merged under the 'data' object. {: .alert.alert-warning :} You **cannot** set the `floorMin` parameter without specifying a `data` object. See the [simple static floor](/dev-docs/modules/floors.html#simple-static-floor-signaling) section above for more info. {: .table .table-bordered .table-striped } | Param | Type | Description | Default | |---+---+---+---+---| | floorMin | float | The mimimum CPM floor used by the module (as of 4.13). The module will take the greater of floorMin and the matched rule CPM when evaluating getFloor() and enforcing floors. **Note**: this is not a method of setting a [static floor](/dev-docs/modules/floors.html#simple-static-floor-signaling). | - | | floorMinCur | float | Prebid Server only: the currency used for the floorMin value. | - | | floorProvider | string | Optional atribute (as of prebid version 4.1) used to signal to the Floor Provider's Analytics adapter their floors are being applied. They can opt to log only floors that are applied when they are the provider. If floorProvider is supplied in both the top level of the floors object and within the data object, the data object's configuration shall prevail.| - | | skipRate | integer | skipRate is a random function whose input value is any integer 0 through 100 to determine when to skip all floor logic, where 0 is always use floor data and 100 is always skip floor data. The use case is for publishers or floor providers to learn bid behavior when floors are applied or skipped. Analytics adapters will have access to model version (if defined) when skipped is true to signal the module is in floors mode. If skipRate is supplied in both the root level of the floors object and within the data object, the skipRate configuration within the data object shall prevail. | 0 | | enabled | boolean | Prebid Server only: provides a request level override to disable server-side floor activity. If there's server-side floor config, it must also be true in order for floor activity to take place. | true | | fetchStatus | string | Prebid Server only: this is a read-only field set by the Prebid-Server floors feature to let analytics adapters know whether the floors data used was dynamically fetched. | n/a | | skipped | boolean | Prebid Server only: this is a read-only field set by the Prebid-Server floors feature to let analytics adapters know whether the 'skipRate' feature was invoked. i.e. this value is 'true' when the floor activity has been turned off by the skipRate, and 'false' otherwise. | n/a | | location | string | Prebid Server only: this is a read-only field set by the Prebid-Server floors feature to let analytics adapters know where the floors data came from. Possible values are: 'request', 'fetch' or 'noData'. | n/a | | enforcement | object | Controls the enforcement behavior within the module.| - | | enforcement.enforceJS | boolean | If set to true, the module will provide floors to bid adapters for bid request matched rules and suppress any bids not exceeding a matching floor. If set to false, the module will still provide floors for bid adapters, but there will be no floor enforcement.| true | | enforcement.enforceBidders | array of strings | Controls which bidders should have JavaScript floor enforcement applied when `enforcement.enforceJS` is `true`. Use bidder codes or `"*"` for all bidders. | `["*"]` | | enforcement.enforcePBS | boolean | If set to true, the module will signal to Prebid Server to pass floors to it’s bid adapters and enforce floors. If set to false, Prebid.js should still pass matched bid request floor data to Prebid Server, however no enforcement will take place. | true | | enforcement.floorDeals | boolean | Enforce floors for deal bid requests. | false | | enforcement.bidAdjustment | boolean | If true, the module will use the bidAdjustment function to adjust the floor per bidder. If false (or no bidAdjustment function is provided), floors will not be adjusted. Note: Setting this parameter to false may have unexpected results, such as signaling a gross floor when expecting net or vice versa. | true | | enforcement.enforceRate | integer | Prebid Server only: Defines a percentage for how often bid response enforcement activity should take place given that the floors feature is active. If the floors feature is skipped due to skipRate, this has no affect. For every non-skipped auction, this percent of them should be enforced: i.e. bids discarded. This feature lets publishers ease into enforcement in case bidders aren't adhering to floor rules. | 100 | | enforcement.noFloorSignalBidders | array of strings | (PBJS 8.31+, PBS-Java 3.4+) This is an array of bidders for which to avoid sending floors. This is useful for bidders where the publishers has established different floor rules in their systems. The value can be `["*"]`. | - | | endpoint | object | Controls behavior for dynamically retrieving floors. | - | | endpoint.url | string | URL of endpoint to retrieve dynamic floor data. | - | | data | object (required) | Floor data used by the module to pass floor data to bidders and floor enforcement. | - | | data.floorProvider | string | Optional atribute (as of prebid version 4.2) used to signal to the Floor Provider's Analytics adapter their floors are being applied. They can opt to log only floors that are applied when they are the provider. If floorProvider is supplied in both the top level of the floors object and within the data object, the data object's configuration shall prevail.| - | | data.currency | string | Currency of floor data. The module will convert currency where necessary. See Currency section for more details. | 'USD' | | data.skipRate | integer | skipRate is a random function whose input value is any integer 0 through 100 to determine when to skip all floor logic, where 0 is always use floor data and 100 is always skip floor data. The use case is for publishers or floor providers to learn bid behavior when floors are applied or skipped. Analytics adapters will have access to model version (if defined) when skipped is true to signal the module is in floors mode. If skipRate is supplied in both the root level of the floors object and within the data object, the skipRate configuration within the data object shall prevail.| 0 | | data.useFetchDataRate | integer | (PBS-Go 2.6+) useFetchDataRate is a random function whose input value is any integer 0 through 100 to determine when to skip dynamic floor data and fall back to static floor data. 0 means always use static floor data and 100 means always use dynamic floor data. The use case is for publishers or floor providers to confirm how dynamic floors data compares to static floors data. Analytics adapters will have access to the location of the actual floors data used, either "request" or "fetch". | 100 | | data.floorsSchemaVersion | integer | The module supports two versions of the data schema. Version 1 allows for only one model to be applied in a given data set, whereas Version 2 allows you to sample multiple models selected by supplied weights. If no schema version is provided, the module will assume version 1 for the sake of backwards compatibility.| 1 | | data.modelTimestamp | int | Epoch timestamp associated with modelVersion. Can be used to track model creation of floor file for post auction analysis.| - | | data.noFloorSignalBidders | array of strings | (PBJS 8.31+, PBS-Java 3.4+) This is an array of bidders for which to avoid sending floors. This is useful for bidders where the publishers has established different floor rules in their systems. The value can be `["*"]`. | - | | data.modelGroups | array of objects | Array of model objects to be used for A/B sampling multiple models. This field is only used when data.floorsSchemaVersion = 2 | - | | data.modelGroups[].currency | string | Currency of floor data. Floor Module will convert currency where necessary. See Currency section for more details. | 'USD' | | data.modelGroups[].skipRate | integer | skipRate is a random function whose input value is any integer 0 through 100 to determine when to skip all floor logic, where 0 is always use floor data and 100 is always skip floor data. The use case is for publishers or floor providers to learn bid behavior when floors are applied or skipped. Analytics adapters will have access to model version (if defined) when skipped is true to signal the module is in floors mode. | 0 | | data.modelGroups[].modelVersion | string | Used by floor providers to train on model version performance. The expectation is a floor provider’s analytics adapter will pass the model verson back for algorithm training. | - | | data.modelGroups[].modelWeight | integer | Used by the module to determine when to apply the specific model. All weights will be normalized and applied at runtime. Futher clarification will be provided in examples below. | - | | data.modelGroups[].schema | object | Allows for flexible definition of how floor data is formatted. | - | | data.modelGroups[].schema.delimiter | string | Character separating the floor keys. | '\|' | | data.modelGroups[].schema.fields | array of strings | Supported pre-defined values are: gptSlot, adUnitCode, mediaType, size | - | | data.modelGroups[].values | key / values | A series of attributes representing a hash of floor data in a format defined by the schema object. | - | | data.modelGroups[].values."rule key" | string | Delimited field of attribute values that define a floor. | - | | data.modelGroups[].values."rule floor value" | float | The floor value for this key. | - | | data.modelGroups[].default | float | Floor used if no matching rules are found. | - | | data.modelGroups[].noFloorSignalBidders | array of strings | (PBJS 8.31+, PBS-Java 3.4+) This is an array of bidders for which to avoid sending floors. This is useful for bidders where the publishers has established different floor rules in their systems. The value can be `["*"]`. | - | | additionalSchemaFields | object | Object contain the lookup function to map custom schema.fields. Not supported by Prebid Server. | - | | additionalSchemaFields."custom key" | string | custom key name | - | | additionalSchemaFields."key map function" | function | Function used to lookup the value for that particular custom key | - | {: .alert.alert-info :} As noted for Schema 1, when you see 'skipped' in the floors data, it indicates the status of the `skipRate` A/B test for this request. This is just a mechanism to be able to tell if the floor rules are providing value. e.g `skipRate` will often start at a high value like 90%, which means "only apply floors 10% of the time". If skipRate is 90, you would expect to see "skipped: true" 9 times out 10. *Example 1* Model weights add up to 100 and are sampled at a 25%, 25%, 50% distribution. Additionally, each model group has diffirent schema fields: ```javascript pbjs.setConfig({ floors: { enforcement: { ... }, ... data: { "currency": "EU", "skipRate": 20, "floorsSchemaVersion":2, "modelGroups": [ { "modelWeight":25, "modelVersion": "Model1", "schema": { "fields": [ "domain", "gptSlot", "mediaType", "size" ] }, "values": { "www.publisher.com|/1111/homepage/top-banner|banner|728x90": 1.00, "www.publisher.com|/1111/homepage/top-rect|banner|300x250": 1.20, "www.publisher.com|/1111/homepage/top-rect|banner|300x600": 1.80, ... "www.domain.com|/1111/homepage/top-banner|banner|728x90": 2.11 ... "www.publisher.com|*|*|*": 0.80, }, "default": 0.75 }, { "modelWeight": 25, "modelVersion": "Model2", "schema": { "fields": [ "domain", "mediaType", "size" ] }, "values": { "www.publisher.com|banner|728x90": 1.00, "www.publisher.com|banner|300x250": 1.20, "www.publisher.com|banner|300x600": 1.80, ... "www.domain.com|banner|728x90": 2.11 ... "www.publisher.com|*|*|*": 0.80, }, "default": 0.75 }, { "modelWeight": 50, "modelVersion": "Model3", "schema": { "fields": [ "gptSlot", "mediaType", "size" ] }, "values": { "/1111/homepage/top-banner|banner|728x90": 1.00, "/1111/homepage/top-rect|banner|300x250": 1.20, "/1111/homepage/top-rect|banner|300x600": 1.80, ... "/1111/homepage/top-banner|banner|728x90": 2.11 ... "*|banner|*": 0.80, }, "default": 0.75 } ] } } }); ``` *Example 2* Model weights do not equal 100 and are normalized. Weights will be applied in the following method: Model weight / (sum of all weights) model1 = 20 -> 20 / (20 + 50) = 29% of auctions model 1 will be applied model2 = 50 -> 50 / (20 + 50) = 71% of auctions model 2 will be applied Additionally skipRate is supplied at model group level where model1 will skip floors 20% of times when model1 is selected, whereas model2 will skip 50% of auctions when model2 is selected. ```javascript pbjs.setConfig({ floors: { enforcement: { ... }, ... data: { "currency": "EU", "floorsSchemaVersion":2, "modelGroups": [ { "modelWeight":25, "skipRate": 20, "modelVersion": "Model1", "schema": { "fields": [ "domain", "gptSlot", "mediaType", "size" ] }, "values": { "www.publisher.com|/1111/homepage/top-banner|banner|728x90": 1.00, "www.publisher.com|/1111/homepage/top-rect|banner|300x250": 1.20, "www.publisher.com|/1111/homepage/top-rect|banner|300x600": 1.80, ... "www.domain.com|/1111/homepage/top-banner|banner|728x90": 2.11 ... "www.publisher.com|*|*|*": 0.80, }, "default": 0.75 }, { "modelWeight": 50, "skipRate": 50, "modelVersion": "Model2", "schema": { "fields": [ "gptSlot", "mediaType", "size" ] }, "values": { "/1111/homepage/top-banner|banner|728x90": 1.00, "/1111/homepage/top-rect|banner|300x250": 1.20, "/1111/homepage/top-rect|banner|300x600": 1.80, ... "/1111/homepage/top-banner|banner|728x90": 2.11 ... "*|banner|*": 0.80, }, "default": 0.75 } ] } } }); ``` ### Impression-Level Floor Min An extension supported on Prebid Server only is the ability to define impression (adunit) level minimums for the floor. This allows publishers to put fences around the results from a dynamic floors provider. The Prebid Server OpenRTB fields are imp.ext.prebid.floors.floorMin and floorMinCur. It's expected that these values will be placed in the App or AMP stored request and not supplied on a web page. (Though they could be supplied as AdUnit.ortb2Imp). ## Custom Schema Fields {: .alert.alert-info :} Only supported in Prebid.js, not Prebid Server Out of the box, the Price Floors Module only supports looking up floors by AdUnit, GPT Slot, MediaType, ad size, and domain. Custom schema fields can be added to support other lookup dimensions. Here are the steps: 1. Create a lookup function to give context of the value of custom fields for that given auction 1. Define, Set and Map Custom Schema Attributes ### Create Lookup Function Create a function to allow the module to understand context of a given auction. In the below example, a lookup function provides details about what deviceType this auction is for. e.g. ```javascript function deviceTypes (bidRequest, bidResponse) { //while bidRequest and bidResponse are not required for this function, they are available for custom attribute mapping let deviceType = getDeviceTypeFromUserAgent(navigator.userAgent); if(deviceType === 'mobile') return 'mobile' else if (deviceType === 'tablet') return 'tablet' else if (deviceType === 'desktop') return 'desktop' } ``` ### Define, Set and Map Custom Schema Attributes After defining a lookup function for the given context of the auction, the custom schema field(s) need to be defined in the `floors.schema.fields` array. Once your custom field is defined you can assign rule values in `floors.data.values` derived from these field(s). The last step would be to supply the lookup function(s) that map from each custom field to a value of the context wthin that auction by using the `floors.additionalSchemaFields` attribute as seen below. In the below example, `deviceType` is a custom field not currently supported by default in the Price Floors Module whose values are one of "mobile", "desktop" or "tablet". ```javascript pbjs.setConfig({ floors: { enforcement: { floorDeals: false //default to false }, floorMin: 0.05, data: { floorProvider : 'rubicon', skipRate : 0, currency: 'USD', schema: { fields: ['mediaType', 'deviceType'] }, modelVersion : 'testAddtionalFields', values : { 'banner|mobile' : 0.10, 'banner|desktop' : .15, 'banner|tablet' : 0.16, 'banner|*' : 0.16, '*|*' : 0.03 } }, additionalSchemaFields : { deviceType : deviceTypes // where deviceTypes is the function reference for your lookup function } } }); ``` ## Rule Handling ### Rule Location Priority As defined in the overview, a Rule Location is where a particular rule is located, either defined in (1) the Ad Unit, (2) within setConfig or (3) via a fetch from the browser. It's likely that floor rules are set in one or more location for a given Prebid auction. During an auction, the Price Floors Module will only ever use rules from one Rule Location, decided at run-time. Each auction will be assigned an immutable set of rules from one Rule Location, even if the rules change prior to auction complete. The module uses the below prioritization scheme on determining which Rule Location is selected at run-time: * dynamic * setConfig * adUnit ### Rule Selection Process The job of the Price Floors Module is to select a matching floor rule for enforcement given the context of each Ad Unit. With the usage of “\*” values in rules definitions multiple floor rules can match for a given ad unit auction. The module algorithm will produce a list of every possible permutation for each ad unit auction based on the defined schema types. The best matching rule for each enforced bid request and call to `getFloor()` is based on specificity of values \(meaning match an exact value\) weighted from left to right, where the specificity of a value in the left most column would match over a rule with its “\*” equivalent if “\*” is supplied. Priority order behavior where “\_” is a specific value, and the “\*” is a catch-all Priority order for one column rule sets: ```text _ * ``` Priority order for two column rule set: ```text _ | _ _ | * * | _ * | * ``` Priority order for three column rule sets: ```text _ | _ | _ _ | _ | * _ | * | _ * | _ | _ _ | * | * * | _ | * * | * | _ * | * | * ``` Priority order for four column rule sets: ```text _ | _ | _ | _ _ | _ | _ | * _ | _ | * | _ _ | * | _ | _ * | _ | _ | _ _ | _ | * | * _ | * | _ | * _ | * | * | _ * | _ | _ | * * | _ | * | _ * | * | _ | _ _ | * | * | * * | _ | * | * * | * | _ | * * | * | * | _ * | * | * | * ``` Below are some real example behaviors. #### Example 1 Domain =
| Your Magnite account team | Magnite data-science applied to dynamic floors. (Currently only available to Demand Manager customers) |
|
| Reach out to OpenX at [apollo@openx.com](mailto:apollo@openx.com) | Dynamic floor optimization and more |
|
| [header-bidding@pubmatic.com](mailto:header-bidding@pubmatic.com) | PubMatic's ML powered dynamic Floor Optimization |
|
| [assertiveyield.com](https://assertiveyield.com) | Holistic flooring covering Prebid, Amazon, GAM UPR, RTB and more |
|
| [hello@pubx.ai](mailto:hello@pubx.ai) | pubX is an ML-led, dynamic flooring solution covering the full programmatic revenue spectrum (the header + GAM). pubX integrates with your current set-up, so no need to replace any part of the current stack. Our team is solely focussed on delivering the best flooring technology to publishers. |
|
| [hello@mile.tech](mailto:hello@mile.tech) | Boost your Prebid stack with Mile’s AI-powered dynamic flooring module that delivers an average revenue uplift of 22%. Sign-up for a free trial. |
## Further Reading
* [Prebid Server Price Floors](/prebid-server/features/pbs-floors.html)