OpenRTB protocol
The OpenRTB Video object
imp.video is the largest object in a bid request and the one most likely to be filled in from a template written years ago. Exactly one field is required. Most of the rest are optional in the schema and load-bearing in practice, because a bidder that cannot tell what your player accepts will either not bid or bid on assumptions that produce an error at render.
The seven fields that decide whether anyone bids
Only mimes is required. The recommended set is what a buyer actually needs to select a creative, and omitting any of it converts a bid into a guess.
| Field | Type | Status | Meaning |
|---|---|---|---|
mimes | string array | Required | Supported content MIME types, for example video/mp4. The only required field on the object. An empty array is not a value; it means no creative can be selected. |
protocols | integer array | Recommended | Supported creative subtypes from the AdCOM Creative Subtypes list: VAST versions, and whether wrappers are accepted. Omitting it means the bidder is guessing which VAST version you parse. |
minduration | integer | Recommended | Minimum ad duration in seconds. Mutually exclusive with rqddurs. |
maxduration | integer | Recommended | Maximum ad duration in seconds. Mutually exclusive with rqddurs. |
startdelay | integer | Recommended | 0 for pre-roll, a positive number for a mid-roll at that offset in seconds, -1 for a generic mid-roll, -2 for a generic post-roll. |
w | integer | Recommended | Width of the video player in device-independent pixels. |
h | integer | Recommended | Height of the video player in device-independent pixels. |
A note on protocols, since it is the most consequential omission. It carries the VAST versions you parse and whether you accept wrappers. A bidder with no protocols list will send whatever it has, and the resulting version mismatch is the single most common cause of a creative that arrives and never renders. See what happens on a version mismatch.
Two requests that are correct
A web instream pre-roll, carrying the minimum a bidder needs to price it without guessing:
"imp": [{
"id": "1",
"video": {
"mimes": ["video/mp4", "video/webm"],
"protocols": [2, 3, 5, 6, 7, 8],
"minduration": 5,
"maxduration": 30,
"startdelay": 0,
"w": 1280,
"h": 720,
"plcmt": 1,
"placement": 1,
"linearity": 1,
"skip": 1,
"skipmin": 10,
"skipafter": 5,
"playbackmethod": [1],
"api": [7],
"battr": [13, 14]
},
"bidfloor": 4.5,
"bidfloorcur": "USD",
"secure": 1
}]A CTV structured pod, three slots, each its own impression sharing one podid. Note rqddurs replacing the duration range, no skip, and imp.ssai sitting outside the video object:
"imp": [{
"id": "1",
"video": {
"mimes": ["video/mp4"],
"protocols": [7, 8],
"rqddurs": [15, 30],
"startdelay": -1,
"w": 1920,
"h": 1080,
"plcmt": 1,
"podid": "break-2",
"podseq": 0,
"slotinpod": 1,
"mincpmpersec": 0.9,
"api": [7],
"durfloors": [
{ "mindur": 15, "maxdur": 15, "bidfloor": 22.0 },
{ "mindur": 30, "maxdur": 30, "bidfloor": 38.0 }
]
},
"ssai": 3,
"bidfloorcur": "USD"
}]Both send protocols, which is the field most often left out and the one that decides whether the creative you receive can play. The pod example sends placement nowhere, because on a fresh CTV integration there is no legacy buyer to keep alive; the web example sends both during migration.
Placement: plcmt, placement, and linearity
Three fields describe where the ad sits, and they are routinely confused with each other.
| Field | Type | Status | Meaning |
|---|---|---|---|
plcmt | integer | 2.6-202303 | Placement subtype from the AdCOM Plcmt Subtypes list: 1 Instream, 2 Accompanying Content, 3 Interstitial, 4 No Content or Standalone. This is the field buyers filter on. |
placement | integer | Deprecated | The pre-2.6-202303 placement field. Deprecated, not removed, so it still validates. Sending only this one makes your inventory invisible to any buyer filtering on plcmt. |
linearity | integer | Optional | 1 linear or in-stream, 2 non-linear or overlay. Distinct from plcmt: linearity describes the creative form, plcmt describes the context. |
pos | integer | Optional | Ad position on screen, from the AdCOM Placement Positions list. |
The migration from placement to plcmt has been running since 2.6-202303 and is not finished. Send both during transition: plcmt for buyers who have migrated, placement for those who have not, and make sure the two agree. Full detail in placement vs plcmt and the state of the migration in three years of plcmt.
Skip controls
| Field | Type | Status | Meaning |
|---|---|---|---|
skip | integer | Optional | 1 if the player allows the ad to be skipped, 0 if not. Absent means unknown, which is not the same as 0. |
skipmin | integer, default 0 | Optional | Videos shorter than this many seconds cannot be skipped, regardless of skip. |
skipafter | integer, default 0 | Optional | Number of seconds before the skip control appears. |
The distinction that matters: absent skip means unknown, and skip: 0 means the player will not offer a skip control. A buyer treating those as the same thing will send skippable creative into non-skippable inventory, which on CTV is a rejected creative rather than a degraded experience.
Pod fields
Everything here arrived in 2.6 to make CTV ad breaks sellable. The same fields appear on the Audio object, because a streaming audio break has the same structure as a television one.
| Field | Type | Status | Meaning |
|---|---|---|---|
podid | string | 2.6 | Identifier for the ad pod. Impressions sharing a podid belong to the same break. |
podseq | integer, default 0 | 2.6 | Pod position in the stream: -1 last, 0 any, 1 first. |
slotinpod | integer, default 0 | 2.6 | Guaranteed slot position within the pod: -1 last, 0 any, 1 first, 2 first or last. Replaces the deprecated video.sequence. |
poddur | integer | 2.6 | Total seconds available in a dynamic pod, as opposed to per-slot durations. |
maxseq | integer | 2.6 | Maximum number of ads that may fill a dynamic pod. |
rqddurs | integer array | 2.6 | Exact acceptable durations in seconds. Mutually exclusive with minduration and maxduration, and the field live TV needs because imprecise durations cause dead air. |
mincpmpersec | float | 2.6 | Price floor per second of duration for the dynamic portion of a pod. |
durfloors | DurFloors array | 2.6 | Per-duration price floors, each with mindur, maxdur and bidfloor, so a 15 second spot and a 60 second spot can price differently in the same slot. |
Two shapes exist. A structured pod is one impression per slot, all sharing a podid. A dynamic pod is a single impression with poddur and maxseq, letting bidders fill the break with creatives of varying length. More on the CTV context in CTV and ad pods, and on pricing in bid floors.
Technical and creative constraints
| Field | Type | Status | Meaning |
|---|---|---|---|
api | integer array | Optional | Supported API frameworks: VPAID versions, MRAID, OMID, SIMID. An empty or absent list does not mean none are supported, which is why VPAID keeps arriving where it is unwanted. |
delivery | integer array | Optional | Supported delivery methods: streaming, progressive, download. |
playbackmethod | integer array | Optional | How playback starts and whether sound is on, from the AdCOM Playback Methods list. |
playbackend | integer | 2.5 | What ends playback: video completion, leaving the viewport, or leaving the viewport with a floating continuation. |
minbitrate | integer | Optional | Minimum bit rate in Kbps. |
maxbitrate | integer | Optional | Maximum bit rate in Kbps. |
maxextended | integer | Optional | Maximum extended ad duration beyond maxduration. 0 means no extension, -1 means unlimited. |
boxingallowed | integer, default 1 | Optional | Whether letterboxing of 4:3 content into a 16:9 window is allowed. |
battr | integer array | Optional | Blocked creative attributes. The CTV Ad Portfolio motion attributes (21 static, 22 limited motion, 23 full motion) are blocked here. |
companionad | Banner array | Optional | Companion display units offered alongside the video ad, each described as a Banner object. |
companiontype | integer array | Optional | Supported companion resource types: static, HTML, iframe. |
sequence | integer | Deprecated | Pre-2.6 ordering within a pod. Superseded by slotinpod. |
api deserves a warning. An absent list does not mean no frameworks are supported, and plenty of integrations read it that way in both directions. If you do not accept VPAID, say so by sending a list that excludes it rather than by sending nothing.
What the creative does with all of this
Every field above constrains a VAST document that has not been fetched yet. The bid request says the player accepts VAST 4.2 and durations up to 30 seconds; the tag that arrives declares whatever it declares, and nothing in the auction checks that the two agree.
That handoff is covered in VAST and OpenRTB, and the creative side of the check lives at VASTlint.
Validate it
The Video object is where deprecated fields concentrate: placement without plcmt, sequence instead of slotinpod, pod fields typed into ext because the integration predates 2.6. Paste a request into the bid request tester, run the CLI over a corpus in CI, or give an agent the MCP server. Field-level rules are listed in the rule reference.