-
Notifications
You must be signed in to change notification settings - Fork 2.3k
datamage rtd contextual provider: initial release #14485
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
patmmccann
merged 20 commits into
prebid:master
from
Ops-Mage:rtd/datamage-contextual-provider
Mar 20, 2026
Merged
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
e2812ea
Create datamage RTD integration
leiforion 035b2a6
remove test.html files
leiforion 67456d8
update example.html
leiforion 5ab88c8
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion af7634a
siplify architecture, adding back in linting, improve load
leiforion 5b319d4
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion 923ca7e
fixed caching issues and bot comments from original pr
leiforion 427757c
fix issues identified by bot
leiforion 0345761
replace dep GPT api calls to modern format
leiforion fe0b255
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion 0334499
update btoa to support non typical url encodings, strip common tracki…
leiforion 6b35e9a
remove misc local test files
leiforion 80f2d75
fix fetchPromise transient failur potential
leiforion f255dae
reduce key value size and unusued keys in payload to gam and ortb
leiforion d383867
update Rtd spec file naming
leiforion 3d12e1a
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion 4ce2874
update prebid js path in example
leiforion 3c0a2fd
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion f04b940
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion 4c7cd78
include datamageRtdprovider in submodules json
leiforion File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
143 changes: 143 additions & 0 deletions
143
integrationExamples/realTimeData/datamageRtdProvider_example.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| <!DOCTYPE html> | ||
| <html> | ||
|
|
||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <title>OpsMage Prebid Test Page</title> | ||
|
|
||
| <script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js" crossorigin="anonymous"></script> | ||
| <script> | ||
| window.googletag = window.googletag || { cmd: [] }; | ||
| window.gptSlot = null; | ||
|
|
||
| googletag.cmd.push(function () { | ||
| googletag.pubads().disableInitialLoad(); | ||
|
|
||
| // Save slot reference so we can refresh JUST this slot | ||
| window.gptSlot = googletag.defineSlot( | ||
| '/50536250/designcon', | ||
| [[300, 250]], | ||
| 'div-gpt-ad-1771335974713-0' | ||
| ).addService(googletag.pubads()); | ||
|
|
||
| googletag.pubads().enableSingleRequest(); | ||
| googletag.enableServices(); | ||
|
|
||
| // Display once (no request yet because disableInitialLoad) | ||
| googletag.display('div-gpt-ad-1771335974713-0'); | ||
| }); | ||
|
|
||
| // Helper function to verify targeting in the console | ||
| function dumpGptTargeting(label) { | ||
| googletag.cmd.push(function () { | ||
| const pubads = googletag.pubads(); | ||
| const keys = pubads.getTargetingKeys(); | ||
| const pubadsMap = {}; | ||
| keys.forEach(k => { pubadsMap[k] = pubads.getTargeting(k); }); | ||
|
|
||
| console.log(label, 'PUBADS targeting:', pubadsMap); | ||
|
|
||
| if (window.gptSlot && typeof window.gptSlot.getTargetingMap === 'function') { | ||
| console.log(label, 'SLOT targeting:', window.gptSlot.getTargetingMap()); | ||
| } | ||
| }); | ||
| } | ||
| </script> | ||
|
|
||
| <script async src="build/dev/prebid.js"></script> | ||
|
|
||
| <script> | ||
| var pbjs = window.pbjs = window.pbjs || {}; | ||
| pbjs.que = pbjs.que || []; | ||
|
|
||
| var PREBID_TIMEOUT = 1500; | ||
|
|
||
| var adUnits = [{ | ||
| code: 'div-gpt-ad-1771335974713-0', | ||
| mediaTypes: { banner: { sizes: [[300, 250]] } }, | ||
| bids: [{ | ||
| bidder: 'appnexus', | ||
| params: { placementId: 1234567 } | ||
| }] | ||
| }]; | ||
|
|
||
| pbjs.que.push(function () { | ||
| pbjs.setConfig({ | ||
| debug: true, | ||
|
|
||
| consentManagement: { | ||
| allowAuctionWithoutConsent: true, | ||
| gdpr: { | ||
| cmpApi: 'static', | ||
| timeout: 0, | ||
| consentData: { | ||
| getTCData: { | ||
| tcString: 'CPAa+2APAa+2AAOACBENC1CoAP_AAH_AAAAAAwwxgAAAAA', | ||
| gdprApplies: true, | ||
| purpose: { consents: { 1: true, 2: true, 3: true, 4: true, 5: true } }, | ||
| vendor: { consents: { 1: true }, legitimateInterests: { 1: true } } | ||
| } | ||
| } | ||
| }, | ||
| usp: { cmpApi: 'static', timeout: 0, consentData: { getUSPData: { uspString: '1---' } } } | ||
| }, | ||
|
|
||
| allowActivities: { | ||
| accessDevice: { rules: [{ action: 'allow' }] }, | ||
| enrichUfpd: { rules: [{ action: 'allow' }] }, | ||
| enrichEids: { rules: [{ action: 'allow' }] }, | ||
| transmitTid: { rules: [{ action: 'allow' }] } | ||
| }, | ||
|
|
||
| realTimeData: { | ||
| auctionDelay: 1000, // Prebid will wait up to 1000ms for Datamage to fetch data | ||
| dataProviders: [{ | ||
| name: "datamage", | ||
| waitForIt: true, // <--- CRITICAL FIX: Forces Prebid to wait for this module | ||
| params: { | ||
| api_key: '8328309832', | ||
| selector: 'article', | ||
| fetch_timeout_ms: 2500 | ||
| } | ||
| }] | ||
| } | ||
| }); | ||
|
|
||
| pbjs.addAdUnits(adUnits); | ||
|
|
||
| pbjs.requestBids({ | ||
| bidsBackHandler: function () { | ||
| // Push standard header bidding keys (like hb_pb) to GPT | ||
| pbjs.setTargetingForGPTAsync(); | ||
|
|
||
| // Dump targeting to verify Datamage was successfully pushed via setConfig | ||
| dumpGptTargeting('[before refresh]'); | ||
|
|
||
| // Refresh only our specific slot | ||
| googletag.cmd.push(function () { | ||
| if (window.gptSlot) { | ||
| googletag.pubads().refresh([window.gptSlot]); | ||
| } else { | ||
| // Fallback | ||
| googletag.pubads().refresh(); | ||
| } | ||
| }); | ||
| }, | ||
| timeout: PREBID_TIMEOUT | ||
| }); | ||
| }); | ||
| </script> | ||
| </head> | ||
|
|
||
| <body> | ||
| <h2>OpsMage Prebid Test Page</h2> | ||
| <div id="div-gpt-ad-1771335974713-0" style="width:300px; height:250px;"></div> | ||
| <article>The tech world is currently buzzing over the highly anticipated market debut of fakeDSP, a trailblazing | ||
| startup poised to redefine the landscape of digital signal processing. Leveraging proprietary neuromorphic | ||
| algorithms and quantum-ready architecture, fakeDSP promises to accelerate real-time data synthesis by speeds | ||
| previously thought impossible. With early analysts calling it a definitive disruptor in both the | ||
| telecommunications and audio-engineering sectors, the company’s entrance signifies a major leap forward in how | ||
| complex signals are analyzed and reconstructed in the AI era.</article> | ||
| </body> | ||
|
|
||
| </html> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
|
|
||
| # DataMage RTD Submodule | ||
|
|
||
| DataMage provides real-time contextual classification (IAB Categories, Sentiment, Brands, Locations, Public Figures, Restricted Categories, and related IDs) that can be used to enrich demand signals and Google Ad Manager targeting. | ||
|
|
||
| ## What it does | ||
|
|
||
| DataMage automatically supports two outcomes in a Prebid + GAM setup without requiring any custom glue-code on the page: | ||
|
|
||
| 1. **Passes data to Google Ad Manager (Direct GPT targeting)** | ||
|
|
||
| * The moment Prebid initializes, DataMage fetches classification for the current page and automatically pushes the targeting keys directly to GPT using the modern `googletag.setConfig({ targeting: ... })` API at the page level. | ||
| * This ensures the data is available for all ad slots and works **even if there are no bids** or if the auction times out. | ||
|
|
||
| 2. **Passes data to bidders (ORTB2 enrichment)** | ||
|
|
||
| * Using a memoized cache from the initial fetch, DataMage seamlessly inserts the contextual results into the bid request using OpenRTB (`ortb2Fragments.global.site.content.data`), allowing bidders to receive the enriched signals instantly. | ||
|
|
||
| ## Keys provided | ||
|
|
||
| DataMage automatically maps and provides the following targeting keys (when available in the API response): | ||
|
|
||
| * `om_iab_cat_ids` | ||
| * `om_iab_cats` | ||
| * `om_brand_ids` | ||
| * `om_sentiment_ids` | ||
| * `om_location_ids` | ||
| * `om_public_figure_ids` | ||
| * `om_restricted_cat_ids` | ||
| * `om_restricted_cats` | ||
| * `om_ops_mage_data_id` | ||
| * `om_res_score_bucket` | ||
| * `om_res_score` (only when present) | ||
|
|
||
| ## Prebid config | ||
|
|
||
| ```javascript | ||
| pbjs.setConfig({ | ||
| realTimeData: { | ||
| auctionDelay: 1000, // Gives the module time to fetch data before bids are sent, suggested minimum 1000 | ||
| dataProviders: [{ | ||
| name: "datamage", | ||
| waitForIt: true, // CRITICAL: Forces Prebid to wait for the module to fetch data before resolving the auction | ||
| params: { | ||
| api_key: "YOUR_API_KEY", | ||
| selector: "article", | ||
| fetch_timeout_ms: 2500 | ||
| } | ||
| }] | ||
| } | ||
| }); | ||
|
|
||
| ``` | ||
|
|
||
| ## GAM set up requirements | ||
|
|
||
| Because DataMage automatically injects targeting globally via `setConfig`, your page implementation only requires a standard Prebid setup. | ||
|
|
||
| To ensure DataMage key-values are included in your GAM requests: | ||
|
|
||
| 1. Call `googletag.pubads().disableInitialLoad()` before your ad requests. | ||
| 2. Define your slots and call `googletag.enableServices()`. | ||
| 3. Run `pbjs.requestBids(...)`. | ||
| 4. Inside the `bidsBackHandler` callback: | ||
| * Call `pbjs.setTargetingForGPTAsync()` (to set standard Prebid `hb_` pricing keys). | ||
| * Call `googletag.pubads().refresh()` to trigger the GAM request. | ||
|
|
||
|
|
||
|
|
||
| GAM will automatically combine the standard Prebid slot-level pricing keys with the page-level DataMage contextual keys. | ||
|
|
||
| *Note that you will need a real API key provisioned by DataMage to use this module in production.* | ||
|
|
||
| ### Example: | ||
|
|
||
| ```javascript | ||
| pbjs.requestBids({ | ||
| bidsBackHandler: function () { | ||
| // Push standard header bidding keys to GPT | ||
| pbjs.setTargetingForGPTAsync(); | ||
|
|
||
| // Refresh the ad slots. Datamage page-level keys are already injected! | ||
| googletag.cmd.push(function () { | ||
| googletag.pubads().refresh(); | ||
| }); | ||
| }, | ||
| timeout: 1500 | ||
| }); | ||
|
|
||
| ``` |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.