Private Aggregation API overview

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.

Example workflow for report verification

An example workflow (as shown in the diagram above):

  1. The Shared Storage operation is run with a Private Aggregation config specifying a context ID and an aggregatable report is generated.
  2. The context ID is embedded in the generated aggregatable report sent to your server.
  3. Your server collects the generated aggregatable reports.
  4. 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.