Learning Track

Prebid

Master the header bidding auction: adapter setup, price granularity, timeout strategy, and debugging bid failures.

Intermediate track

What this page helps you understand about header bidding

This page assumes you know the general header bidding concept and want to go deeper: how Prebid auctions actually execute, how to configure adapters and price granularity, and how to debug timeout or no-bid issues in real deployments.

What this page covers

  • Prebid.js addAdUnits and setConfig with annotated examples.
  • Auction flow from page load through GAM key-value targeting.
  • Price granularity, user sync, timeout strategy, and common failures.

What is inside

  • Live JavaScript config examples you can run against a test page.
  • Client-side vs server-side (PBS) comparison table.
  • Troubleshooting guide for the five most common Prebid failure modes.

Best way to use it

  • Run pbjs.getBidResponses() in the browser console on any Prebid page.
  • Test setConfig changes one at a time and compare bid rates before/after.
  • Map each config option back to the auction outcome it controls.

Why Prebid matters

Prebid is the open-source header bidding wrapper used by the majority of large publishers. It runs an auction among multiple SSPs/DSPs before the ad server (GAM) is called, ensuring the publisher gets the highest possible CPM. When it is misconfigured — wrong timeout, bad price granularity, stale adapter — publishers leave significant revenue on the table without any obvious error message. TAMs are often the first escalation point for bid discrepancies and timeout complaints from partners.

Prebid.js auction flow — 8 steps

  1. Page loadspbjs.que.push() defers execution until Prebid.js is ready.
  2. addAdUnits fires — defines which slots run header bidding and which adapters are called for each slot.
  3. requestBids is called — Prebid fans out to all bidder adapters simultaneously. The bidderTimeout clock starts.
  4. Adapters contact their SSPs — each adapter converts the Prebid ad unit into an OpenRTB or proprietary request and fires it to the SSP.
  5. Bids return (or timeout) — bids that arrive before bidderTimeout ms are stored. Late bids are dropped with no error shown to end user.
  6. GAM is called — Prebid sets key-values (e.g. hb_pb=2.80, hb_bidder=appnexus) on the GPT ad request via setTargetingForGPTAsync().
  7. GAM auction — the header bidding line item targeting the key-value competes with direct and other demand. If it wins, GAM calls back Prebid to render the winning creative.
  8. Creative renders — the Prebid creative script calls pbjs.renderAd() which serves the DSP’s ad markup into the slot.

addAdUnits — adapter configuration

Defines which ad units run header bidding and which bidders compete. Each bidder has adapter-specific params.

JavaScript pbjs.addAdUnits() — multi-bidder banner + video
pbjs.addAdUnits([
  {
    code: 'div-gpt-ad-300x250',          // Must match the GPT ad slot div ID on the page
    mediaTypes: {
      banner: {
        sizes: [[300, 250], [300, 600]]   // All sizes the slot accepts — more sizes = more demand
      }
    },
    bids: [
      {
        bidder: 'appnexus',
        params: { placementId: 12345678 }  // Find in AppNexus console under Placements
      },
      {
        bidder: 'rubicon',
        params: {
          accountId: 9001,
          siteId: 283,
          zoneId: 1234                             // All three are required for Rubicon
        }
      },
      {
        bidder: 'ix',                              // Index Exchange — requires explicit size param
        params: {
          siteId: '47777',
          size: [300, 250]                // IX does not infer size from mediaTypes
        }
      },
      {
        bidder: 'pubmatic',
        params: {
          publisherId: '158355',
          adSlot: 'homepage-mrec@300x250'          // PubMatic slot name includes size annotation
        }
      }
    ]
  },

  {
    code: 'div-gpt-ad-video',
    mediaTypes: {
      video: {
        context: 'instream',                       // 'instream' = inside video player; 'outstream' = injected player
        playerSize: [[640, 480]],
        mimes: ['video/mp4'],
        protocols: [1, 2, 3, 4, 5, 6],
        playbackmethod: [2],                       // 2 = auto-play with sound on
        skip: 1,
        skipafter: 5                               // User can skip after 5 seconds
      }
    },
    bids: [
      {
        bidder: 'appnexus',
        params: {
          placementId: 98765432,
          video: {
            skippable: true,
            playback_method: ['auto_play_sound_on']
          }
        }
      }
    ]
  }
]);
  • code Must exactly match the GPT slot div ID. A mismatch means Prebid sets key-values on a slot that does not exist — GAM never receives the header bidding data.
  • mediaTypes.banner.sizes Send all sizes the slot accepts. Adapters filter to sizes they support. Sending only [300,250] when the slot can take [300,600] loses demand from buyers who only bid on the larger format.
  • video.context instream means the ad plays inside an existing video player. outstream means Prebid injects its own player into the page content. Setting the wrong context causes adapters to reject or ignore the ad unit.
  • video.protocols Array of supported VAST protocols: 1=VAST 1.0, 2=VAST 2.0, 3=VAST 3.0, 4=VAST 1.0 Wrapper, 5=VAST 2.0 Wrapper, 6=VAST 3.0 Wrapper, 7=VAST 4.0, 8=VAST 4.0 Wrapper. Most DSPs require at least [2,3,5,6].

