Impression Tracking

Praevis Exchange automatically injects impression tracking pixels into all ad responses. This guide explains how to integrate on your end to ensure accurate billing reconciliation.


Overview

Praevis automatically injects impression tracking into all winning creatives (Sprint 0.6.26 + Sprint 0.6.34). Your app aggregator needs to render the tracking pixels to confirm delivery.

Why Impression Tracking Matters

Problem: Discrepancy between auction wins (opportunities) and delivered impressions (confirmed renders).

Solution: Both sides count the same event — ads that actually rendered.

Auction wins:      10,000 impressions (opportunities)
Delivered:          7,500 impressions (confirmed renders)
Billing basis:      7,500 impressions ✓

Result: Billing matches actual delivery

How It Works

Standard Flow (OpenRTB 2.6)

1. App sends bid request to Praevis
2. Praevis runs auction, returns winning creative
3. App receives creative in "adm" field
4. App renders the ad
5. Ad fires impression tracking pixel ← KEY STEP
6. Praevis receives impression confirmation
7. Both sides increment delivered impression counter

Implementation

Banner/Display Ads

Praevis injects a 1x1 transparent tracking pixel at the start of the adm field.

Example response:

{
  "seatbid": [{
    "bid": [{
      "adm": "<img src=\"https://rtb.praevis.com/track/impression?id=abc-123&pub=pub-001&partner=partner-001&dsp=dsp-001&price=2500000\" width=\"1\" height=\"1\" style=\"display:none;border:0;\" alt=\"\" /><div class=\"ad-creative\">Your ad here</div>",
      "price": 2.50
    }]
  }]
}

Your job: Render the complete adm HTML. The tracking pixel fires automatically when the ad loads.

Requirements:

  • ✓ Render the complete adm HTML (including tracking pixel)
  • ✓ Don’t strip out <img> tags or tracking URLs
  • ✓ Fire the pixel when the ad is actually visible (not just downloaded)

Video/VAST Ads

Praevis injects the tracking URL into the VAST <Impression> element (format-aware — doesn’t corrupt XML).

Example VAST response:

<VAST version="3.0">
  <Ad>
    <InLine>
      <Impression><![CDATA[https://rtb.praevis.com/track/impression?id=abc-123&pub=pub-001&partner=partner-001&dsp=dsp-001&price=2500000]]></Impression>
      <Creatives>
        <Creative>
          <Linear>
            <MediaFiles>
              <MediaFile>https://cdn.example.com/video.mp4</MediaFile>
            </MediaFiles>
          </Linear>
        </Creative>
      </Creatives>
    </InLine>
  </Ad>
</VAST>

Your video player should automatically fire the <Impression> URL when the ad starts playing (standard VAST behavior).


Alternative: Server-to-Server Callback

If your app aggregator can’t render HTML pixels (e.g., native ads), use a server callback instead.

When ad renders in the app:

GET https://rtb.praevis.com/track/impression?id={auction_id}&pub={publisher_id}&partner={partner_id}&dsp={dsp_id}&price={price_micros}

Parameters:

  • id - Auction ID (from bid response)
  • pub - Your publisher ID
  • partner - Platform partner ID (if applicable)
  • dsp - DSP ID that won (from bid response)
  • price - Clearing price in micro-dollars (price × 1,000,000)

Example:

curl "https://rtb.praevis.com/track/impression?id=abc-123&pub=pub-001&partner=partner-001&dsp=dsp-001&price=2500000"

Response:

  • HTTP 200
  • Content-Type: image/gif
  • Body: 1x1 transparent GIF (43 bytes)

Testing

1. Send a Test Bid Request

curl -X POST https://rtb.praevis.com/rtb \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "id": "test-123",
    "imp": [{
      "id": "1",
      "banner": {
        "w": 300,
        "h": 250
      }
    }],
    "site": {
      "id": "test-site",
      "publisher": {
        "id": "your-publisher-id"
      }
    }
  }'

2. Check the Response

Look for the tracking pixel in the adm field:

{
  "id": "test-123",
  "seatbid": [{
    "bid": [{
      "id": "bid-456",
      "impid": "1",
      "price": 2.50,
      "adm": "<img src=\"https://rtb.praevis.com/track/impression?id=auction-789&pub=your-publisher-id&partner=partner-001&dsp=test-dsp&price=2500000\" width=\"1\" height=\"1\" style=\"display:none;\" /><div>Your ad here</div>"
    }]
  }]
}

3. Render and Verify

Render the adm HTML in your app. The tracking pixel should fire automatically.

Verify it fired:

curl "https://rtb.praevis.com/track/impression?id=test-789&pub=your-publisher-id&partner=partner-001&dsp=test-dsp&price=2500000"

Expected response:

  • HTTP 200
  • Content-Type: image/gif
  • Body: GIF89a… (binary GIF data)

4. Check Metrics

After 24 hours of live traffic, compare metrics:

Praevis Dashboard:

  • Wins: 10,000
  • Delivered: 7,500
  • Delivery Rate: 75%

Your Dashboard:

  • Impressions Delivered: ~7,500 (should match Praevis delivered count)

Expected discrepancy: <5% (acceptable due to ad blockers, network failures, etc.)


Best Practices

When to Fire the Impression Pixel

✓ Fire when: Ad is actually visible in the app (viewable impression) ✗ Don’t fire when: Ad is just downloaded but not rendered ✗ Don’t fire when: User scrolls past ad before it loads

Industry standard: Fire when ad is 50% visible for 1 second (MRC viewability standard)

Fire Only Once

Each impression pixel should only fire once per ad render.

✗ Don’t: Fire on every scroll, resize, or focus event ✓ Do: Fire once when ad first becomes viewable

Handle Ad Blockers

Some users have ad blockers that may block tracking pixels. This is normal and expected.

Expected impression drop rate: 20-38% (industry standard)

  • ~10-15% from ad blockers
  • ~5-10% from network failures
  • ~5-10% from viewability filters

Code Examples

JavaScript (Web)

// After receiving bid response
const bidResponse = await fetch('https://rtb.praevis.com/rtb', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'your-api-key'
  },
  body: JSON.stringify(bidRequest)
}).then(r => r.json());
 
