Frequency capping is an advertising practice that limits the number of ads from a given category that are shown to a user within a given time period. Frequency capping improves the end-user experience by keeping ad impressions fresh and interesting, and helps advertisers manage ad spend.
This proposal introduces how FLEDGE on Android can be used to implement frequency capping functionality in an accurate and privacy-preserving way.
FLEDGE implements frequency capping by combining two features: The on-device storage of counters for ad-specific events, and the ability to filter ads according to a predefined set of filter strategies. Frequency capping enables advertisers to indicate a counter threshold over a sum of histogram values for a given time period.
Counters are unique for each combination of device profile, adtech, and counter key. Each ad should contain a set of counter keys to use in case a view or impression for the ad is registered. For each key, FLEDGE stores a set of counters, and each counter tallies all ad-specific events that occur within a specific time interval. On-device counters are incremented when an impression or view occurs, and counter data will be persisted on device. The exact persistence time will be defined later.
The ad filtering logic in FLEDGE's ad selection workflow has access to counters, remarketing ads, and contextual ads, giving FLEDGE frequency capping the ability to work with all such types of ad requests.
Note: Ad filtering is only available in the Privacy Sandbox on Android. Chrome's FLEDGE implementation does not currently implement a mechanism for filtering contextually-targeted non-FLEDGE ads. This proposal covers buy-side support only. If there is demand, we will add sell-side support at a later date.
FLEDGE frequency capping supports a broad range of requirements, including:
- Real-time filtering, with minimal server-side delay when on-device counters are updated.
- Flexible hierarchy of keys, including individual ads, campaigns, or any other grouping.
- Congruence with other frequency capping methods, without dependency on AdID.
- Works across apps on a given device user profile.
- Accurate and complete counters.
- Support for custom definitions of ad events, such as views or impressions.
- One function for both remarketing and contextual ads.
To set up frequency capping, follow these steps:
Step 1: Add frequency capping information to ads
Contextual and remarketing ads will indicate what are the relevant histogram
counters to update in case of a view or impression using a new field
on_device_counters_keys
that will contain a list of arbitrary key values. The
field is not included in the metadata
field that is not parsed by FLEDGE.
The following example shows the data format for the adsData
field in
AdSelectionConfig
. For remarketing, the format of the list of ads for a given
custom audience is consistent with the content of the ads
field shown below:
'adsData': [
{
"buyer": "ads.example.com",
"ads": [
{
'render_url': 'exampleUrl',
'metadata': {...}, /* metadata are opaque to FLEDGE and
just required to be in valid JSON
format */
'on_device_counters_keys': [
'campaign_id:1234',
'campaign_id:1234+adgroup_id:5678'
]
}]
}]
}
Step 2: Register a view or impression
Adtechs can invoke the updateEventHistogram
method to register occurrences of
events that are used for frequency capping. A method can be invoked repeatedly
on the same event for keys specified in the winning ad's eventType
.
void updateEventHistogram(@EventType eventType, long adSelectionId)
Inputs:
eventType
: Identifies whether an event is counted as a view, an impression or the win of the ad selection process.adSelectionId
: ID values in theAdSelectionOutcome
object that are returned byselectAds
calls.
The updateEventHistogram
call updates the histogram for the set of keys
defined as part of either the remarketing ads fetched by a CustomAudience
or
the contextual ads included in the AdSelectionConfig
parameter for
selectAds
. In addition to the keys in on_device_counters_keys
, the call will
also update the histogram for a counter identified by the ad's render_url
value.
If we assume that the ad in Step 1 is the winner of an AdSelection
with an
id
value of 9999
, a call to updateEventHistogram(EventType.VIEW,
adSelectionId: 999)
increments the counters for the following three primary
keys:
{'ads.example.com', 'campaign_id:1234', VIEW}
{'ads.example.com', 'campaign_id:1234+adgroup_id:5678', VIEW}
{'ads.example.com', 'exampleUrl', VIEW}
.
The adtech name is taken from the buyer field, either from contextual ads or from custom audiences, depending on where the winning ads come from.
FLEDGE for Android will automatically increment all the counters mentioned above
for the event type EventType.AD_SELECTION_WIN
for ads returned by a
selectAds
API call. This is functionally equivalent to the addition of the
prev_wins
argument to browser_signals
in generateBid
in Chrome's FLEDGE
implementation.
Step 3: Implement frequency cap filtering with filters
For optimal performance, the frequency cap filtering function is executed within
AdServices
. FLEDGE understands if a message has to be filtered by reading the
filters field in the AdsData
object. A list of filters is specified in
frequency_cap
. The values for key, event_type
and interval_seconds
are
used to retrieve a histogram of events that are used for filtering and FLEDGE.
Filtering information can be specified for remarketing ads provided by a custom
audience and for contextual ads as part of the AdSelectionConfig
object.
For contextual ads with frequency cap filters, ads are passed in using the ads
field in the AdSelectionConfig
object. Ads are filtered, and the ad with the
highest bid is returned as the result of the selectAds
call.
For remarketing ads with frequency cap filters, ads are filtered before the
buyer-provided generateBid()
JavaScript function is invoked.
The following example shows a message with frequency cap filtering:
{
'render_url': 'url',
'metadata': {...}, /* metadata are opaque to FLEDGE and assumed
to be in valid JSON format */
'on_device_counters_keys': [
'campaign_id:1234',
'campaign_id:1234+adgroup_id:5678'
],
"filters": {
"frequency_cap": {
"view": {
"campaign_id:1234": {
"cap": 10,
"interval_seconds": 86400
},
"adgroup_id:5678": {
"cap": 10,
"interval_seconds": 86400
},
},
"win": {
"campaign_id:1234": {
"cap": 5,
"interval_seconds": 604800
},
"adgroup_id:5678": {
"cap": 5,
"interval_seconds": 345600
},
}
},
// This field is only required in contextual ads and is used in
// reportImpression calls to fetch the reportWin function.
'reportingJS': "https://ads.example.com?reportWin.js"
}
Step 4: Report on winning Ads
Once the ad selection process is complete, it returns an AdSelectionOutcome
object containing the renderUri
and adSelectionId
, a numeric identifier for
the selectAds
call. This ID can be used to invoke the reportImpression
API
that currently supports event-level reporting. In Beta 1, this method supports
reporting for remarketing ads, and will be extended to support reporting for
contextual ads in a later release. For contextual ads, the buyer is required to
indicate where the reportWin
function can be retrieved during a
reportImpression
call by using an extra field called reportingJS
in the ad
structure, as shown in the example above.
Best practices for selecting ad candidates
FLEDGE moves the enforcement of frequency capping from the server to the device. Although winning bids are reported with the Privacy Sandbox, developers won't know why an ad isn't shown. Ads might not be shown due to a lost bid, or due to frequency capping. With no full visibility into the reasons why certain ads don't win, bidding systems require additional work to ensure optimal ads are served. These best practices will help ensure optimal ad serving with FLEDGE.
Send enough remarketing ads
Remarketing ads cannot be optimized per user. If a user sees a significant
number of ads from a custom audience and the ad limits are low, all ads may be
filtered out. Remarketing ads are refreshed periodically, so enough ad inventory
should pass through frequency capping to ensure remarketing ads continue to be
served. This needs to be balanced with limitations on the size of the ads that
can be specified during the joinCustomAudience
call, and during the custom
audience background fetch process. Buyers must consider that there might be an
increase in latency during the bidding phase. To minimize the impact of these
issues, frequency cap filtering is performed before the call to generateBid
.
Keep contextual counters on the server
With server side estimation, a developer can have rough estimates for when frequency capping may be active. Those estimations can indicate that an ad has likely hit the frequency cap threshold, and should therefore be sent with more ad candidates or be eliminated completely.
Send multiple ad candidates on the contextual response
You should send multiple ad candidates with a contextual response before a FLEDGE auction. This ensures that if several ads are filtered out, other ads will still be shown. Ad candidates can be prioritized so that some ads are provided as backup.
Since execution is time-bound, ad candidates should be chosen according to their likelihood to win an auction and to not be filtered out.
Limitations
The following are known limitations of FLEDGE frequency capping:
- FLEDGE frequency capping operates at the device user profile level, currently with no shared counters on other devices and other profiles. Any increments of ads shown from other devices need to be incorporated manually, if desired.
- Device counters are stored and accessed on the device. Server-side counters need to be managed separately.
- As frequency capping and related ad filtering is processed on a device, adtech platforms don't have direct control over these operations. To clear the device's frequency capping threshold, adtech platforms can send multiple candidate ads.
- Bid adjustments based on recorded frequency are unsupported. The
generateBid
functions cannot view frequency counters.