Blog · Protocol
What happens when your stack doesn't support an OpenRTB version? Nothing. That's the problem.
On the creative side of the pipe, a version mismatch has a name: VAST error 102, fired when a player rejects a document version it cannot read. OpenRTB has no equivalent. The spec instructs receivers to ignore fields they do not recognize, and the version itself travels in an optional HTTP header that plenty of integrations never set. So when one side of an integration runs 2.5 and the other sends 2.6, nothing errors, nothing logs, and nothing bids differently on day one. The failure arrives later, as mispriced inventory, missing consent signals, and win rates that drift for no visible reason.
There is no version handshake in the JSON
An OpenRTB 2.x bid request does not carry its version in the body. The spec puts it in an HTTP header, x-openrtb-version: 2.6, and the header is best practice rather than enforced. When it is absent, the receiver guesses. Some exchanges document the guess: Xandr, for example, defaults supply integrations to 2.4/2.5 semantics unless the header says otherwise. Others just parse and hope.
Compare that with the layer below: a VAST document declares its version on the root element, and a player that refuses it fires a trackable error. And the layer above it has nothing at all. Two JSON documents that differ only in which fields their authors knew about are, on the wire, just two JSON documents. OpenRTB 3.0 fixed this with an in-body openrtb.ver field, but 3.0 is a different protocol, and most of the bid stream still speaks 2.x.
The tolerance rule cuts both ways
The missing handshake is not an oversight. The 2.x versioning policy says bidders and exchanges must tolerate new or unexpected fields gracefully, treating unknown attributes and enum values as if they were not there. That rule is why the protocol could evolve from 2.0 to 2.6 without flag days, and why dated snapshots can add fields without version bumps.
The cost is the feedback loop. Ignoring unknown fields means there is no such thing as a rejected field, which means a sender has no way to learn that a receiver dropped half its signal on the floor. A 2.6 request sent to a 2.5 parser is a valid 2.5 request. It just says less than the sender thinks it says.
What actually disappears
OpenRTB 2.6 did two kinds of things to the request: it added fields, and it moved fields. The two fail differently when they meet a 2.5 reader.
Added fields vanish quietly. A 2.5 reader ignores what it never knew:
imp.video.plcmtis skipped, so the buyer classifies your video inventory from the deprecatedplacementfield, or from nothing. The enums do not map 1:1, so instream can price as something else entirely.imp.rwddis skipped, so rewarded inventory trades as standard interruptive video.- The pod fields (
podid,slotinpod,rqddurs,mincpmpersec) are skipped, so a structured CTV pod flattens into unrelated impressions with no duration economics.
Moved fields are worse, because both paths exist. 2.6 promoted several signals from extensions to first-class attributes: source.ext.schain became source.schain, regs.ext.gdpr became regs.gdpr, user.ext.consent became user.consent, user.ext.eids became user.eids. A sender that writes only the new path, talking to a reader that checks only the old one, produces concrete failures:
- The supply chain looks absent or incomplete, and buyers running supply path checks respond with nbr 16 or 17, or just filter you.
- EU traffic arrives at the buyer with, as far as its parser can tell, no GDPR flag and no consent string. Depending on the buyer's defaults, that either kills the bid or processes personal data on traffic that carried a perfectly good TCF string at a path nobody read. One of those outcomes costs money; the other is a compliance incident.
Dual-sending is the coping mechanism, and it drifts
The ecosystem's answer is to write both paths: placement and plcmt, regs.ext.gdpr and regs.gdpr, schain in both homes. It works, which is why three years into the plcmt migration dual-sending is still the norm rather than a transition state.
But dual-sending has its own failure mode: the two copies drift. A pipeline that sets plcmt from new logic and placement from a legacy default is making two different claims about the same impression, and different buyers will price different truths. If you dual-send, the copies need to be generated from one source of truth and checked for agreement, not maintained by two code paths that happened to both survive a refactor.
Which 2.6 do you mean?
Since 2022, "we support 2.6" has been an ambiguous claim. The version number only moves on breaking changes; everything else lands in dated snapshots, and the snapshots are not interchangeable. A stack current to 2.6-202204 has never heard of regs.gpp. One current to 2.6-202505 does not know content.realtime exists.
The June 2026 snapshot sharpened the point by redefining an existing field: content.livestream changed meaning from "live now" to "scheduled or linear." Two parties can both truthfully support 2.6, exchange that field, and disagree about what it says. No unknown-field rule protects you there, because the field is known to both sides. The only fix is naming the snapshot in your integration docs and your validation config.
The only loud failure
There is one place a version gap makes noise: a receiver with strict request validation that rejects the payload outright, answering nbr 2, Invalid Request, or silently 204ing everything you send. That at least shows up as a bidder whose bid rate is zero, which someone eventually investigates. The irony of OpenRTB versioning is that the strict integration that rejects your request is easier to fix than the tolerant one that quietly accepts it, because the strict one tells you.
How to catch a mismatch that refuses to announce itself
- Send the header, always.
x-openrtb-versionon every request, with the version your body actually conforms to. It costs nothing and it is the only in-band claim you get. - Pin the snapshot in writing. Integration docs and partner onboarding should name a dated snapshot (2.6-202505, 2.6-202606), not a bare 2.6. The versions page lists what each snapshot added.
- Audit moved fields in both homes. If you send first-class
schain,gdpr,consent, oreids, check which partners still read only the ext paths, and dual-send from a single source of truth until they migrate. - Watch per-partner metrics across version changes. A parser gap shows up as a step change in one partner's bid rate, win rate, or clearing price while everyone else stays flat. Nothing in the protocol will flag it for you.
- Validate against the version you claim. rtblint is version-aware across 2.0 through 3.0 including the dated 2.6 snapshots, so a field that does not exist in the version you pinned gets flagged before a partner's parser ignores it. Paste a request into the tester or gate it in CI with the CLI.
Sources
The OpenRTB specification is published by IAB Technology Laboratory under a Creative Commons Attribution 3.0 License. Field names and versioning policy referenced here are drawn from it; the analysis is our own. rtblint is independent and not affiliated with IAB Tech Lab.
- OpenRTB 2.6 specification, including the versioning header convention and the unknown-field tolerance policy
- Microsoft (Xandr): Integration with OpenRTB 2.6, documenting the x-openrtb-version header and default version behavior
- OpenRTB 2.x release history: the dated snapshots from 2.6-202204 through 2.6-202606