@mixpeek/prebid
v1.0.0
Published
Mixpeek for Prebid.js - Enrich bid requests with real-time multimodal AI contextual data
Downloads
4
Readme
Mixpeek Contextual Adapter for Prebid.js
🎯 Overview
The Mixpeek Contextual Adapter enables publishers and SSPs using Prebid.js to enrich bid requests with real-time contextual data powered by Mixpeek's multimodal AI engine. This adapter provides:
- Privacy-First Targeting: No cookies, just content-based context
- Multimodal Analysis: Text, images, video, and audio processing
- IAB Taxonomy: Automatic classification into IAB content categories
- Brand Safety: Real-time brand safety scoring
- Ad Adjacency Awareness: Tracks previous ad to avoid repetition and improve user experience
- Sub-100ms Performance: Optimized for header bidding speed requirements
- Graceful Fallbacks: Never blocks the auction
🚀 Quick Start
Installation
npm install @mixpeek/prebidBasic Setup
// 1. Include the Mixpeek RTD module
import '@mixpeek/prebid'
// 2. Configure Mixpeek as an RTD provider
pbjs.setConfig({
realTimeData: {
auctionDelay: 250, // Max time to wait for contextual data (ms)
dataProviders: [{
name: 'mixpeek',
waitForIt: true, // Wait for Mixpeek before starting auction
params: {
apiKey: 'YOUR_MIXPEEK_API_KEY',
collectionId: 'your-collection-id',
// Use development server (temporary)
endpoint: 'https://server-xb24.onrender.com',
// Or production: endpoint: 'https://api.mixpeek.com',
namespace: 'your-namespace', // optional
featureExtractors: ['taxonomy', 'brand-safety'],
mode: 'page', // 'page', 'video', or 'auto'
timeout: 5000, // ms - higher for dev server
cacheTTL: 300 // seconds
}
}]
}
})
// 3. The RTD module automatically enriches bid requests!
pbjs.requestBids({
adUnits: [...],
bidsBackHandler: function(bids) {
// Bids now include Mixpeek contextual data in ortb2
}
})📋 Prerequisites
- Mixpeek Account: Sign up at mixpeek.com
- API Key: Generate an API key in your Mixpeek dashboard
- Collection: Create a collection with feature extractors configured
- Prebid.js: Version 6.0.0 or higher
🔧 Configuration Options
RTD Configuration
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| realTimeData.auctionDelay | number | ❌ | 250 | Max time to wait for all RTD providers (ms) |
| realTimeData.dataProviders[].name | string | ✅ | - | Must be 'mixpeek' |
| realTimeData.dataProviders[].waitForIt | boolean | ❌ | false | Wait for Mixpeek before starting auction |
Mixpeek Parameters
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| params.apiKey | string | ✅ | - | Your Mixpeek API key |
| params.collectionId | string | ✅ | - | Mixpeek collection ID for document processing |
| params.endpoint | string | ❌ | https://server-xb24.onrender.com | Mixpeek API endpoint (dev server default) |
| params.namespace | string | ❌ | - | Optional namespace for data isolation |
| params.featureExtractors | array | ❌ | ['taxonomy'] | Feature extractors to use (taxonomy, brand-safety, etc.) |
| params.mode | string | ❌ | auto | Content mode: page, video, image, or auto |
| params.timeout | number | ❌ | 250 | API request timeout in milliseconds |
| params.cacheTTL | number | ❌ | 300 | Cache TTL in seconds |
| params.enableCache | boolean | ❌ | true | Enable local caching |
| params.debug | boolean | ❌ | false | Enable debug logging |
| params.batchSize | number | ❌ | 1 | Number of concurrent requests |
| params.retryAttempts | number | ❌ | 2 | Number of retry attempts on failure |
📊 Output: OpenRTB 2.6 Data Structure
The RTD module injects contextual data into your bid requests using the OpenRTB 2.6 standard:
Site-Level Data (ortb2.site.content)
{
"ortb2": {
"site": {
"content": {
"cat": ["IAB19-11"], // IAB Content Categories
"cattax": 6, // IAB Content Taxonomy v3.0
"genre": "Technology - AI", // Human-readable category
"keywords": "ai,technology,ml", // Extracted keywords
"language": "en", // Content language
"title": "Article Title", // Page title
"url": "https://example.com", // Page URL
"ext": {
"data": {
"mixpeek": {
"score": 0.94, // Confidence score
"brandSafety": 0.98, // Brand safety score
"sentiment": "positive", // Content sentiment
"embeddingId": "emb_abc123" // Embedding ID
}
}
}
}
}
}
}Impression-Level Data (ortb2Imp.ext.data)
{
// Current page context
"hb_mixpeek_taxonomy": "IAB19-11", // Primary IAB taxonomy code
"hb_mixpeek_category": "Technology > AI", // Human-readable category
"hb_mixpeek_node": "node_tech_ai", // Taxonomy node ID
"hb_mixpeek_path": "tech/ai/ml", // Hierarchical path
"hb_mixpeek_score": "0.94", // Confidence score
"hb_mixpeek_safety": "0.98", // Brand safety score
"hb_mixpeek_keywords": "AI,ML,tech", // Extracted keywords
"hb_mixpeek_embed": "emb_abc123", // Embedding ID for retrieval
// Previous ad context (adjacency awareness)
"hb_mixpeek_prev_creative": "12345", // Last creative ID shown
"hb_mixpeek_prev_bidder": "appnexus", // Last bidder that won
"hb_mixpeek_prev_adunit": "sidebar-1", // Last ad unit code
"hb_mixpeek_prev_cat": "IAB18-1,IAB12-3" // Last ad categories
}🎥 Usage Examples
Page Context (Articles, Blogs)
pbjs.setConfig({
realTimeData: {
auctionDelay: 250,
dataProviders: [{
name: 'mixpeek',
waitForIt: true,
params: {
apiKey: 'sk_your_api_key',
collectionId: 'col_articles',
mode: 'page',
featureExtractors: ['taxonomy', 'brand-safety', 'keywords']
}
}]
}
})Video Context (Pre-roll, Mid-roll)
pbjs.setConfig({
realTimeData: {
auctionDelay: 300, // Longer delay for video processing
dataProviders: [{
name: 'mixpeek',
waitForIt: true,
params: {
apiKey: 'sk_your_api_key',
collectionId: 'col_videos',
mode: 'video',
videoSelector: '#main-video', // CSS selector for video element
featureExtractors: ['taxonomy', 'scene-detection']
}
}]
}
})Multi-Content Auto-Detection
pbjs.setConfig({
realTimeData: {
auctionDelay: 250,
dataProviders: [{
name: 'mixpeek',
waitForIt: true,
params: {
apiKey: 'sk_your_api_key',
collectionId: 'col_mixed',
mode: 'auto', // Automatically detects page, video, or image content
featureExtractors: ['taxonomy', 'brand-safety', 'clustering']
}
}]
}
})🏗️ How It Works
sequenceDiagram
participant Publisher
participant Prebid
participant MixpeekAdapter
participant MixpeekAPI
participant SSP
Publisher->>Prebid: Request Bids
Prebid->>MixpeekAdapter: beforeRequestBids event
MixpeekAdapter->>MixpeekAdapter: Extract page/video content
MixpeekAdapter->>MixpeekAdapter: Check cache
alt Cache Miss
MixpeekAdapter->>MixpeekAPI: POST /collections/{id}/documents
MixpeekAPI->>MixpeekAPI: Process with feature extractors
MixpeekAPI-->>MixpeekAdapter: Return enrichments
MixpeekAdapter->>MixpeekAdapter: Cache result
end
MixpeekAdapter->>Prebid: Inject contextual key-values
Prebid->>SSP: Send enriched bid request
SSP-->>Prebid: Return bids
Prebid-->>Publisher: Render ad🧪 Testing
# Run all tests
npm test
# Run with coverage
npm run test:coverage
# Watch mode
npm run test:watch📖 Advanced Configuration
Custom Feature Extractors
pbjs.setConfig({
realTimeData: {
auctionDelay: 250,
dataProviders: [{
name: 'mixpeek',
waitForIt: true,
params: {
apiKey: 'sk_your_api_key',
collectionId: 'col_custom',
customExtractors: [
{
feature_extractor_id: 'sentiment-analyzer',
payload: {
model: 'sentiment-v2',
threshold: 0.7
}
}
]
}
}]
}
})Conditional Loading
// Only enrich on specific pages
if (window.location.pathname.startsWith('/articles/')) {
pbjs.setConfig({
realTimeData: {
auctionDelay: 250,
dataProviders: [{
name: 'mixpeek',
waitForIt: true,
params: {
apiKey: 'sk_your_api_key',
collectionId: 'col_articles',
mode: 'page'
}
}]
}
})
}Event Callbacks
pbjs.onEvent('mixpeekContextReady', function(context) {
console.log('Mixpeek context loaded:', context)
// Custom analytics or modifications
})
pbjs.onEvent('mixpeekContextError', function(error) {
console.error('Mixpeek context error:', error)
// Custom error handling
})🔄 Previous Ad Tracking (Adjacency Awareness)
The adapter automatically tracks the most recently served ad to enable adjacency-aware targeting. This helps:
- Avoid Ad Repetition: Prevent showing the same creative or category repeatedly
- Frequency Capping: Build frequency cap rules based on previous impressions
- Competitive Separation: Avoid showing competing brands consecutively
- Enhanced User Experience: Improve ad diversity and relevance
How It Works
- Automatic Tracking: On every
bidResponseevent, the adapter stores minimal information about the winning ad - Lightweight Storage: Data is stored in memory + localStorage (privacy-safe, no PII)
- Targeting Keys: Previous ad data is automatically injected into subsequent bid requests
Data Tracked
| Field | Description | Example |
|-------|-------------|---------|
| creativeId | Winning creative ID | "12345" |
| bidder | Winning bidder code | "appnexus" |
| adUnitCode | Ad unit that served the ad | "sidebar-1" |
| categories | IAB categories of the ad | ["IAB18-1", "IAB12-3"] |
| timestamp | When the ad was served | 1697123456789 |
Targeting Keys Injected
The following keys are automatically added to ortb2Imp.ext.data:
hb_mixpeek_prev_creative- Last creative IDhb_mixpeek_prev_bidder- Last winning bidderhb_mixpeek_prev_adunit- Last ad unit codehb_mixpeek_prev_cat- Last ad categories (comma-separated)
SSP/DSP Usage
SSPs and DSPs can use these keys for advanced targeting rules:
// Example: Avoid showing the same creative twice in a row
if (bidRequest.ortb2Imp.ext.data.hb_mixpeek_prev_creative === currentCreative.id) {
// Skip this creative or reduce bid
}
// Example: Competitive separation
const prevCategories = bidRequest.ortb2Imp.ext.data.hb_mixpeek_prev_cat?.split(',') || []
if (prevCategories.includes('IAB18-1') && currentAd.category === 'IAB18-1') {
// Don't show competing fashion ads back-to-back
}Privacy & Storage
- No User Tracking: Only ad metadata is stored, no user identifiers or behavior
- Session-Scoped: Data persists across page views within a session
- Local Storage: Falls back to memory-only if localStorage is unavailable
- Minimal Data: Only essential fields are stored (< 200 bytes)
- GDPR/CCPA Compliant: No consent required as it doesn't track users
Programmatic Control
You can access the previous ad tracker directly if needed:
import previousAdTracker from '@mixpeek/prebid/utils/previousAdTracker'
// Get last ad info
const lastAd = previousAdTracker.getLast()
console.log('Last creative:', lastAd?.creativeId)
// Clear history (e.g., on user logout or page type change)
previousAdTracker.clear()🔒 Security & Privacy
- No PII: The adapter never sends user identifiers or cookies
- Content-Only: Only page/video content is analyzed
- HTTPS: All API calls use TLS encryption
- API Key Safety: Store API keys securely (environment variables, server-side rendering)
- GDPR/CCPA Compliant: Contextual targeting doesn't require user consent
📚 Documentation
User Guides
- Quick Start - Get running in 5 minutes
- Integration Guide - Step-by-step integration
- API Reference - Complete API documentation
- Testing Guide - How to test the adapter
- Endpoint Configuration - Configure API endpoints
- Health Check - Health check configuration
Developer Resources
- Mixpeek API Docs - Platform documentation
- Internal Planning - Gap analysis & implementation plans (internal)
🤝 Support
- Email: [email protected]
- GitHub Issues: Create an issue
- Documentation: docs.mixpeek.com
- Slack Community: Join our Slack
📄 License
Apache 2.0 - see LICENSE file for details.
🙏 Credits
Built with ❤️ by Mixpeek
Integrates with Prebid.js - an open-source header bidding solution
