Two ways in. Point an ad tag at /tag and clearTag cleans and
decorates it in flight, or call /v1/enrich and place the values yourself. Both
return the same signals for the same content.
Content in, signals out. Every field is optional; send what you have, as your partner actually sends it — macros, mixed delimiters, wrong language and all.
curl "https://cleartag.scenecontext.io/v1/enrich?\ channel=WELT%20HD&genre=Nachrichten,%20Politik&title=Tagesschau"
| Param | |
|---|---|
channel | Channel or station name, in any spelling. |
genre | One or more genres. Commas, pipes, slashes, "und" and "&" all separate. |
title series | Programme and series names. |
cat | IAB codes (IAB17, 17) or labels. |
keywords | Free-form terms, comma separated. |
id | Your own content id, if you have a stable one. |
url | Page or content URL. |
country | ISO alpha-2 (DE, CH). On
POST it belongs at device.geo.country, where OpenRTB already puts it. Not a
targeting value — it decides which market a channel name resolves to, so without it a
German channel can match a same-named channel elsewhere. |
livestream | Optional hint for linear or FAST inventory. Send it only where it is true — omitted means unknown, not video-on-demand, and a mixed catalogue is the normal case. Resolution does not depend on it. |
Responses carry Cache-Control: public, max-age=300. The same content always
resolves to the same signals, so put a CDN or edge cache in front of this and an SSAI
stitcher can call it per ad break.
curl -X POST https://cleartag.scenecontext.io/v1/enrich \
-d '{"content":{"channel":"WELT HD","genre":"Nachrichten, Politik"},
"tag":"https://securepubads.g.doubleclick.net/gampad/ads?iu=..."}'
Same fields, nested under content, plus two extras:
tag | An ad tag URL. Supply it and the response also carries
decorated_tag, ready to fire. |
junk | "strip" removes publisher keys whose value is
only a macro or placeholder. Off by default, which keeps every key exactly as sent. |
cust_params, and carries device ids and consent
strings. Passing one as a query parameter means triple encoding and writes personal data into
every access log and proxy on the way. Use GET to look up signals, POST to hand over a tag.{
"ok": true,
"enriched": true,
"id": "SC5fMRINbiJdcBaL-0",
"content": { "content_channel": "welt", "content_genre": "news" },
"emitted": { "channel": "welt", "genre": "news",
"id": "SC5fMRINbiJdcBaL-0", "seg": "current-events,news" },
"segments": [ { "slug": "news", "score": 0.666 } ],
"openrtb": { "content": { ... }, "key_val": "channel=welt|genre=news|..." }
}
| Field | |
|---|---|
emitted | The key-values to place. Identical for every ad server, so the same values work whichever transport your partner uses. |
id | The content id. Stable for the same content across tags and ad servers — this is what makes an impression identifiable downstream. |
openrtb | The same enrichment as a bid-request content object,
plus PubMatic-style key_val. |
enriched | false with a reason when
nothing usable survived. Never an error — see below. |
enriched: false and a reason, not a 4xx. On the proxy path the original tag is
redirected to unchanged. An ad request is never lost because enrichment failed./tag?tag=<url-encoded ad tag>[&channel=…&genre=…&junk=strip]
Cleans, resolves and decorates, then redirects. A 302 rather than a fetch, so your ad server still sees the real device IP and user agent — targeting, frequency capping and fraud signals are unchanged. Explicit params override whatever the tag carries.
Identical parameters to /tag. Returns what would be sent, including
received_keys classifying each key you sent as read, junk or
ignored. This is what the bench renders. Never logged.
Some signals are an ad-ops decision rather than a technical one. These live in
data/policy.json and apply on POST /reload — no release:
emit.title | Content title as ct_ti and OpenRTB
content.title. Off if a programme name is competitively sensitive, or simply
should not leave your ad server. |
emit.series | Series name. Same consideration. |
emit.keywords | Programme keywords in the content object. Never
in cust_params — keywords are unbounded and the reportable key-value ceiling
is what publishers hit first. |
emit.sc_segments | SceneContext segment ids in the content object. Useful to a buyer onboarded onto that vocabulary, noise to everyone else. |
data/pps.json, so a
different IAB release is a data change rather than a code one.fill.title_from_resolver exists for when that
changes; it will never invent one from keywords.| /tag | Token bucket per client address. Over the limit it degrades to an unenriched passthrough rather than failing — a viewer never loses an ad because we were busy. |
| /v1/enrich | Its own bucket, sized for server-side integration rather than for someone refreshing a dashboard. Ask for a partner key and we raise it for you specifically. |
| Body | 64 KB on POST. |
X-API-Key: <key> or Authorization: Bearer <key>. Quota is
then counted per key rather than per address — an exchange calls from many hosts, and a
shared NAT would otherwise let one caller exhaust everyone else's.