Generate aggregate data reports using data from Protected Audience and cross-site data from Shared Storage.
To provide critical features that the web relies on, the Private Aggregation API has been built for aggregating and reporting on cross-site data in a privacy-preserving manner.
Implementation status
Proposal | Status |
---|---|
Prevent invalid Private Aggregation API reports with report verification for Shared Storage Explainer |
Available in Chrome |
Private Aggregation debug mode availability dependent on 3PC eligibility GitHub issue |
Available in Chrome M119 |
Reducing report delay Explainer |
Available in Chrome M119 |
Private Aggregation contribution timeout for Shared Storage Explainer |
Available in M119 |
Support for Private Aggregation API and Aggregation Service for Google Cloud Explainer |
Available in Chrome M121 |
Padding for aggregatable report payloads Explainer |
Available in Chrome M119 |
Private Aggregation debug mode available for auctionReportBuyers reporting Explainer |
Available in Chrome M123 |
Filtering ID support Explainer |
Available in Chrome M128 |
Client-side contribution merging Explainer |
Available in Chrome M129 |
What is the Private Aggregation API
The Private Aggregation API allows developers to generate aggregate data reports with data from the Protected Audience API and cross-site data from Shared Storage.
The main function of this API is known as contributeToHistogram()
. The histogram operation lets you aggregate data
across users in each bucket (known in the API as an aggregation key) you define.
Your histogram call accumulates values and returns a noised aggregated result in
the form of a summary report. For example, the report might show the number of
sites each user has seen your content on, or come across a bug in your third-party script. This operation is performed within another API's worklet.
For example, if you have previously recorded demographic and geographic data in Shared Storage, you can use the Private Aggregation API to construct a histogram that tells you approximately how many users in New York City have seen your content cross-site. To aggregate for this measurement, you can encode the geography dimension into the aggregation key and count the users in the aggregatable value.
Key concepts
When you call the Private Aggregation API with an aggregation key and an aggregatable value, the browser generates an aggregatable report.
Aggregatable reports are sent to your server for collection and batching. The batched reports are processed later by the Aggregation Service, and a summary report is generated.
See the Private Aggregation API fundamentals document to learn more about the key concepts involved with the Private Aggregation API.
Differences from Attribution Reporting
The Private Aggregation API shares many similarities with the Attribution Reporting API. Attribution Reporting is a standalone API designed to measure conversions, whereas Private Aggregation is built for cross-site measurements in conjunction with APIs such as the Protected Audience API and Shared Storage. Both APIs produce aggregatable reports that are consumed by the Aggregation Service backend to generate summary reports.
Attribution Reporting associates data gathered from an impression event and a conversion event, which happen at different times. Private Aggregation measures a single, cross-site event.
Test this API
To test the Private Aggregation API locally, enable all the Ad privacy APIs under chrome://settings/adPrivacy
.
Read more about testing in experiment and participate.
Use the demo
The demo of Private Aggregation API for Shared Storage can be accessed at goo.gle/shared-storage-demo, and the code is available on GitHub. The demo implements the client-side operations and produces an aggregatable report that is sent to your server.
A demo of Private Aggregation API for the Protected Audience API will be published in the future.
Use cases
Private Aggregation is a general-purpose API for cross-site measurement, and it's available to be used in Shared Storage and Protected Audience API worklets. The first step is to decide specifically what information you want to collect. Those data points are the basis of your aggregation keys.
With Shared storage
Shared Storage lets you to read and write cross-site data in a secure environment to prevent leakage, and the Private Aggregation API lets you measure cross-site data stored in Shared Storage.
Unique reach measurement
You may want to measure how many unique users have seen their content. Private Aggregation API can provide an answer such as "Approximately 317 unique users have seen the Content ID 861."
You can set a flag in Shared Storage to signify whether the user has already seen the content or not. On the first visit where the flag does not exist, a call to Private Aggregation is made and then the flag is set. On subsequent visits by the user, including cross-site visits, you can check Shared Storage and skip submitting a report to Private Aggregation if the flag is set. To learn more about methods to implement these measurements, check out our reach whitepaper.
Demographics measurement
You may want to measure the demographics of the users who have seen your content across different sites.
Private Aggregation can provide an answer, such as "Approximately 317 unique users are from the age of 18-45 and are from Germany." Use Shared Storage to access demographics data from a third-party context. At a later point in time, you can generate a report with Private Aggregation by encoding the age group and country dimensions in the aggregation key.
K+ frequency measurement
You may want to measure the number of users who have seen a piece of content or an ad at least K times on a given browser, for a pre-chosen value of K.
Private Aggregation can provide an answer such as "Approximately 89 users have seen the Content ID 581 at least 3 times." A counter can be incremented in Shared Storage from different sites and can be read within a worklet. When the count has reached K, a report can be submitted using Private Aggregation.
Multi-touch attribution
This guidance is to be published on the dev site so that ad techs can understand how to implement MTA within Shared Storage + Private Aggregation.
With the Protected Audience API
The Protected Audience API enables retargeting and custom audience use cases, and Private Aggregation lets you report events from buyer and seller worklets. The API can be used for tasks such as measuring the distribution of auction bids.
From a Protected Audience API worklet, you can aggregate your data directly using contributeToHistogram()
and report your data based on a trigger using contributeToHistogramOnEvent()
, which is a special extension for the Protected Audience API.
Available functions
The following functions are available in the privateAggregation
object available in Shared Storage and Protected Audience API worklets.
contributeToHistogram()
You can call privateAggregation.contributeToHistogram({ bucket: <bucket>, value: <value> })
, where the aggregation key is bucket
and the aggregatable value as value
. For the bucket
parameter, a BigInt
is required. For the value
parameter, an integer Number is required.
Here is an example of how it may be called in Shared Storage for reach measurement:
iframe.js
// Cross-site iframe code
async function measureReach() {
// Register worklet
await window.sharedStorage.worklet.addModule('worklet.js');
// Run reach measurement operation
await window.sharedStorage.run('reach-measurement', {
data: { contentId: '1234' }
});
}
measureReach();
worklet.js
// Shared storage worklet code
function convertContentIdToBucket(campaignId){
// Generate aggregation key
}
// The scale factor is multiplied by the aggregatable value to
// maximize the signal-to-noise ratio. See "Noise and scaling"
// section in the Aggregation Fundamentals document to learn more.
const SCALE_FACTOR = 65536;
class ReachMeasurementOperation {
async run(data) {
const key = 'has-reported-content';
// Read the flag from Shared Storage
const hasReportedContent = await sharedStorage.get(key) === 'true';
// Don't send report if the flag is set
if (hasReportedContent) {
return;
}
// Send histogram report
// Set the aggregation key in `bucket`
// Bucket examples: 54153254n or BigInt(54153254)
// Set the scaled aggregatable value in `value`
privateAggregation.contributeToHistogram({
bucket: convertContentIdToBucket(data.contentId),
value: 1 * SCALE_FACTOR
});
// Set the flag in Shared Storage
await sharedStorage.set(key, true);
}
}
register('reach-measurement', ReachMeasurementOperation);
The previous code example will call Private Aggregation whenever the cross-site iframe content is loaded. The iframe code loads the worklet, and the worklet calls the Private Aggregation API with the content ID converted to an aggregation key (bucket).
contributeToHistogramOnEvent()
Within Protected Audience API worklets only, we provide a trigger-based mechanism for sending a report only if a certain event occurs. This function also allows for the bucket and value to depend on signals that are not yet available at that point in the auction.
The privateAggregation.contributeToHistogramOnEvent(eventType, contribution)
method takes an eventType
that specifies the triggering event, and the contribution
to be submitted when the event is triggered. The triggering event can come from the auction itself after the auction ends, such as an auction win or loss event, or it can come from a fenced frame that rendered the ad.
To send a report for auction events, you can use two reserved keywords, reserved.win
, reserved.loss
, and reserved.always
. To submit a report triggered by an event from a fenced frame, define a custom event type. To trigger the event from a fenced frame, use the fence.reportEvent()
method available from the Fenced Frames Ads Reporting API.
The following example sends an impression report when the auction win event is triggered, and sends a click report if a click
event is triggered from the fenced frame that rendered the ad. These two values can be used to calculate the clickthrough rate.
function generateBid(interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals, browserSignals) {
// …
privateAggregation.contributeToHistogramOnEvent("reserved.win", {
bucket: getImpressionReportBucket(),
value: 1
});
privateAggregation.contributeToHistogramOnEvent("click", {
bucket: getClickReportBuckets(), // 128-bit integer as BigInt
value: 1
});
See the Extended Private Aggregation Reporting explainer to learn more.
enableDebugMode()
While third-party cookies are still available, we'll provide a temporary mechanism that allows easier debugging and testing by enabling the debug mode. A debug report is useful in comparing your cookie-based measurements with your Private Aggregation measurements, and also lets you quickly validate your API integration.
Calling privateAggregation.enableDebugMode()
in the worklet enables the debug mode which causes aggregatable reports to include the unencrypted (cleartext) payload. You can then process these payloads with the Aggregation Service local testing tool.
The debug mode is only available to callers that are allowed to access
third-party cookies. If the caller does not have access to third-party cookies,
enableDebugMode()
will silently fail.
You can also set the debug key by calling privateAggregation.enableDebugMode({ <debugKey: debugKey> })
where a BigInt
can be used as a debug key. The debug key can be used to associate data from a cookie-based measurement and data from Private Aggregation measurement.
These can be called only once per context. Any subsequent calls will throw an exception.
// Enables debug mode
privateAggregation.enableDebugMode();
// Enables debug mode and sets a debug key
privateAggregation.enableDebugMode({ debugKey: BigInt(1234) });
Report verification
The Private Aggregation API enables cross-site measurement while protecting user privacy. However, bad actors may attempt to manipulate the accuracy of these measurements. To prevent this, you can use a context ID to verify the authenticity of reports.
Setting a context ID helps ensure that the data is accurate when contributing to the final aggregate results. This is achieved by:
- Preventing illegitimate or inauthentic reports: Ensure that reports are generated through legitimate and authentic API calls, making report fabrication difficult for bad actors.
- Preventing report replaying: Detect and reject any attempts to reuse old reports, ensuring that each report is contributed only once to the aggregate results.
Shared Storage
When using Shared Storage to run an operation that can send an aggregatable report, you can set an unpredictable ID outside of the worklet.
This ID is embedded in the report created from the worklet. You can specify it
when calling either the run()
or selectURL()
Shared Storage methods, within
the options object under the privateAggregationConfig
key.
For example:
sharedStorage.run('measurement-operation', {
privateAggregationConfig: {
contextId: 'exampleId123456789abcdeFGHijk'
}
});
After this ID is set, you can use it to verify that the report was sent from
your Shared Storage operation. To prevent information leakage, exactly one
report is sent per Shared Storage operation (even if no contributions are made),
regardless of the number of contributeToHistogram()
calls.
The Private Aggregation API sends aggregatable reports with a random delay up to one hour, however, setting a context ID to verify a report reduces this delay. In this case, there is a fixed, smaller delay of 5 seconds from the Shared Storage operation starting.
An example workflow (as shown in the diagram above):
- The Shared Storage operation is run with a Private Aggregation config specifying a context ID and an aggregatable report is generated.
- The context ID is embedded in the generated aggregatable report sent to your server.
- Your server collects the generated aggregatable reports.
- Processes on your server check the context ID in each aggregatable report against your stored context IDs to ensure its validity before batching the reports and sending them to your Aggregation Service.
Context ID verification
Incoming reports to your collector server can be verified in a few different ways before being sent to the Aggregation Service. Reports with invalid context IDs can be rejected when the Context ID is:
- Unknown: If a report arrives with a context ID your system hasn't created, you can discard it. This prevents unknown or malicious actors from injecting data into your aggregation pipeline.
- A duplicate: If you receive two (or more) reports with the same context ID, it means you need to choose which of the reports to discard.
- Flagged in spam detection:
- If you detect suspicious activity from a user, for example a sudden change in a user's activity, while processing their report, you can discard it.
- You can store reports alongside their context IDs and any relevant signals (for example, user agent, referral source, etc.). Later, as you analyze user behavior and identify new spam indicators, you can re-evaluate stored reports based on their associated context IDs and signals. This lets you discard reports from users showing suspicious activity, even if they weren't initially flagged.
Engage and share feedback
The Private Aggregation API is under active discussion and subject to change in the future. If you try this API and have feedback, we'd love to hear it.
- GitHub: Read the explainer, raise questions and participate in discussion.
- Developer support: Ask questions and join discussions on the Privacy Sandbox Developer Support repo.
- Join the Shared Storage API group and the Protected Audience API group for the latest announcements related to Private Aggregation.