How the Protected Audience API Runs On-Device Auctions
The Protected Audience API is a Privacy Sandbox initiative designed to serve targeted and remarketing advertisements without relying on cross-site tracking or third-party cookies. By shifting the ad auction and decision-making logic from external ad servers directly into the user’s browser, the API ensures that user browsing behavior remains private on the device. This article breaks down what the Protected Audience API is and details the step-by-step JavaScript execution flow required to run privacy-centric, on-device ad auctions.
What Is the Protected Audience API?
The Protected Audience API (formerly known as FLEDGE) allows advertisers and supply-side platforms (SSPs) to bid on and render interest-based ads locally. Instead of tracking users across different websites with identifiers, the browser stores interest groups and executes the auction logic in isolated JavaScript environments called worklets. User interest profiles and bid computations never leave the browser in a readable, cross-site identifiable format.
Step 1: Joining an Interest Group
The remarketing flow begins on the advertiser’s website. When a user performs an action (such as viewing a product), the advertiser or their demand-side platform (DSP) assigns the user to an interest group using JavaScript:
const interestGroup = {
owner: 'https://dsp.example',
name: 'running-shoes',
biddingLogicUrl: 'https://dsp.example/js/bidding_logic.js',
trustedBiddingSignalsUrl: 'https://dsp.example/signals',
trustedBiddingSignalsKeys: ['shoe_123'],
ads: [{
renderUrl: 'https://dsp.example/ads/running-shoe.html',
metadata: { category: 'sports' }
}]
};
await navigator.joinAdInterestGroup(interestGroup, 2592000); // 30 daysThe browser stores this configuration locally, including the URL for the JavaScript file that calculates bids and the URLs for rendering the ad creatives.
Step 2: Initiating the Auction
When the user visits a publisher site with ad inventory, the
publisher or their SSP initiates the on-device auction by calling
navigator.runAdAuction(). This requires an auction
configuration object:
const auctionConfig = {
seller: 'https://ssp.example',
decisionLogicUrl: 'https://ssp.example/js/decision_logic.js',
interestGroupBuyers: ['https://dsp.example'],
auctionSignals: { isSecure: true },
sellerSignals: { adSlotWidth: 300, adSlotHeight: 250 }
};
const adAuctionResult = await navigator.runAdAuction(auctionConfig);Step 3: Generating Bids in the Browser
Once the auction starts, the browser creates an isolated environment
for each participating interest group buyer. The browser loads the
buyer’s biddingLogicUrl and executes the
generateBid() function:
function generateBid(interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals, browserSignals) {
return {
bid: 2.50,
render: interestGroup.ads[0].renderUrl
};
}This function can utilize real-time data fetched automatically by the
browser from a trusted Key-Value service via the
trustedBiddingSignalsUrl.
Step 4: Scoring the Bids
After all participating buyers submit their bids, the browser runs
the seller’s decisionLogicUrl in a separate worklet. The
seller evaluates and ranks the bids using the scoreAd()
function:
function scoreAd(adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
// Return a numerical desirability score
return bid * 1.5;
}The ad with the highest non-zero desirability score wins the auction.
Step 5: Rendering the Winning Ad
The navigator.runAdAuction() promise resolves to an
opaque output, typically a FencedFrameConfig object or an
opaque URN. This result is passed to a <fencedframe>
or an <iframe>:
const frame = document.createElement('fencedframe');
frame.config = adAuctionResult;
document.body.appendChild(frame);By rendering inside a fenced frame, the publisher page cannot inspect the winning ad’s content or interest group, and the ad cannot access the publisher page’s context, preventing cross-site user re-identification while successfully serving relevant ads.