Blog · CTV
CTV runs at 65 percent of commercial capacity. Part of that gap is demand that could not read the request.
Omdia published Video Advertising KPI Benchmarks: Monetization on July 29. Ad-supported CTV could average $0.21 per hour of viewing if every available slot were sold; services are running at roughly 65 percent of commercial capacity and 32 percent of the regulatory ad capacity broadcast linear is permitted. The prescription is to grow the advertiser base to maximise fill rates and to improve advertising context protocols. The second half of that sentence is a bidstream statement, and it is the half nobody is going to write a trade headline about.
A fill rate cannot tell you why
Unfilled is one bucket in a seller's report and at least four different events in reality. Nobody wanted the impression at the floor. Somebody wanted it and ran out of time. Somebody wanted it and could not confirm who was selling it. Somebody wanted it and dropped the request before it ever reached a pricing model, because a field was missing, malformed, or carried a value their integration does not recognise.
Those are not the same problem and only one of them is solved by adding advertisers. OpenRTB has a mechanism for separating them: BidResponse.nbr, the no-bid reason code. It is optional, it is unevenly populated, and most exchanges do not pass it back to the seller in a form anyone reads. Where it is available, three values do most of the diagnostic work.
nbr 2 Invalid Request
The bid request failed the bidder's own validation. This is
demand that exists, refusing inventory it cannot parse.
nbr 6 Unsupported Device
Often a device the buyer would happily buy, described in a way
their device taxonomy does not resolve.
nbr 15 Insufficient Auction Time
Not enough of tmax left by the time the request arrived. A path
problem that reads as a demand problem in every report above it.
nbr 16 Incomplete SupplyChain
schain.complete != 1. The buyer cannot establish authorisation,
so the answer is no regardless of how much they want the spot.None of those four are cured by onboarding another DSP. Three of them get worse as you add hops. If your unfilled bucket is not split by nbr, the 35 percentage points Omdia measured are unattributed in your own data too. Our low fill diagnostic order and the no-bid reason code guide cover the triage.
The two percentages describe different problems
Worth separating before drawing conclusions from either. Sixty-five percent of commercial capacity is a fill number: the slots are on sale and they do not sell. Fixing that alone is about one and a half times, not the two to three the report projects.
Thirty-two percent of linear's regulatory ceiling is a load number: streaming simply runs fewer ad minutes per hour than a broadcaster is legally allowed to. The rest of the upside lives there, and it means more ad slots per viewing hour rather than better fill on the slots that already exist.
For a bidstream, more slots per hour means one specific thing: more pods, and deeper ones. Which is unfortunate, because pod requests are the most structurally complex requests OpenRTB has, and the slot most likely to go unsold is the one whose request is most likely to be wrong.
Pod fields are where CTV requests break
Every field below arrived in OpenRTB 2.6. Each one exists on both imp.video and imp.audio, and each one is capable of producing a request that is internally inconsistent without being syntactically invalid.
podid Ties the imps of one break together. Every imp in the pod
must carry the same value. Different values across the
break means the buyer sees unrelated impressions and
competitive separation logic has nothing to work with.
podseq Position of the pod in the stream. -1 last, 0 any, 1 first.
slotinpod Position the seller can guarantee within the pod.
-1 last, 0 any, 1 first, 2 first or last.
Replaces the deprecated video.sequence. Sending both,
or sending sequence alone, is a live source of confusion.
poddur Total seconds to fill for a dynamic pod.
maxseq Maximum number of ads in that pod.
Dynamic pods need both. Sending poddur without maxseq
leaves the buyer unable to size a bid to the break.
rqddurs Array of exact durations the seller accepts.
MUTUALLY EXCLUSIVE with minduration and maxduration.
Sending all three is the single most common pod error,
and the resolution is bidder-specific, which means the
same request produces different behaviour per buyer.
mincpmpersec Duration-based floor. A floor expressed per second and a
floor expressed per impression are different numbers, and
a bidder that reads the wrong one either overbids or
filters the imp out before pricing.The pattern across all of them is the same. None of these produce a parse failure. They produce a request a bidder can read and cannot act on with confidence, and the safe response to a request you cannot act on with confidence is to pass. Full field detail is in the CTV bid request reference, and the pod mechanics in CTV pod bidding.
The new capacity lands on enums that moved two weeks ago
Here is the part that connects the two halves of this. The load headroom is not going to come from more mid-rolls. Ad-supported tiers compete with an ad-free tier of the same service, and tripling mid-roll minutes is a churn experiment nobody wants to run. It comes from the CTV Ad Portfolio formats: pause, screensaver, overlay, squeezeback, in-scene, plus QR codes on any of them. IAB Tech Lab finalised the signaling guidance for those on July 22.
Those formats are described in a bid request by integers that were added to AdCOM on July 16, in release 1.0-202607.
video.plcmt 5 Pause 6 Screensaver 7 Overlay
8 Squeezeback 9 In-scene
video.pos 8 through 17 (partial screen, corners, frame,
double box, L shape, reversed L shape)
video.playbackmethod 8, 9 initiated by user pausing content
10, 11 initiated by idling
imp.video.battr 19 contains advertiser QR code
21 static visual 22 limited motion
23 full-motion videoA buyer whose value sets predate that release rejects every one of these. Not with an error a seller sees. With a no-bid, or with an nbr of 2 if the seller is lucky enough to receive one.
So the sequence is: the industry identifies its growth in new formats, standardises the signals for them, and every integration that has not refreshed its enum tables since mid-July filters that inventory out on arrival. In the seller's report, that is unsold. In Omdia's framing, it is part of the 35 points. It is not a demand shortfall at all, it is a version skew, and it resolves on a schedule set by whoever updates their catalogue last.
We are not throwing stones from a distance here. Our own value sets predated 202607 and failed valid pause-ad requests until we shipped the update, which we wrote up in full including the four errors we were producing. That post also covers the Content object field names that are wrong in both current tagged AdCOM releases, which is worth reading before you regenerate anything from a tag.
What actually gets a CTV request filtered before pricing
Ranked by how often we see it, and restricted to things a seller controls.
video.placementsent withoutvideo.plcmt.placementhas been deprecated since 2.6-202303. Buyers who have completed the migration readplcmtand find nothing, which reads as unclassified inventory rather than as instream CTV. Send both while partners finish; never send only the old one. Background in placement vs plcmt.- A thin or absent
contentobject. Content-level targeting is most of what a CTV buy is. Withoutcontent.genre,content.title,content.series,content.catwith acattax, andcontent.livestream, a premium episode and an unlabelled loop are the same request. Note thatcontent.livestreamwas redefined in 2.6-202606, covered in live event ad insertion. - Missing
device.ifawith nolmtto explain it. No identifier means no frequency management, and frequency is the complaint every CTV buyer has. Absent-with-a-reason and absent-with-no-reason are treated very differently on the buy side. - Inconsistent
app.bundleacross exchanges. The same CTV app reaching a buyer under three different bundle identifiers through three paths is three separate blocklist and allowlist entries, and the two the buyer has not whitelisted look like unsold inventory. schain.completenot equal to 1. An incomplete chain isnbr16 at a growing number of buyers, and it is a hard filter rather than a discount.tmaxthat does not survive the path. Every hop consumes budget. A request that arrives at the bidder with 40 milliseconds left isnbr15, which is indistinguishable from disinterest in any report that does not carry the code. See tmax and timeouts.
Another classification layer arrives in eight days
The IAB Redefining Media Types standard is in public comment until August 8, 2026. It is explicitly designed to be encoded into OpenRTB bid requests, and its impression-level attributes map onto fields that already exist: the sound-state attribute lands on playbackmethod, which is one of the lists whose definitions were reworded on July 16 while the comment window was open.
The premise of RMT is sound. Buyers and sellers describe video inventory in incompatible vocabularies and a shared classification would help. The risk is the ordinary one for this industry: a new layer of meaning landing on fields that are populated inconsistently underneath, adding a second thing that can be wrong rather than fixing the first. Detail in our RMT walkthrough.
What to do this quarter
- Split unfilled by
nbrbefore you buy a demand-side solution to it. If your exchange does not return the code, ask. An unattributed 35 percent is a number you cannot act on. - Validate a real pod, not a single imp. Every pod field is a cross-impression consistency claim, and validating one impression in isolation tests almost none of it. Paste a full pod into the bid request tester.
- Refresh AdCOM enums to 1.0-202607 on the receive side now. If you buy CTV and reject
plcmt5 through 9 orpos8 through 17, you are declining the formats sellers are actively trafficking, and neither side can see that from their dashboard. - Pick one of
rqddursor the min/max duration pair, never both. This is the highest-frequency pod error we see and it has no consistent resolution across bidders. - Audit
plcmtcoverage across every path you sell through. Not just your direct integration. Curated and resold paths are where the field gets dropped. - Gate it in CI. Validation that runs once during an integration project does not survive the next SDK release. The CI guide covers the setup.
The honest limit of this argument
Bid request quality does not explain 35 percentage points. Floors above the clearing price, genuinely thin demand in smaller markets, and buyers who have not built CTV capability at all are real and probably larger. Nobody has published a decomposition and we are not going to invent one.
What is defensible is narrower and more useful. Some non-trivial share of what a seller books as unsold is demand that arrived, could not classify the request, and passed. That share is measurable with nbr, it is fixable with field-level validation, and it costs nothing to close compared with acquiring an advertiser. Every other line in the 35 percent needs a sales team, a market, or a price change. This one needs a schema check.
The creative-side half of the same report, covering what happens to a CTV slot that fills and then fails to render, is on vastlint.
Sources
OpenRTB and AdCOM are published by IAB Technology Laboratory under a Creative Commons Attribution 3.0 License. Field names and enumerated values are drawn from those documents; the analysis is our own. RTBlint is independent and not affiliated with IAB, IAB Tech Lab, or Omdia.
- Omdia: $0.21 per viewing hour sets monetization benchmark for ad-supported CTV, July 29, 2026
- Advanced Television: report coverage, including the two to three times revenue framing
- AdCOM 1.0-202607, tagged July 16, 2026
- IAB Tech Lab: CTV Ad Portfolio, signaling guidance finalized July 22, 2026
- IAB: Redefining Media Types Standard, public comment through August 8, 2026
- The delivery half: what happens to a CTV slot that fills and fails (vastlint)