setConfig — global auction settings

Controls timeout, price buckets, currency, user sync, and identity. Call once before requestBids.

JavaScript pbjs.setConfig() — production example with annotations
pbjs.setConfig({

  bidderTimeout: 1500,                 // ms — adapters not responding by this time are dropped
                                              // Under 800ms = high timeout rate; over 2000ms = slow page

  enableSendAllBids: false,            // false = only winning bid key-values sent to GAM (cleaner)
                                              // true = all bidder key-values sent (hb_pb_appnexus, hb_pb_rubicon...)

  priceGranularity: {
    buckets: [
      { max: 5,   increment: 0.05 },       // $0–$5 in 5-cent steps (100 buckets)
      { max: 20,  increment: 0.10 },       // $5–$20 in 10-cent steps (150 buckets)
      { max: 100, increment: 0.50 }        // $20–$100 in 50-cent steps (160 buckets)
    ]
    // Custom granularity prevents revenue leakage from bucket rounding.
    // A $2.49 bid with 'medium' granularity rounds to $2.40 — 9 cent/CPM loss at scale.
  },

  userSync: {
    syncEnabled: true,
    filterSettings: {
      iframe: {
        bidders: ['appnexus', 'rubicon', 'pubmatic'],
        filter: 'include'                    // Whitelist these adapters for iframe syncs
      },
      image: {
        bidders: '*',                        // Allow all adapters to fire image pixel syncs
        filter: 'include'
      }
    },
    syncsPerBidder: 5,                      // Max sync calls per adapter per page load
    syncDelay: 3000                          // ms after auction before syncs fire — prevents latency impact
  },

  currency: {
    adServerCurrency: 'USD',                // Normalize all bid prices to this for GAM key-value targeting
    granularityMultiplier: 1
  },

  schain: {                                           // Supply chain — required by many DSPs
    validation: 'strict',
    config: {
      ver: '1.0',
      complete: 1,
      nodes: [{ asi: 'your-exchange.com', sid: 'pub-456', hp: 1 }]
    }
  },

  debug: false                               // Set to true in dev — verbose auction log in console

});
Price granularity is the #1 silent revenue leak. Prebid’s built-in “medium” granularity uses $0.10 buckets up to $20. A $4.77 bid becomes the $4.70 key-value. That 7-cent rounding on a high-traffic site compounds across millions of impressions. Always use custom buckets matched to your actual CPM distribution.

requestBids + GAM integration

JavaScript Prebid → GAM handoff with timeout fallback
pbjs.que.push(function () {
  pbjs.setConfig({ bidderTimeout: 1500 });
  pbjs.addAdUnits(adUnits);

  pbjs.requestBids({
    timeout: 1500,
    bidsBackHandler: function (bids) {
      // Called when all bids are in OR timeout fires — whichever is first
      pbjs.setTargetingForGPTAsync();    // Writes hb_pb, hb_bidder, hb_adid to GPT slot targeting
      googletag.pubads().refresh();     // Fires GAM ad request with Prebid key-values attached
    }
  });
});

// === Browser console debugging commands ===
// pbjs.getBidResponses()                   → all bids per ad unit (including status)
// pbjs.getHighestCpmBids()                 → winning bid per unit
// pbjs.getBidResponsesForAdUnitCode('X')   → bids for a specific div ID
// pbjs.getAdserverTargeting()              → key-values set on GPT slots

Client-side Prebid.js vs Prebid Server (PBS)

DimensionPrebid.js (client-side)Prebid Server (server-side)
Where auction runs User’s browser / device Cloud server (self-hosted or via AppNexus, Rubicon)
Page latency Higher — JS executes in browser, each adapter fires separately Lower — one server-to-server call replaces N adapter calls
Cookie sync / ID matching Full browser cookie access — highest match rates Limited — server has no browser cookies; relies on eids
CTV / OTT support Not applicable — no JS runtime on Smart TVs or STBs Primary method — CTV apps call PBS API directly
Adapter count 350+ adapters in the Prebid.js GitHub repo Separate adapter repo — growing but fewer total
Configuration format JavaScript on the page Server YAML config + stored requests in database
Best debugging tool Browser console + Prebid Analyst Chrome extension Server logs + direct /openrtb2/auction endpoint testing

