Using the Topics API
Warning: This feature is currently opposed by two browser vendors. See the Standards positions section below for details of opposition.
Note: An Enrollment process is required to use the Topics API in your applications. See the Enrollment section for details of what sub-features are gated by enrollment.
This page explains how the Topics API works and how it can be used to create an interest-based advertising (IBA) solution.
High-level overview
Let's say we've got an ad tech platform, ad-tech1.example
, which is embedding ads via <iframe>
s into the following publisher sites:
yoga.example
knitting.example
football.example
If the <iframe>
content from ad-tech1.example
implements a feature that enables the Topics API, when each of the sites is loaded the browser will:
- Infer topics of interest from the site URL. The topics are taken from a standard taxonomy; for the above URL examples, they would be "Fitness", "Fibre & textile arts", and "Soccer".
- Mark the topics as observed, which involves recording a topics history entry for each one in a private topics history storage. Each topics history entry includes the following information:
- A document id (i.e. an identifier for the current page).
- Topics calculation input data (i.e. the page hostname).
- The time (since the Unix epoch) when the page was first observed.
- The domain(s) where the topic was observed (known as topic caller domains).
Selecting topics of interest to influence ad choice
Note: Different browser implementations may select topics in different ways. The below text is based on how Chrome currently selects topics, for demonstration purposes.
On an ongoing basis, the browser will:
- Keep track of how often the user observes each topic during each new epoch. An epoch is a week by default, but the length can be altered for testing purposes (see Testing hints). Chrome places each of the 22 root topics (those without an ancestor) from the taxonomy into one of two buckets indicating higher or standard utility for the overall ad tech ecosystem. All descendants of the root topics inherit the same bucket assignment from their parent. The assignment of root topics to buckets is based on input about utility Google received from companies across the ecosystem.
- Select top topics for each user, at the end of each epoch:
- Chrome converts caller domain hostnames from the user's browsing history into topics.
- These topics are sorted first by bucket, and then by frequency (how many times they were matched in a hostname). That is, if two topics are in the same bucket but have different frequencies, the higher frequency topic is sorted higher.
- Chrome selects the top five topics as the user's top topics for that epoch, which are eligible to be shared with callers.
- The top topics are returned to
ad-tech1.example
, only ifad-tech1.example
appears in the list of caller domains for each topic, as stored in the topic's history entry.Note: Initially, no topics are returned, so the
<iframe>
will likely display a default non-targeted ad. However, once the end of the first epoch is reached, the API will start to return topics andad-tech1.example
can start to show more relevant ads based on the observed topics for the current user.
ad-tech1.example
then selects a relevant ad to serve to the user, based on the returned topics.
What API features enable the Topics API?
The following features all serve a dual purpose — they return the user's top topics to the caller and they trigger the browser to record the current page visit as observed by the caller, so the page's hostname can later be used in topics calculation. To do so, they need to be included in a calling ad tech's <iframe>
; the <iframe>
then has to be embedded on the pages where you want topics observed.
- You can specify a
browsingTopics: true
option in the options object of afetch()
call to the ad tech platform. - You could also pass
browsingTopics: true
into the options object of aRequest()
constructor call, and pass the resultingRequest
object into thefetch()
call. - You can set a
browsingtopics
attribute on the<iframe>
, at the same time or before setting thesrc
attribute to load the source. This could be done:- Declaratively on the HTML:
html<iframe browsingtopics src="ad-tech1.example"> ... </iframe>
- Programmatically by setting the equivalent
HTMLIFrameElement.browsingTopics
property totrue
:
jsconst iframeElem = document.querySelector("iframe"); iframeElem.browsingTopics = true;
When the request associated with one of the above features is sent:
- A
Sec-Browsing-Topics
header is sent along with the request, which contains the top topic(s) for the current user. - The ad tech server selects a relevant ad to display in the
<iframe>
, based on these topics, and sends the required data to display it in the response. - An
Observe-Browsing-Topics
header should be set on the response to the request — this has the effect of causing the browser to record the current page visit as observed by the calling ad tech provider, so the associated topic(s) will be recorded in a topics history entry, and subsequently be used in topic selection.Note: It is important to clarify that this doesn't record the top topics sent in the
Sec-Browsing-Topics
header as observed. It records the topics inferred from the calling site's URL (i.e. the site where the ad tech<iframe>
is embedded) as observed.
The browsingTopics()
method
Alternatively, the embedded <iframe>
can call Document.browsingTopics()
to return a user's current top topic(s), which can then be returned to the ad tech platform in a subsequent fetch request. This does not rely on the HTTP headers, but is somewhat less performant. You are advised to use one of the HTTP header methods listed above, falling back to browsingTopics()
only in situations where the headers cannot be modified.
Note: Because the browsingTopics()
method does not rely on the HTTP headers, the Observe-Browsing-Topics
header is not used for setting the topics as observed and recording/updating topics history entries; the browser does this automatically when the method is called.
Private topic sets
A caller can only access topics that they themselves have observed for a user — and not topics observed by other callers. For example:
- If the
ad-tech1.example
platform has an<iframe>
embedded ontennis.example
that includes a Topics API feature, they would observe topics like "Sports" and "Tennis" for a user who visits that site. - If another ad tech platform,
ad-tech2.example
, has a Topics API<iframe>
embedded on "gardening.example", they would observe the topic "Gardening".
These ad tech platforms will only get topics for a user that they have observed. In this example, ad-tech1.example
won't get "Gardening" and ad-tech2.example
won't get "Tennis".
In other words, callers such as ad tech platforms only get topics for pages where they have a presence. More importantly, the recorded topics of interest are the only information that can be accessed via this API — unlike with tracking cookies, no other information can be leaked.
Examples
Using Document.browsingTopics()
// Get an array of topics for this user
const topics = await document.browsingTopics();
// Request an ad creative
const response = await fetch("https://ads.example/get-creative", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(topics),
});
// Get the JSON from the response
const creative = await response.json();
// Display ad
Passing the browsingTopics
option into fetch()
// Request an ad creative
const response = await fetch("https://ads.example/get-creative", {
browsingTopics: true,
});
// Get the JSON from the response
const creative = await response.json();
// Display ad
Including the browsingtopics
attribute in an <iframe>
<iframe browsingtopics src="ad-tech1.example"> ... </iframe>
Complete examples
- Topics API demo: Demonstrates how
document.browsingTopics()
calls can be used to observe and then access topics (see source code). - Topics API header demo: Demonstrates a
fetch()
request with aSec-Browsing-Topics
header can be used to observe and then access topics (see source code).
Testing hints
Chrome
The default epoch length for observing topics is one week, which is way too long to test code that uses the Topics API. To shorten this for test purposes, in Chrome you can open the browser with a feature flag along the following lines:
BrowsingTopicsParameters:time_period_per_epoch/15s/max_epoch_introduction_delay/3s
See Run Chromium with command-line switches for more information on how to do this.
You can also test your Topics API code locally without enrollment by enabling the following Chrome developer flag:
chrome://flags/#privacy-sandbox-enrollment-overrides
See also
- Topics API on developers.google.com (2023)