Unique reach measurement

Many content producers and advertisers want to know how many unique people saw their content. Use Shared Storage to record the first time a user saw your ad, embedded video, or publication, and prevent duplicative counting of that same user on different sites. You can then use the Private Aggregation API to output a summary report for your reach.

The Shared Storage API is a Privacy Sandbox proposal for general purpose, cross-site storage, which supports many possible use cases. Private Aggregation API is an output available in Shared Storage that lets you to aggregate cross-site data. To learn more about methods to implement these measurements, check out our reach whitepaper.

Try unique reach measurement

To experiment with unique reach measurement with Shared Storage and Private Aggregation, confirm you're using Chrome M107 or later. Enable all the Ad privacy APIs under chrome://settings/adPrivacy.

You can also enable Shared Storage with the --enable-features=PrivacySandboxAdsAPIsOverride,OverridePrivacySandboxSettingsLocalTesting,SharedStorageAPI,FencedFrames flag in the command line.

Experiment with code samples

You may want to keep track of how many unique users have seen your content across different sites. In this example, the content ID dimension is encoded into the aggregation key (bucket), and the count is used as the aggregatable value. The summary report will contain information such as "Approximately 391 users have seen the content ID 123."

In this example: * unique-reach-measurement.js is loaded via a frame, and is responsible for loading the shared storage worklet. * unique-reach-measurement-worklet.js is the shared storage worklet that checks the flag in shared storage and sends a report via the Private Aggregation API.

reach-measurement.js

async function measureUniqueReach() {
  // Load the Shared Storage worklet
  await window.sharedStorage.worklet.addModule('reach-measurement-worklet.js');

  // Run the reach measurement operation
  await window.sharedStorage.run('reach-measurement', { data: { contentId: '1234' } });
}

measureUniqueReach();

reach-measurement-worklet.js

// Learn more about noise and scaling from the Private Aggregation fundamentals
// documentation on Chrome blog
const SCALE_FACTOR = 65536;

function convertContentIdToBucket(contentId) {
  return BigInt(contentId);
}

class ReachMeasurementOperation {
  async run(data) {
    const { contentId } = data;

    // Read from Shared Storage
    const key = 'has-reported-content';
    const hasReportedContent = (await sharedStorage.get(key)) === 'true';

    // Do not report if a report has been sent already
    if (hasReportedContent) {
      return;
    }

    // Generate the aggregation key and the aggregatable value
    const bucket = convertContentIdToBucket(contentId);
    const value = 1 * SCALE_FACTOR;

    // Send an aggregatable report via the Private Aggregation API
    privateAggregation.contributeToHistogram({ bucket, value });

    // Set the report submission status flag
    await sharedStorage.set(key, true);
  }
}

// Register the operation
register('reach-measurement', ReachMeasurementOperation);

Engage and share feedback

The Shared Storage proposal 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.