OpenRTB for ad ops and product managers

You do not need to write a bidder to read a bid request. Most revenue questions in programmatic, such as why a partner stopped bidding, why a deal does not deliver, or why two reports disagree, come down to a handful of fields. This page explains those fields in plain terms, the signals buyers filter on, and how to check a payload yourself before escalating.

What ad ops owns

Engineers build the integration, but most of the values inside it come from settings that ad ops and product teams control.

  • Configuration that becomes fields. Floors, deals, blocked advertisers and categories, ad unit sizes and formats, and the auction type.
  • Inventory declarations. ads.txt, app-ads.txt, and sellers.json entries, which buyers compare with what each request says.
  • Partner setup. Which OpenRTB version each partner speaks, which extra fields it needs, and when test traffic is switched to live.
  • Reconciliation. Which notice each side counts as an impression, and in which time zone.
  • Triage. Reading a captured request, a response, or a reason code to decide whether a problem is configuration, demand, or code.

Reading a bid request in two minutes

A bid request is one JSON object that a seller sends to buyers for each ad opportunity. It always has the same parts: the auction rules at the top, the impressions for sale in imp, where the ad runs (site, app, or dooh), the device and user, the regulatory signals in regs, and the sales path in source. Here is a connected TV request for a live sports stream:

{
  "id": "ctv-8f21c4a0",
  "at": 1,
  "tmax": 300,
  "cur": ["USD"],
  "badv": ["rivalbet.example"],
  "imp": [
    {
      "id": "1",
      "bidfloor": 18.00,
      "bidfloorcur": "USD",
      "video": {
        "mimes": ["video/mp4"],
        "plcmt": 1,
        "startdelay": -1,
        "minduration": 15,
        "maxduration": 30,
        "protocols": [7, 8],
        "w": 1920,
        "h": 1080
      },
      "pmp": {
        "deals": [{ "id": "pg-sports-q4", "bidfloor": 32.00, "at": 3, "wseat": ["agency-311"] }]
      }
    }
  ],
  "app": {
    "bundle": "com.example.sportsnow",
    "storeurl": "https://play.google.com/store/apps/details?id=com.example.sportsnow",
    "publisher": { "id": "pub-1180" },
    "content": { "genre": "Sports", "livestream": 1, "language": "en" }
  },
  "device": {
    "devicetype": 3,
    "ip": "198.51.100.77",
    "os": "Android",
    "ifa": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
    "lmt": 0
  },
  "regs": { "coppa": 0 },
  "source": {
    "schain": {
      "ver": "1.0",
      "complete": 1,
      "nodes": [{ "asi": "ssp.example", "sid": "pub-1180", "hp": 1 }]
    }
  }
}
What you seeWhat it means
"at": 1First-price auction: the winner pays what it bid.
"tmax": 300Buyers have 300 milliseconds to answer, network time included.
"bidfloor": 18.00Open market bids under 18 dollars CPM are rejected.
"plcmt": 1, "startdelay": -1Instream video, played in a mid-roll break.
The deal with "at": 3A fixed-price deal at 32 dollars CPM, open only to seat agency-311.
"devicetype": 3Connected TV.
"livestream": 1The content is live, not on demand.
"complete": 1The seller claims the sales path below is the whole path.

A field that is missing is not the same as zero. Many fields have a default, so leaving out at means second price, and leaving out bidfloorcur means US dollars. The spec reading guide explains defaults in more depth.

The fields that move revenue

FieldPlain meaningWhy revenue depends on it
imp.bidfloor, imp.bidfloorcurMinimum price per thousand impressions, and its currencySet too high, bids disappear; a currency mix-up silently changes the floor.
imp.pmp.dealsThe deals offered on this impression, each with its own floor and buyersA deal ID that does not match the buyer's booking never delivers.
atFirst price (1) or second price (2)Buyers shade their bids differently in each; the wrong value misleads them.
imp.banner.format, imp.video.maxdurationThe sizes and ad lengths the slot acceptsEvery extra size or length that fits brings in more eligible demand.
imp.video.plcmtThe kind of video placement: instream, interstitial, and so onBuyers target and exclude by placement, so a wrong value hides inventory.
device.devicetypePhone, tablet, computer, connected TV, and othersCTV campaigns look for 3 (Connected TV) or 7 (Set Top Box).
app.bundle, site.domainWhich app or website the ad runs inBuyer allow and block lists are keyed on these values.
user.eidsIdentity provider IDs for this userWithout a usable ID, fewer buyers bid and those who do bid lower.
tmaxHow long buyers have to answerToo short, and distant buyers time out instead of bidding.
source.schainEvery company in the sales pathBuyers discount or skip paths they cannot verify.

The floors and deals rows are covered in depth by the floors reference and the deals guide.

Signals buyers filter on before they price

