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.

FieldTypeStatusMeaning
mimesstring arrayRequiredSupported 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.
protocolsinteger arrayRecommendedSupported 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.
mindurationintegerRecommendedMinimum ad duration in seconds. Mutually exclusive with rqddurs.
maxdurationintegerRecommendedMaximum ad duration in seconds. Mutually exclusive with rqddurs.
startdelayintegerRecommended0 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.
wintegerRecommendedWidth of the video player in device-independent pixels.
hintegerRecommendedHeight 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.

FieldTypeStatusMeaning
plcmtinteger2.6-202303Placement 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.
placementintegerDeprecatedThe 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.
linearityintegerOptional1 linear or in-stream, 2 non-linear or overlay. Distinct from plcmt: linearity describes the creative form, plcmt describes the context.
posintegerOptionalAd 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

FieldTypeStatusMeaning
skipintegerOptional1 if the player allows the ad to be skipped, 0 if not. Absent means unknown, which is not the same as 0.
skipmininteger, default 0OptionalVideos shorter than this many seconds cannot be skipped, regardless of skip.
skipafterinteger, default 0OptionalNumber 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.

FieldTypeStatusMeaning
podidstring2.6Identifier for the ad pod. Impressions sharing a podid belong to the same break.
podseqinteger, default 02.6Pod position in the stream: -1 last, 0 any, 1 first.
slotinpodinteger, default 02.6Guaranteed slot position within the pod: -1 last, 0 any, 1 first, 2 first or last. Replaces the deprecated video.sequence.
poddurinteger2.6Total seconds available in a dynamic pod, as opposed to per-slot durations.
maxseqinteger2.6Maximum number of ads that may fill a dynamic pod.
rqddursinteger array2.6Exact acceptable durations in seconds. Mutually exclusive with minduration and maxduration, and the field live TV needs because imprecise durations cause dead air.
mincpmpersecfloat2.6Price floor per second of duration for the dynamic portion of a pod.
durfloorsDurFloors array2.6Per-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

FieldTypeStatusMeaning
apiinteger arrayOptionalSupported 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.
deliveryinteger arrayOptionalSupported delivery methods: streaming, progressive, download.
playbackmethodinteger arrayOptionalHow playback starts and whether sound is on, from the AdCOM Playback Methods list.
playbackendinteger2.5What ends playback: video completion, leaving the viewport, or leaving the viewport with a floating continuation.
minbitrateintegerOptionalMinimum bit rate in Kbps.
maxbitrateintegerOptionalMaximum bit rate in Kbps.
maxextendedintegerOptionalMaximum extended ad duration beyond maxduration. 0 means no extension, -1 means unlimited.
boxingallowedinteger, default 1OptionalWhether letterboxing of 4:3 content into a 16:9 window is allowed.
battrinteger arrayOptionalBlocked creative attributes. The CTV Ad Portfolio motion attributes (21 static, 22 limited motion, 23 full motion) are blocked here.
companionadBanner arrayOptionalCompanion display units offered alongside the video ad, each described as a Banner object.
companiontypeinteger arrayOptionalSupported companion resource types: static, HTML, iframe.
sequenceintegerDeprecatedPre-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.