Shows how to call ctxSegments() to classify a page URL and inspect the classifications the DCN
returns for it: taxonomy categories (e.g. against the
IAB Content Taxonomy), free-form keywords,
and/or a brand-safety assessment (against the
Brand Safety Floor + Suitability Framework), depending on which classifiers the DCN has
enabled.
// Classify the URL of the current page (defaults to window.location.href):
optable.instance.ctxSegments();
// Or classify an explicit URL:
optable.instance.ctxSegments("https://optable.co/");
The response is a ContextualSegmentsResponse:
{
classifications: {
categories: [{ id, name, score, taxonomy }],
keywords: [{ keyword, prominence }],
brandSafety: { assessed, categories: [{ name, riskLevel }] },
},
}
The classifications object groups results by classification method, and the DCN includes only
the methods it has enabled. Three methods exist today:
categories: taxonomy category ids scored for the page| Field | Description |
|---|---|
id | Category id within its taxonomy (e.g. an IAB category id). |
name | Human-readable category name. |
score | Relevance score from 0 to 1. |
taxonomy |
Id of the taxonomy the category belongs to (e.g. iab_ct_3_1). |
keywords: free-form terms extracted from the page| Field | Description |
|---|---|
keyword | The extracted keyword text. |
prominence |
Per-page ordinal rank (1 = most prominent), not a score, so prominences are not comparable across pages. |
brandSafety: brand-safety assessment of the page
The categories and the risk tiers follow the
Brand Safety Floor + Suitability Framework, which is where the category list and the
term floor come from.
Note: only the page's text is assessed. Embedded media, such as images, video and audio, is not, so a page can come back with nothing flagged and still carry unsafe media. Treat the result as a signal about what the page says, not about everything a visitor sees.
| Field | Description |
|---|---|
assessed |
Whether an assessment backs categories. It decides what an empty
categories means, so read it first: see the table below.
|
categories[].name |
Human-readable brand-safety category name. |
categories[].riskLevel |
Tier the category was flagged at: low, medium, high or
floor, in increasing severity, so floor is the framework's brand-safety
floor rather than a baseline. not_assessed means the pass did not cover the category,
and is not a severity.
|
Read assessed before categories. An empty
categories list means two opposite things depending on it:
assessed | categories | Meaning |
|---|---|---|
true | empty | An assessment ran and flagged nothing. |
true |
non-empty |
An assessment ran: each entry is a category it flagged, at its riskLevel, or a
not_assessed category the pass did not cover. Assessed categories with no finding are
omitted.
|
false |
always empty | Nothing is known about this page. The DCN never classified it, the read failed, or this DCN does not run the brand-safety classifier at all. |
Two helpers read this off the cached response, so you do not have to walk it yourself.
ctxBrandSafety() returns the group above, and ctxMaxRiskLevel() returns the most
severe tier flagged, or null when nothing is flagged, which is convenient for a single gate:
if (optable.instance.ctxMaxRiskLevel() === "floor") {
// Do not monetize this page.
}
null comes back in two different situations: an assessment ran and flagged nothing, and
nothing is known about the page at all. A gate that must tell those apart reads
ctxBrandSafety().assessed as well. Note also that a page assessed clean is not a clearance:
the taxonomy enumerates risks, so the classifier can report that a page matches one but never that it is
free of them.
Alternatively, configure the SDK with initContextual set to a callback. The SDK will
automatically call ctxSegments() for the URL of the current page on initialization, and
invoke the callback with the response as soon as it's available — no second call required:
optable.instance = new optable.SDK({
host: "ca.edge.optable.co",
site: "web-sdk-demo",
node: "optable",
cookies: false,
initContextual: function (response) {
// Use the response, or read it later via optable.instance.ctxTargetingKeyValues().
console.log("contextual segments:", response);
},
});
Note: the URL requested must have been classified by the DCN. For the demo DCN used by
this page, the URL https://optable.co/ should have been classified, so you can try that. A
URL the DCN holds no classification for comes back with empty categories and
keywords arrays, and brandSafety.assessed set to false.
Derived from the cached ctxSegments() response via
optable.instance.ctxTargetingKeyValues(), this object can be passed straight to Google Ad
Manager via googletag.pubads().setTargeting(key, values):
var loadGAM = function (tdata = {}) {
window.googletag = window.googletag || { cmd: [] };
googletag.cmd.push(function () {
for (const [key, values] of Object.entries(tdata)) {
googletag.pubads().setTargeting(key, values);
}
googletag.pubads().refresh();
});
};
ctxTargetingKeyValues() reads the response cached on the SDK instance, so the instance should
be initialized with the initContextual: true option. That way the contextual segments are
fetched during initialization, and the cache is likely to be populated by the time loadGAM()
runs:
loadGAM(optable.instance.ctxTargetingKeyValues());
By default the returned map has one key per taxonomy the DCN classified into (keyed by the raw taxonomy
value), the page's keywords under ctx_kw, and its brand-safety tier under
ctx_bs_max. For example,
ctxTargetingKeyValues() might return:
{
"iab_ct_3_1": ["53", "91", "58", "115", "90", "52"],
"ctx_kw": ["advertising", "programmatic", "ad tech"],
"ctx_bs_max": ["no_flags"]
}
If you want loadGAM() to run as soon as the contextual segments arrive — without making a
second ctxSegments() call — pass a callback to initContextual. The SDK fires the
contextual request automatically during initialization and invokes the callback with the response,
populating the cache before ctxTargetingKeyValues() reads from it:
optable.instance = new optable.SDK({
host: "ca.edge.optable.co",
site: "web-sdk-demo",
node: "optable",
cookies: false,
initContextual: function (response) {
loadGAM(optable.instance.ctxTargetingKeyValues());
},
});
If you are not using initContextual at all, fetch the segments explicitly and pass the result
to loadGAM() once ctxSegments() resolves (falling back to an untargeted load on
error):
optable.cmd.push(function () {
optable.instance
.ctxSegments()
.then(loadGAM)
.catch((err) => {
loadGAM();
});
});
By default each taxonomy is emitted under its own value as the GAM key. Pass a map to
ctxTargetingKeyValues() to rename keys and allow-list which taxonomies are emitted — only
taxonomies present in the map are included:
// Emit only the "iab_ct_3_1" taxonomy, under the GAM key "ctx_iab":
loadGAM(optable.instance.ctxTargetingKeyValues({ iab_ct_3_1: "ctx_iab" }));
Keyword classifications are also emitted, by default under the GAM key ctx_kw. The values are
the page's keywords ordered by prominence (most prominent first), capped to the top 10, and
sanitized to GAM's value rules (lowercased, reserved characters stripped, truncated to 40 characters). Pass
keywordKey to rename the key or maxKeywords to change the cap, or set
keywordKey to an empty string to opt out of keyword key-values entirely:
// Rename the keyword key and emit only the top 5 keywords:
loadGAM(optable.instance.ctxTargetingKeyValues({ iab_ct_3_1: "ctx_iab" }, { keywordKey: "kw", maxKeywords: 5 }));
// Opt out of keyword key-values:
loadGAM(optable.instance.ctxTargetingKeyValues(undefined, { keywordKey: "" }));
Brand safety is emitted by default under ctx_bs_max, carrying the page's most severe flagged
tier. It is the same text-only assessment described
above, so a line item keyed on it is gating on the page's
words and not on its embedded media. Use brandSafetyKey to rename it, or pass an empty
brandSafetyKey to opt out, exactly as keywordKey works:
// Rename the brand-safety key:
loadGAM(optable.instance.ctxTargetingKeyValues(undefined, { brandSafetyKey: "bs" }));
// Opt out of the brand-safety key-value:
loadGAM(optable.instance.ctxTargetingKeyValues(undefined, { brandSafetyKey: "" }));
A value is emitted for every state rather than the key being dropped when nothing is flagged:
| State | Value |
|---|---|
| A tier was flagged |
The most severe one: low, medium, high or floor
|
| Assessed, nothing flagged | no_flags |
| Nothing is known about the page | not_assessed |
The two sentinels exist so a line item can tell them apart. If the key were simply absent when nothing was
flagged, "we never looked" and "we looked and flagged nothing" would look identical in GAM, and you could
not exclude unassessed inventory without also excluding clean inventory. no_flags is
deliberately not named safe: a pass with no finding is not a clearance.
not_assessed is spelled the same as the riskLevel a category carries when the
assessment did not cover it. That is deliberate: one word for one idea, "nothing is known here", at two
scopes. The scopes cannot be confused, because ctx_bs_max only ever carries a page-level
answer.
One difference from keywordKey is worth knowing before you ship: keywords are dropped when
the DCN produced none, whereas ctx_bs_max is always present unless you disable it. A DCN that
does not run the brand-safety classifier therefore adds ctx_bs_max=not_assessed to every ad
request. If you do not use brand safety at all, pass an empty brandSafetyKey to keep it out.