Common Prebid failure patterns

High timeout from one adapter

One adapter consistently times out while others return bids. The adapter is making slow calls, the DSP endpoint is latency-heavy, or the endpoint URL is wrong in the adapter params.

▸ Run pbjs.getBidResponses() — look for “Timed out” on that bidder. Test raising bidderTimeout to 2000 ms. If still timing out, capture a HAR trace and escalate to the adapter team with p95 latency data.

Bids return but GAM ignores them

Prebid wins the auction but GAM serves a lower-priority line item. Usually a key-value targeting mismatch: the line item targets hb_pb=2.80 but Prebid sets hb_pb=2.75 due to bucket rounding.

▸ Open GPT console (googletag.openConsole()) → Targeting tab → confirm hb_pb value. Compare against GAM line item key-value ranges. Adjust price granularity buckets to cover the actual bid values.

Adapter returns $0 or near-zero bid

Adapter is responding but the DSP is not competing. Possible causes: currency conversion failure, floor price mismatch, or DSP deliberately under-bidding to win at near-zero cost.

▸ Check pbjs.getBidResponses() for the adapter. Confirm currency config matches DSP. Compare adapter bid against bidfloor. Contact DSP — confirm their targeting and minimum bid for your inventory.

No bids at all from all adapters

requestBids fires but bidsBackHandler receives an empty bids object. All adapters either timed out or returned no-bids. Usually a config error in addAdUnits or the Prebid.js file failed to load entirely.

▸ Check browser console for JS errors. Confirm pbjs.adUnits is populated. Enable debug: true in setConfig and review the verbose auction log for per-adapter rejection reasons.

User sync not firing

Adapter bids are lower than expected because users are unknown to the DSP. The cookie sync iframe never fired — caused by overly restrictive filterSettings or CMP blocking iframe calls before consent is given.

▸ Check Network tab for /usersync endpoints. Confirm CMP consent includes Purpose 1 (storage). Verify that filterSettings includes the adapter in the iframe or image sync allowlist.

Duplicate creatives or stale bids

Same creative renders twice, or a bid from a previous page load wins the current auction. Caused by enableSendAllBids: true writing multiple conflicting key-values, or bid caching enabled without cache TTL aligned to auction cycle.

▸ Switch to enableSendAllBids: false. If using Prebid cache, set cache TTL to match the page’s expected ad refresh interval. Confirm GAM line item creative rotation is set to “Optimize” not “Even”.

Price granularity presets vs custom

PresetRangeIncrementBucketsUse case
low$0–$5$0.5010Testing only — far too coarse for production
medium$0–$20$0.10200Common default — acceptable for general display
high$0–$20$0.012000Premium display — risk hitting GAM key-value limits
auto$0–$20variablevariesStepped: finer at lower CPMs, coarser at higher
dense$0–$8$0.01–$0.05400Video — finer granularity where bids cluster
customAnyAny per tierYour choice★ Best — match your actual CPM distribution from reports

Practice drills and outputs

Drill 1 — Build a test Prebid page

  • Clone the Prebid.js repo from GitHub and run gulp serve to start a local dev server.
  • Configure 3 adapters (AppNexus, Rubicon, OpenX) on a 300x250 banner unit.
  • Enable debug: true and capture the full auction log from the browser console.
  • Output: prebid-test-log.json — annotate bid, no-bid, and timeout events per adapter.

Drill 2 — Price granularity comparison

  • Run the same test page with priceGranularity: 'medium' and then with a custom 3-tier config.
  • Record the hb_pb key-value set for each winning bid under both configs.
  • Calculate the average rounding loss per bid (actual CPM minus bucket value).
  • Output: granularity-comparison.csv with: actual CPM, medium bucket, custom bucket, delta.

Drill 3 — Timeout rate audit

  • On a production or staging page, run pbjs.getBidResponses() 20 times across different times of day.
  • Record timeout rate and bid latency per adapter. Identify adapters above the 90th percentile for latency.
  • Find the optimal bidderTimeout that captures 95% of bids without adding unnecessary page latency.
  • Output: timeout-audit.md with per-adapter timeout rate and recommended timeout value.

References

  • Prebid.org — full adapter list, setConfig docs, and developer guide
  • Prebid.js GitHub — adapter README files with required params per bidder
  • Prebid Server GitHub — PBS API docs and stored request format
  • Prebid Analyst Chrome extension — live auction inspector in browser DevTools

AdTech Toolkit

Enter any two values
to calculate the third

More tools coming soon