A buyer runs gates before it thinks about price. When a gate fails, a well-behaved buyer answers with a no-bid reason code in BidResponse.nbr, which makes the gate visible to you.

GateWhat the buyer checksNo-bid reason when it fails
Valid requestThe payload parses and follows the spec2 Invalid Request
Real trafficUser agent, IP address, and behavior look human3 Known Web Crawler, 4 Suspected Non-Human Traffic, 5 Cloud, Data Center, or Proxy IP
Supported deviceThe buyer runs campaigns for this device or OS6 Unsupported Device
Allowed sellerThe site, app, or publisher is not on the buyer's block list7 Blocked Publisher or Site
Known userA user ID the buyer can match8 Unmatched User
Authorized sellerThe seller appears in the publisher's ads.txt or app-ads.txt11 Ads.txt Authorization Unavailable, 12 Ads.txt Authorization Violation
Verifiable pathThe supply chain is complete and every node is acceptable16 Incomplete SupplyChain, 17 Blocked SupplyChain Node
Enough timeThe time budget covers the buyer's own work15 Insufficient Auction Time

Privacy signals act as a gate too. If regs.gdpr is 1 and the consent string is missing, or a regs.gpp string signals an opt-out, many buyers bid without personal data or not at all. All codes are in no-bid reason codes.

Reading what comes back

Three kinds of feedback reach ad ops. No-bid reasons come from buyers, as above. Loss reasons come from the exchange to each buyer that lost, and buy-side teams use them to tell pricing problems (100 Below Auction Floor, 102 Lost to Higher Bid) from creative problems (the 200 range). Notices count the money: the win notice nurl fires when a bid wins, and the billing notice burl fires when the impression becomes billable.

Most discrepancies between a buyer's report and a seller's report trace back to those two notices. If one side counts wins and the other counts billable impressions, every won ad that never rendered becomes a gap. Agree with each partner which notice defines an impression, and in which time zone the reporting day ends. The loss reason codes guide covers the buyer side of this feedback.

Symptoms and where to look first

SymptomCheck firstUsually means
A deal has requests but no bidsDeal ID, deal floor and its currency, the seat listThe deal setup does not match the buyer's booking
Bid rate fell right after a releaseA captured request in a validator; nbr 2 countsThe request became malformed
A buyer bids in testing but not liveConsent fields, ads.txt lines, supply chainA gate that test traffic skipped
CTV inventory earns mobile pricesdevicetype, plcmt, the app bundleThe inventory is classified as something else
Many losses at 100 Below Auction FloorFloor level and currencyFloors are above what buyers value, or in the wrong currency
Partner reports far more wins than billed impressionsWhich notice each side countsWins counted on nurl, billing on burl
High timeout rate for one partnertmax and the partner's server regionThe time budget is too thin for that route

For the full fill-rate funnel, see why is my fill rate low.

Questions to settle with every partner

  • Which OpenRTB version and dated release do you send or expect?
  • Which extension fields under ext do you require, and where are they documented?
  • Will you return no-bid reason codes during testing?
  • What event fires the billing notice, and do you send loss notices?
  • Which currencies do you accept, and which currency are floors in?
  • How are deal IDs and seat IDs formatted, and who creates them?
  • Which time zone do your reports use?

The answers belong in the integration notes for that partner. Many exchanges publish their own requirements, and the exchange notes collect the documented ones.

Checking a payload yourself

Ask engineering or your partner for a captured request, taken after the seller has added everything it adds. A sample from documentation shows what was intended, not what was sent. Read it against the tables above, then run it through a validator to catch wrong types, unknown fields, and outdated paths. For live checks that cost nothing, a request with test set to 1 runs a full auction that neither side bills.

To see a complete CTV pod request, open the CTV video pod sample in the tester, then paste your captured request in its place.

Questions people ask

Which OpenRTB fields affect revenue the most?

Floors and their currency (imp.bidfloor and imp.bidfloorcur), deal terms in imp.pmp.deals, the auction type in at, the formats on offer (banner sizes, video durations, and plcmt), device.devicetype, the inventory identity in site or app, user identifiers such as user.eids, the tmax time budget, and the supply chain in source.schain. Each one decides how many buyers can bid and what they are willing to pay.

Why do DSP and SSP impression counts differ?

Usually because the two sides count different events. A win notice (nurl) fires when a bid wins the auction, while the billing notice (burl) fires only when the impression becomes billable, typically on render. If one side counts wins and the other counts billable impressions, every won but unrendered ad becomes a gap. Agree that both sides count burl, and agree on the time zone that cuts the reporting day.

How can ad ops find out why a buyer is not bidding?

Ask the buyer to return no-bid reason codes in nbr during testing. Code 2 means the request was invalid, 8 means no usable user ID, 11 and 12 point at ads.txt, and 16 and 17 point at the supply chain. Then check the captured request yourself against the spec and the buyer's documented requirements before changing floors or targeting.