// Extract ad markup
const adMarkup = bidResponse.seatbid[0].bid[0].adm;
 
// Render ad (tracking pixel fires automatically)
document.getElementById('ad-slot').innerHTML = adMarkup;
 
// Optional: Fire pixel only when ad is viewable
const adElement = document.getElementById('ad-slot');
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
      // Ad is 50%+ visible - tracking pixel fires automatically
      observer.disconnect();
    }
  });
}, { threshold: 0.5 });
observer.observe(adElement);

Swift (iOS)

// After receiving bid response
let adMarkup = bidResponse["seatbid"][0]["bid"][0]["adm"].stringValue
 
// Option 1: Render HTML in WKWebView (tracking pixel fires automatically)
webView.loadHTMLString(adMarkup, baseURL: URL(string: "https://rtb.praevis.com"))
 
// Option 2: Server-to-server callback (for native ads)
if isAdViewable() {
    let trackingURL = extractTrackingURL(from: adMarkup)
    URLSession.shared.dataTask(with: URL(string: trackingURL)!).resume()
}

Kotlin (Android)

// After receiving bid response
val adMarkup = bidResponse.getJSONArray("seatbid")
    .getJSONObject(0)
    .getJSONArray("bid")
    .getJSONObject(0)
    .getString("adm")
 
// Option 1: Render HTML in WebView (tracking pixel fires automatically)
webView.loadDataWithBaseURL(
    "https://rtb.praevis.com",
    adMarkup,
    "text/html",
    "UTF-8",
    null
)
 
// Option 2: Server-to-server callback (for native ads)
if (isAdViewable()) {
    val trackingURL = extractTrackingURL(adMarkup)
    OkHttpClient().newCall(Request.Builder().url(trackingURL).build()).enqueue(...)
}

Troubleshooting

Tracking pixel not firing

Check:

  1. Is the adm HTML being rendered completely? (Don’t strip <img> tags)
  2. Is your app blocking external image requests?
  3. Is Content Security Policy (CSP) blocking rtb.praevis.com?
  4. Are you using HTTPS? (HTTP pixels may be blocked)

Solution: Whitelist rtb.praevis.com in your app’s network security config.

Delivery rate too low (<50%)

Check:

  1. Are ads rendering below the fold? (Fire pixel only when viewable)
  2. Are network requests timing out? (Check app connectivity)
  3. Are you testing with ad blockers enabled?

Solution: Implement viewability detection (fire pixel when ad is 50% visible).

Delivery rate too high (>95%)

Check:

  1. Are you firing the pixel on ad download instead of render?
  2. Are you firing the pixel multiple times per ad?

Solution: Fire pixel only once, when ad is actually visible.


Privacy & GDPR

The tracking pixel is a first-party tracking call (to rtb.praevis.com, not a third-party tracker).

  • No cookies are set
  • No personal data is collected
  • Only aggregate impression counts are stored
  • GDPR compliant (no PII in tracking URL)

Billing Impact

Before Impression Tracking

Praevis Wins:      10,000 impressions
Praevis Charges:   10,000 × $2.50 CPM = $25.00
Publisher Delivered: 7,500 impressions
Discrepancy:       -33% ($8.33 overbilled)

After Impression Tracking

Praevis Wins:      10,000 impressions (tracked for reporting)
Praevis Delivered: 7,500 impressions (confirmed via pixel)
Praevis Charges:   7,500 × $2.50 CPM = $18.75
Publisher Delivered: 7,500 impressions
Discrepancy:       <5% (acceptable variance)

Result: Billing now matches actual delivery.


Support

Questions? Contact [email protected] or visit the dashboard support section.

Need help testing your integration? We can monitor impression tracking on our end and verify correct implementation. Contact support for assistance.