在程序化广告生态全景中,无论是海量业务角色的日常交互、交易模式所划分的 RTB/PMP 架构,还是围绕 last look 机制展开的拍卖博弈与供应链 schain 的多节点核验,其底层无一例外都建立在同一套数据交互语言之上。面对如此复杂的交易网络与庞杂的对接需求,系统是如何在百毫秒内完成信息握手、价格确认并最终触达受众的?这一切的答案,都藏在 OpenRTB 协议的底层规范之中。
本文是 买侧竞价链路 系列的第 2 篇(协议)。 全系列 9 篇:
- Bidder 竞价服务架构
- OpenRTB 协议精讲
- Bidder 解析层
- Bidder 定向过滤
- RTA 竞价前过滤
- Bidder 频次控制:五维配置与身份降级
- Bidder 频次控制工程篇
- Bidder 预算 Pacing
- Pacing 目标曲线
一句话定位:OpenRTB 是需求方与供给方在实时竞价中互操作的标准协议,本文将系统剖析 bid request 与 bid response 对象模型、基于 at 的结算语义及多维通知回传机制。
总览篇曾将 Bidder 架构拆解为「解析 → 定向 → 召回 → 打分 → 出价 → 排序 → 填充」七级漏斗。作为整个链路的入口,第一级解析层(Parse)的核心任务便是将外部传入的「原始竞价请求」转化为引擎内部的可计算对象,而这份原始请求的格式规范,正是本篇的主角——OpenRTB。
正因如此,本篇将剥离具体业务引擎的工程实现细节,专注于深度剖析 Bidder 每天必须吞吐的这份底层协议本身:探讨一次广告曝光(Impression)机会如何被编码为 bid request,需求方平台(DSP)又是如何构造 bid response 进行应答的。同时,我们还会深入解析交易所的结算规则、不同竞价结果下的多维通知机制、成交价的宏替换原理,以及这套 JSON 对象模型在多年间的演进脉络。
为了保持聚焦,本篇仅在字段层面点到库存的售卖方式与供应链核验等分支话题,不对其进行重复展开。读完本篇再回到解析层,你将清晰地认识到竞价引擎到底在对什么数据进行归一化处理。
读图提示:左侧 BidRequest 的核心对象负责描述广告曝光的具体环境特征与合规约束;右侧 BidResponse 则通过
seatbid[].bid[] 打包了需求方的出价金额、创意内容及通知接收地址。
TL;DR
- OpenRTB 协议:由 IAB Tech Lab 维护,作为需求方与供给方在实时竞价中的互操作基础,它将广告曝光机会与对应出价分别编码为
bid request与bid response两份 JSON 对象,从而免去了海量生态角色间 (N \times M) 的私有对接成本。 - BidRequest 对象:以
imp[](广告位数组)为核心骨架,每个imp声明了四选一的广告形态并辅以bidfloor(底价)约束;其余如site/app、device等顶层对象则共同为出价提供丰富的上下文环境。 - BidResponse 对象:以按买方席位分组的
seatbid[].bid[]为结构主轴,单个出价对象内囊括了price(出价金额)、impid(关联请求中的广告位)、adm(创意标记)及合规校验字段;若不参竞,则返回nbr(no-bid reason)说明原因。 - 拍卖结算类型(at):直接决定最终的成交逻辑,
at=1代表行业当前默认的一价结算,at=2则是传统的二价结算。需要强调的是,拍卖过程与定价权始终由交易所全盘掌控。 - 创意交付方式:现代工程实践普遍采用直接在响应中内联
adm的方式以优化全链路延迟,而依赖nurl在胜出后拉取创意的传统方案已逐步退出历史舞台。 - 通知机制与 notice URL:
nurl承载了赢得拍卖后的初步回调功能,burl将计费确认严格延后至实际有效曝光阶段,而lurl则专门向需求方传递由于低价或过滤导致的输单原因。 - 成交价回填宏(${AUCTION_PRICE}):允许交易所在触发回调前,将特定占位符替换为真实的加密成交价,彻底解决了二价机制下需求方无法精准预知结算成本的业务痛点。
- 扩展机制(ext):作为协议平滑演进的重要保留出口,它支持各家交易所挂载私有业务逻辑,但同时也成为了后续竞价引擎归一化处理中的主要适配负担。
- 版本演进路线:2.5 版本奠定了当代工程基线,2.6 版本重点收编了隐私合规与供应链透明度体系,而 3.0 则利用 AdCOM 实现了传输层与通用广告对象模型在架构上的彻底分离。
Table of contents
Open Table of contents
1. 为什么需要一份协议:从「两两私约」到 OpenRTB
回顾在 last look 深挖中所提及的发展历程:在实时竞价起步之初,每一家需求方在接入不同交易所时,都必须私下约定请求的数据结构、字段命名以及货币单位。随着整个生态中需求方与供给方数量的激增,这种原始的对接模式迅速演变为一场灾难性的 (N \times M) 矩阵陷阱——接入一个新的流量源几乎等同于重写一遍解析模块。
正因如此,OpenRTB 协议应运而生。它所发挥的底层作用与 HTTP 类似:通过建立一套业界公认的标准接口,将巨大的对接成本坍缩为 (N + M)。在此框架下,DSP 仅需实现一次标准解析方案,便可无缝对接所有遵循该规范的交易所。自 2010 年起,IAB Tech Lab 持续迭代这份规范,使其成为了支撑当今每日数以万亿计广告竞价的坚实基座。
从本质上看,OpenRTB 仅仅聚焦并规范了两项核心内容:
- 广告曝光机会的数据结构:即由交易所向 DSP 广播的
bid request。 - 出价响应的数据结构:即由 DSP 向交易所作答的
bid response。
除此之外,它还定义了一套标准的结算与通知语义(涵盖胜负判定、成交价格确认及回调逻辑),并预留了 ext 扩展机制供各家容纳私有创新属性。正是这套精简且高度可扩展的体系,保障了整个商业化链路的顺畅运转。
需要明确的是,OpenRTB 采用的是一套「交易所主持,需求方应答」的权利不对等模型。在这一流程中,交易所会将同一份请求广播给全网多个 DSP,并在收集齐所有响应后独立执行拍卖逻辑,单方面决定最终的胜者与结算价格。由于 DSP 始终只能看到属于自己的那份请求与出价,这种协议机制客观上也为 last look 类信息不对称策略提供了存活的土壤。
2. BidRequest:把一次曝光机会编码成 JSON
在探究具体字段语义前,我们先来看一份精简但结构齐全的真实请求形态(省略号处为非核心信息):
{
"id": "1a2b3c-req-0001",
"at": 1,
"tmax": 120,
"cur": ["USD"],
"imp": [
{
"id": "1",
"tagid": "homepage-top",
"bidfloor": 0.85,
"bidfloorcur": "USD",
"secure": 1,
"banner": { "w": 300, "h": 250, "pos": 1 },
"pmp": {
"private_auction": 0,
"deals": [
{ "id": "DEAL-42", "bidfloor": 2.5, "at": 1, "wseat": ["agencyX"] }
]
}
}
],
"app": {
"bundle": "com.example.news",
"publisher": { "id": "pub-9" },
"cat": ["IAB12"]
},
"device": {
"ua": "Mozilla/5.0 ...",
"ip": "203.0.113.7",
"ifa": "6D92078A-8246-...",
"lmt": 0,
"os": "iOS",
"devicetype": 4,
"geo": { "country": "USA", "type": 2 }
},
"user": { "id": "ssp-side-user-77", "buyeruid": "dsp-side-user-abc" },
"source": { "fd": 1, "tid": "txn-555", "schain": { "...": "见 schain 专文" } },
"regs": { "coppa": 0, "ext": { "gdpr": 0, "gpp": "DBABzw~..." } }
}
这份 JSON 的顶层字段和嵌套的子对象,实质上都在回答关于「本次广告曝光环境」的具体特征。我们可以将其归纳为以下维度:
| 对象 / 字段 | 回答的问题 | 关键子字段(挑重点) |
|---|---|---|
id | 请求唯一标识 | 由交易所生成,DSP 在响应中必须原样带回 |
imp[] | 要竞价的广告位目标 | 请求的绝对核心,详见 §2.1 |
at | 采用的拍卖结算机制 | 取值 1 为一价,2 为二价+;详见 §4 |
tmax | 最大允许处理耗时 | 交易所设定的时间预算,超时的响应将被直接丢弃 |
cur[] | 支持的结算货币体系 | 允许的货币列表,通常默认值为 USD |
site / app | 曝光发生的媒体载体 | 网页环境使用 site,应用端使用 app 且二者互斥;包含 domain / bundle 与 cat 品类 |
device | 终端与网络环境特征 | 包括 ua、ip / ipv6、ifa 广告标识、lmt / dnt 限制跟踪状态及 geo 地理位置 |
user | 触达的具体受众身份 | 包含 id(供给方视角)与 buyeruid(需求方视角),以及 data[] 人群标签 |
source | 链路来源与流转节点 | 涵盖 fd 最终决策标识、tid 事务编号及 schain 供应链核验追踪 |
regs | 适用的隐私法规约束 | 涉及 coppa,以及 ext 挂载的 gdpr 与 gpp 隐私串 |
test | 流量测试属性标识 | 取值为 1 时表示处于测试状态,不应产生实际扣费 |
bcat/badv/bapp | 媒体侧的拦截过滤策略 | 用于全局屏蔽特定的广告品类、广告主落地域名或竞品应用 |
wseat/bseat | 席位参与权限控制 | 明确规定允许或拒绝参与本次竞价的白/黑名单席位 |
ext | 规范外的私有扩展容器 | 容纳各家交易所专属创新字段的保留出口,也是方言适配的重灾区 |
接下来,我们将重点拆解请求构建中最易引发工程缺陷的三个核心设计:imp 的内部构造、site 与 app 的互斥关系,以及 user 体系中容易混淆的身份映射机制。
2.1 imp:请求的核心,一个广告位一个
在整个 bid request 中,所有的顶层属性都是在为 imp(广告曝光位)铺垫上下文,因为后者才是真正进行商业化售卖的实体标的。尽管单次请求能够并行携带多个 imp 以支持复杂页面的批量竞价,但在高并发流媒体场景下通常只包含单一对象。在 imp 节点内部,最具决定意义的是以下四选一的媒介形态:
banner:传统展示或图片广告,通常附带w/h的绝对尺寸规格,或以format[]提供多尺寸候选列表,并辅以pos字段标明是否处于首屏高价值区。video:流媒体视频广告,其深层参数定义了mimes(支持的编码格式)、minduration/maxduration(播放时长上下界),以及protocols(兼容的 VAST 标准版本)。audio:数字音频广告,广泛应用于播客或音乐流媒体插播场景。native:原生信息流广告,通过内嵌的native.request节点传递一份高度定制的资产组装清单,精确指明标题长度、主图比例及行动号召按钮(CTA)的渲染规格。
除此之外,还有两个直接干预买方出价策略的业务级控制字段:
bidfloor与bidfloorcur:确立了本次拍卖的起拍底价。如果需求方后续报出的价格无法越过这一硬性门槛,将直接在初筛阶段被判定出局。pmp:私有交易容器对象。其中的deals[]数组封装了前期线下谈判达成的交易条件,每个 deal 均设有专属的id(即行业通用的 Deal ID)与特定的wseat准入名单。更为严格的是,当配置项private_auction=1开启时,该广告位将彻底对公开实时竞价关闭大门,仅允许持有对应 Deal ID 的席位参与。这正是将传统排他性流量售卖「激活」进程序化链路的关键所在。
重点风险提示: 在研发竞价解析模块时,开发人员必须极其严谨地区分 imp.id 与全局 bid request.id。前者仅仅是当前请求内对应具体广告位的局部编号,必须与最终响应里的 bid.impid 精准缝合;一旦发生越界混用,将导致整个链路的串号崩溃。
2.2 site vs app:二选一,别同时出现
由于一次真实的广告曝光只能依托于一个确定的物理宿主环境,协议层通过硬性约束确保 site(网页端)与 app(移动应用或智能终端)对象绝对互斥,两者绝不可在同一请求中并存。虽然它们的结构树高度相似——均挂载了代表媒体主体的 publisher、内容特征的 content 以及所属行业的 cat 标签,但两者在界定自身来源真实性时存在着本质的区别:
site.domain/site.page:对外暴露出网页的顶级域名与具体路由。由于 Web 环境的开放性使得域名极易遭到恶意篡改与仿冒,所以流量质量攻防战往往聚焦于这个核心字段。app.bundle/app.storeurl:对外传递应用的底层包名与官方商店链接。行业权威的app-ads.txt规范正是利用bundle标识跨越网络去反查开发者官网,以此建立起闭环的授权核验链路。
对比这两种环境标识,它们不仅决定了防作弊策略的走向,更直接指示了买方系统后续应当挂载哪套归因链路:Web 流量深度绑定 Cookie 与广告主自身的 Ad Server 体系,而 App 流量则无可避免地需要依赖设备层标识(如 IDFA 或 OAID)并协同第三方移动归因平台(MMP)进行转化追踪。
2.3 user.id vs user.buyeruid:cookie matching 的产物
在 user 节点中并列存在的两套用户标识,往往是初涉广告研发者的理解盲区。事实上,它们分别代表了同一自然人在不同利益主体眼中的数字身份:
user.id:完全属于供给方(SSP / 交易所)域内的受众标识映射。user.buyeruid:专属需求方(DSP)域内、用以反查本方画像库的受众标识映射。
为何必须维持这种双向冗余结构?根本原因在于买卖双方各自圈禁了独立的用户数据孤岛,缺乏天然互通的追踪介质。为了打通这一商业闭环,双方必须通过前置的 Cookie Matching(Cookie 同步)流程,利用追踪像素在客户端发生高频碰撞,从而在云端沉淀出一张庞大的 A ↔ B 标识映射表。因此,当交易所向某家特定 DSP 广播竞价请求时,便会主动从该映射表中提取属于对方的 buyeruid 进行回填。DSP 接收后,无需执行复杂的跨域比对,即可直接利用该标识检索出高价值人群标签,进而完成严苛的频次控制与出价决策。
然而需要强调的是,随着主流浏览器接连清退第三方 Cookie,这套高度依赖前端像素碰撞的标识同步机制正面临着匹配率雪崩的系统性风险。正因如此,放弃设备标识共享、仅回馈实时决策建议的 RTA 模式才会在近几年迅速崛起。虽然这些底层字段依然驻留在协议树中,但它们所能撬动的确定性红利正在被重塑。
3. BidResponse:把一次出价编码成 JSON
当需求方在有限的 tmax 窗口内完成策略运算后,需向交易所发回一份标准化的 bid response 响应报文:
{
"id": "1a2b3c-req-0001",
"cur": "USD",
"seatbid": [
{
"seat": "agencyX",
"bid": [
{
"id": "bid-001",
"impid": "1",
"price": 3.20,
"adm": "<div>...creative markup...</div>",
"adomain": ["example-brand.com"],
"crid": "creative-88",
"cid": "campaign-12",
"cat": ["IAB19"],
"w": 300,
"h": 250,
"dealid": "DEAL-42",
"nurl": "https://dsp.example/win?imp=${AUCTION_IMP_ID}&price=${AUCTION_PRICE}",
"burl": "https://dsp.example/bill?imp=${AUCTION_IMP_ID}&price=${AUCTION_PRICE}",
"lurl": "https://dsp.example/loss?imp=${AUCTION_IMP_ID}&reason=${AUCTION_LOSS}"
}
]
}
]
}
响应包的顶层必须通过 id 字段将本次作答精准锚定回原始请求。而实质性的业务逻辑则全面下沉至 seatbid[] 数组体系中:
seatbid层级:该数组强制要求按买方席位(seat)为粒度进行数据分组。由于一个大型 DSP 往往同时代理由多家不同资质的广告主或代理商,这种分层设计能让交易所有序追踪资金来源。同时,它还可以利用group字段配置高级打包策略,强制宣示「该组内包含的所有出价必须被视为一个整体,要么全盘中标,要么全盘流局」。bid层级:具体落实到某一广告位的细分出价载体。其内部涵盖了从价格博弈到合规校验的全部参数约束:
| 字段 | 核心业务含义 | 工程备注说明 |
|---|---|---|
impid | 出价锁定的目标广告位 | 必须严丝合缝地等于请求中某个具体的 imp.id |
price | 竞买出价金额(CPM) | 结算基准采用外层 cur 指定的货币单位,代表每千次曝光价格 |
adm | 真实渲染的创意标记片段 | 容纳 HTML / VAST 标签或原生 JSON 资产,有关其内联交付机制详见 §5.1 |
nurl | 赢得拍卖后的初步回调 | 常用于回传加密价格,详见 §6 |
burl | 延后至渲染阶段的权威通知 | 2.5 版本引入的财务结算基准点,详见 §6 |
lurl | 竞价落选时的复盘回调 | 携带输单原因码以反哺模型优化,详见 §6 |
adomain[] | 广告主落地域名白名单 | 执行品牌安全审核与竞品隔离的核心依据,投放中几乎必填 |
crid | 物料创意唯一 ID | 交易所依赖此字段执行创意级审核及 CDN 缓存策略 |
cat[] | 该条出价所属的行业品类 | 必须确保避开媒体在 bcat 中下发的行业拦截禁区 |
w/h | 物料创意的物理尺寸 | 严格校验是否落入 imp 划定的合法尺寸框架内 |
dealid | 命中的私有交易专属标识 | 必须与上游的 imp.pmp.deals[].id 精准咬合,是非公开竞价结算的纽带 |
倘若 DSP 在评估后判定该流量毫无价值,规范强烈建议直接舍弃 JSON 包体并返回 HTTP 204 No Content 以极致压榨网络延迟。或者,买方也可选择返回一个挂载 nbr(no-bid reason)标识的空对象以阐明弃考原因(诸如 1 内部引擎超时、3 判定为爬虫流量、8 缺乏画像映射等)。主动暴露 nbr 能够极大地协助供给方开展流量健康度诊断,但这亦意味着少量的额外带宽消耗,具体是否落地通常由双方在商务联调阶段敲定。
4. 交易所怎么结算:at 拍卖类型与成交价
这部分往往是最容易被后端新人一扫而过,却又是掌控整个商业化变现生命线的核心地带:DSP 仅仅享有提报 price(出价金额)的权利,而最终究竟谁能胜出、以及按照何种价格进行扣款交割,其独家裁量权完全掌握在交易所手中。请求包中的 at(auction type)字段便是一切结算规则的最高指示:
at | 结算判定规则 | 真实成交价逻辑 | 当前生态演进现状 |
|---|---|---|---|
1 | 一价结算(First Price) | 赢家按自身报出价格交割 | 自 2019 年起全面接管市场成为行业标配 |
2 | 二价结算(Second Price Plus) | 赢家支付次高出价加最小增量 | 属于历史遗留产物,仍有零星存量规模 |
3 | 绝对固定价 Deal (历史) | bidfloor 即为固定的结算金额 | 该机制已被彻底废弃,已迁移至 pmp 容器内部表达 |
关于一价与二价机制之间惨烈的商业博弈,以及为何整个广告生态最终会从二价集体倒向一价结算的深层推手,last look 深挖一文已进行了透彻的降维拆解。在此,我们仅需牢记协议层面的两大铁律:
首先,在二价结算机制的庇护下,成交价绝对不等于出价。这就引发了一个极为现实的工程痛点:当一家 DSP 激进地报出 $3.20 并在拍卖中获胜后,它根本无从知晓自身究竟将被扣取 $3.20 还是仅仅 $1.50。为了跨越这道信息鸿沟,它必须仰赖交易所大发慈悲,在事后的回调中通过 ${AUCTION_PRICE} 宏将真实的清算价格坦诚相告。
其次,底价(Bidfloor)是一道生死攸关的硬性准入门槛。无论采取何种拍卖机制,参竞方的出价必须绝对 ≥ imp.bidfloor(针对公开实时竞价)或者 deal.bidfloor(针对私有专属交易),否则其报价单连被引擎载入内存排序的资格都没有。这种直接被无情踢出局的情况,将严丝合缝地映射为输价原因码体系中的 100 Bid Below Auction Floor 或是 101 Bid Below Deal Floor。
读图提示:从广播到应答、从交易所内部清算到最终的三维通知流转,注意
tmax 是一道无情的物理闸门,晚到即作废;同时也要深刻认识到,拍卖动作完全在交易所侧封闭进行,DSP 仅具备局部视野。
5. 创意怎么送达:adm 内联 vs nurl 拉取
当一家 DSP 成功杀出重围赢得拍卖后,那段承载着精美素材、即将渲染在用户屏幕上的创意标记(markup)究竟该如何穿越重重链路安全送达?OpenRTB 在底层架构上支持两种截然不同的交付范式,理解它们在延迟与可靠性上的利弊权衡至关重要:
5.1 adm 内联机制(现代主流实践)
DSP 在下发 bid response 时,直接将庞大的创意片段完整地内嵌于 adm 字段之中。一旦判定其获胜,交易所由于已经提前将素材握在手中,能够毫无停滞地将其直推至客户端触发渲染。在这种架构下,nurl 的职责被大幅削弱,纯粹降级为一条用于回传成交明细与胜利打点的轻量级通知通道。
对比前一种方案,内联交付彻底免去了一次横跨公网去索要创意的网络交互(RTT),在延迟优化上达到了极致;更为关键的是,它彻底封死了「赢了拍卖却因为网络抖动拉不到素材」这一引发财务损失的致命失败面。当然,这种设计的代价也是显而易见的:无论最终胜负如何,每一条竞价响应都必须无可避免地背负沉重的完整素材包体,从而导致带宽消耗急剧膨胀。然而,在现代网络基础设施的加持下,用区区带宽换取渲染成功率与低延迟几乎总是一笔极其稳赚不赔的买卖。
5.2 nurl 拉取机制(已退居幕后的传统方案)
在早期带宽极为金贵的年代,DSP 倾向于在初次应答时省略臃肿的 adm,仅仅抛出一个轻量级的 nurl 地址作为钩子。直到交易所正式宣布其赢得拍卖并主动发起回调时,DSP 才在这二次网络请求的响应流中慢吞吞地吐出真实的创意片段。
需要强调的是,这种将极其脆弱的「拉取素材」动作强行塞入毫秒级曝光关键路径上的做法,不仅平白无故地增加了一跳 RTT 延迟,更是人为制造了一个极易崩溃的超时熔断点。一旦出现「赢下单子却因 nurl 超时而渲染空白」的惨剧,那就意味着真金白银的纯粹收入损耗。发展至今,除非存在高度特殊的分发架构,业界已全面倒向内联机制。nurl 的历史使命早已从「运送庞大创意」退化为「运送状态通知」,切勿再让它去承担无法承受的交付重任。
6. 三条 notice URL:win / billing / loss 各打各的点
这是 OpenRTB 演进史中最能体现「标准制定者曾在泥坑中挣扎过」的一组精巧设计。为什么非得大动干戈地拆分出三条独立的回调 URL?其核心底层逻辑在于:「赢得拍卖」、「真实产生计费曝光」与「在竞价中惨遭淘汰」完全是处于截然不同的生命周期阶段、且背负着完全不同业务使命的孤立事件:
| 通知机制 URL | 工程触发时机 | 核心业务落地场景 | 强关联的高价值宏替换节点 |
|---|---|---|---|
nurl | 赢得拍卖的瞬间即刻触发 | 快速回传清算价格,支撑乐观预算预扣 | ${AUCTION_PRICE} |
burl | 广告完成真实渲染并被有效度量时方可触发 | 财务对账与预算核减的绝对权威时点 | ${AUCTION_PRICE} |
lurl | 在激烈竞价中惨遭落选时触发 | 获取输单根因,作为模型优化的负反馈信号 | ${AUCTION_LOSS} |
6.1 为什么把 nurl(赢)和 burl(计费)拆开
将这两者强行剥离,正是 2.5 版本引入 burl 的最深层业务动机:「在极速的服务器竞价中拔得头筹」,与「在脆弱的用户终端设备上产生了一次可被计费的有效曝光」,两者之间横亘着一条巨大的漏斗鸿沟。在极为复杂的现实场景中,一条广告极有可能因为用户手指划走太快、播放器组件渲染崩溃,或者是根本没有进入屏幕可视区域(viewability 标准未达标),而最终无法转化为任何商业计费。
在早期仅存在单一 nurl 的荒蛮时代,大量需求方错误地将其充当计费的绝对锚点,从而导致自身认定的「纸面赢价花费」与供给方确认的「真实曝光账单」之间出现了灾难性的数据错位。正因如此,2.5 版本果断地将二者的业务语义进行了解耦切割:nurl 维持原样,一旦在服务器端确立胜势便火速触发,极其适合用来回传价格并执行宽松的预算预扣;而 burl 的触发点则被极其严苛地向后推迟,唯有当交易所接收到客户端的权威信标、确认本次曝光符合全部计费门槛时,才会慎重地予以触发。
对于奋战在第一线的 DSP 工程团队而言,必须牢记一条铁律:预算的最终冻结与扣减必须且只能以 burl 的触达为准绳,nurl 仅仅只能作为前置的辅助参考指标。倘若越界滥用,必然会将那些「赢了但完全没被用户看见」的虚假曝光也粗暴地统计进财务花费中,最终导致整个投放系统的效果评估体系彻底崩盘。
6.2 成交价靠 ${AUCTION_PRICE} 宏回填
再次回到 §4 中提出的那个尖锐痛点:在二价结算的黑盒下,DSP 到底该如何获知自己真实的采买成本?行业给出的标准解法便是极其巧妙的「宏替换机制」:DSP 需要提前在其下发的 nurl 或 burl 链接中预埋 ${AUCTION_PRICE} 这个标准占位符,而交易所则会在向外发起 HTTP 回调前的一瞬间,精准地将其替换为底层的真实结算价。
DSP 提交初始占位: https://dsp.example/win?imp=${AUCTION_IMP_ID}&price=${AUCTION_PRICE}
交易所执行替换回调:https://dsp.example/win?imp=1&price=3.05 ← 宏已被替换为最终清算价
为了防范买方利用明文价格恶意反推竞争对手的出价策略,亦或是抵御中间节点的恶意篡改,${AUCTION_PRICE} 往往会被交易所施加强度极高的密码学加密。DSP 在接收到这段密文后,必须调用事前线下互换的解密密钥与校验算法将其还原。除此核心宏之外,协议还附赠了一整套周边配套的占位符家族,以丰富回调上下文:
${AUCTION_ID}、${AUCTION_BID_ID}、${AUCTION_IMP_ID}:帮助后端严密对齐当前通知隶属于哪一场具体的拍卖、对应哪一份出价单以及锁定哪一个具体的物理广告位。${AUCTION_SEAT_ID}、${AUCTION_AD_ID}:明确指引是哪一个买方席位或是哪一份具体的物料创意最终脱颖而出。${AUCTION_CURRENCY}:申明本次结算所采用的底层货币基准。${AUCTION_MBR}:即 Minimum Bid to win 字段,它坦陈了恰好能够赢下本场竞价所需要的极限最低出价,是极其珍贵的价格弹性分析素材。${AUCTION_LOSS}:详尽的输价原因识别码,该宏仅在挂载于lurl通道中时才具备实质意义。
6.3 lurl:输了也要知道为什么
lurl(loss notice,同样于 2.5 版本正式服役)是交易所向 DSP 仁慈施舍的负反馈渠道。当买方在激烈的搏杀中败下阵来,交易所便会通过触发它并慷慨地附赠 ${AUCTION_LOSS} 原因码,以此帮助落榜者定位死因。以下是几种最高频出现的核心码段:
| 错误识别码 | 底层拦截原因 |
|---|---|
0 | 状态异常扭转(实际上赢得了竞价) |
1 | 遭遇交易所内部系统灾难性错误 |
2 | 本次物理曝光机会已无可挽回地过期 |
100 | 报价寒酸,甚至低于公开的起拍底价 |
101 | 报价无法满足私有 deal 划定的专属底线 |
102 | 报价尚可,但不幸输给了极其财大气粗的更高价 |
103 | 输给了一个享有特权的 PMP 私有交易通道 |
2xx | 惨遭过滤(因行业品类冲突、尺寸越界或黑名单域名等触怒审核规则) |
对于驱动 DSP 运转的核心出价模型而言,lurl 所带回的情报堪称价值连城。当系统频繁接收到「输给更高价(102)」的死因报告时,意味着算法应当考虑适度上调溢价系数;而一旦被「创意被过滤(2xx)」的警告刷屏,则强烈暗示着当前的投放素材在合规层面存在致命缺陷。这两种截然不同的优化路径,如果不借助 lurl 进行清晰导航,DSP 便只能依靠赢单数据在黑暗中盲目反推,其算法盲区将被无限放大。
7. ext:协议的扩展逃生口,与「方言」的由来
OpenRTB 留有一个堪称神来之笔的架构规定:协议树中的几乎每一个节点对象,都允许挂载一个名为 ext 的开放扩展字段,专门用于容纳那些尚未被官方规范收录的私有业务逻辑与试验数据。这正是这套古老的协议能够在一日千里的广告技术界屹立十几年不倒的关键生存策略:它完全摒弃了每诞生一个新概念就必须劳民伤财去升级大版本的僵化思路。交易所完全可以先在 ext 这个自留地里野蛮生长,待相关业务模式在实战中彻底跑通并趋于成熟后,IAB 才会审慎地考虑将其提拔、收编进下一代的主干规范之中。
纵观历史进程,大量如今不可或缺的「正式嫡系字段」,其早年均脱胎于 ext 这片试验田:
- GDPR consent:在合规风暴初临时,它曾被迫委身于
user.ext.consent与regs.ext.gdpr之下。 - 供应链核验机制(schain):它最早蛰伏在
source.ext.schain节点中,直到 2.6 版本的发布才得以正式转正至一等公民source.schain(详见 schain 专文)。 - GPP 隐私合规串(Global Privacy Platform):目前仍以
regs.ext.gpp的身份,逐步演化为跨越多个法域隔离的通用隐私载体。
然而,凡事皆有代价,ext 同样是导致整个生态充斥着无序「方言」的万恶之源。面对同一个诸如「可见度检测(viewability)」的业务概念,激进的交易所 A 可能会将其粗暴地塞进 imp.ext.viewability,而保守的交易所 B 则偏爱嵌套进 imp.ext.pm.viewability,两者的字段命名约定与数据树深度截然不同。这正是前文强调 Bidder 解析层必须耗费海量算力去做方言脏活与归一化清洗的根本原因:只有将各路群雄私藏在 ext 里的非标黑话,强行映射为竞价引擎内部那套唯一、整洁的标准模型,下游的定向与召回模块才能做到心无旁骛,再也无需去挂虑这股流量究竟是发源于哪家具体的交易所。
概括而言,ext 既是 OpenRTB 最值得称道的架构亮点,也是它强加给所有开发者的残酷生态税。它赋予了协议无尽的柔韧度以包容整个行业的野蛮演进,却也逼迫着每一家渴望扩张的买方,都必须为「对接第 N 个全新的交易节点」缴纳一份沉重的归一化代码适配成本。
8. 版本演进:2.5 → 2.6 →(3.0 / AdCOM)
在当前的工程实战中,你所遭遇的协议报文几乎清一色都会打上 2.x 的时代烙印。但梳理其背后的演进主轴,能够极大地提升你在汪洋代码中定位冷僻字段的嗅觉:
- 2.5(2016 落地):这无疑是彻底夯实当代工程地基的里程碑版本。它极具前瞻性地引入了
burl与lurl的双轨通知机制(详见 §6),构建了metric与source溯源体系,并制定了一整套通行全网的无出价与输单原因码。直至今日,海量中腰部交易所依然死守着以 2.5 为基准的协议红线。 - 2.6(2022 启动并持续微调):该阶段的核心使命便是「收编正规军」。它将那些在
ext试验田中摸爬滚打多年的成熟方案果断吸纳进主干规范:诸如source.schain、regs.gpp/gpp_sid,并针对疯狂崛起的 CTV 与视频流媒体流量引入了强有力的 podding 字段支撑。其迭代方向极为明确:彻底将隐私合规与全链路供应链透明度提拔为系统的一等公民。 - 3.0(激进重构,落地步履维艰):这是一场堪称地震级别的架构推倒重写。其核心思想在于追求极致的分层设计:强行将「数据该如何传输」(Layer-4 甚至预留了拥抱 Protobuf 的接口)与「到底在传输什么广告业务对象」进行物理隔离。后者被剥离并独立升华成了 AdCOM(Advertising Common Object Model)规范:过往揉捏在一起的
imp、site/app及创意素材等实体,被抽象成了可以跨越不同协议任意复用的通用对象模型库。虽然这种架构理念在学术上更为洁癖与纯粹,但由于向后兼容的断崖式破裂所带来的迁移成本过于巨大,其在线上实盘流量中的普及速度远远落后于 2.x 阵营。
面对这三种截然不同的版本形态,最务实的工程策略是:以 2.6 版本的字段分布作为理解底层运作逻辑与编写归一化代码的蓝本,同时将 3.0 与 AdCOM 体系视为一份「预言未来的高阶对象词典」来品读。一旦你在某家激进交易所的 ext 逃生口中瞥见了带有浓烈 AdCOM 风格的深层嵌套结构时,便能做到见怪不怪、从容拆解。
9. 常见误解 ↔ 正解
| 业界常见误解 | 破除迷思的正解阐释 |
|---|---|
| OpenRTB 里 DSP 决定谁最终赢得拍卖 | 拍卖的主持者、胜负的裁决者与成交价的拟定者永远是交易所;DSP 仅仅拥有提报 price 的报价权 |
bid.price 填写的金额就是最终实际扣减的花费 | 在二价机制下绝非如此;真实的成交价唯有依靠 ${AUCTION_PRICE} 宏在事后精准回传(详见 §4 / §6.2) |
nurl 的唯一使命就是用来触发计费确认 | nurl 仅仅代表竞买获胜的初步通知,涉及到真金白银的财务计费必须严格以 burl 触达为准(赢了并不代表真实曝光,详见 §6.1) |
素材创意必须依赖 nurl 的回调机制去触发拉取 | 现代工程的主流实践是在响应中直接使用 adm 内联交付;传统的 nurl 拉取由于平白增加一跳延迟已被抛弃(详见 §5) |
imp.id 和顶层请求的 id 说的完全是一回事 | 前者仅仅指代广告位在当前这个请求包内的局部序列号(需与 bid.impid 精准缝合),而后者则是标识整次交互请求的全局唯一 ID |
user.id 在物理实质上等同于 buyeruid | 它们一个是独属卖方视角的受众 ID、一个是独属买方视角的受众 ID,两者必须依赖底层的 Cookie Matching 碰撞机制才能建立映射关联 |
发生一次曝光时 site 和 app 可以合法地同时出现 | 两者处于绝对的互斥状态,有且仅有其中之一能够存活,因为单次物理曝光不可能同时劈腿两种截然不同的宿主环境 |
| schain 供应链透明度是直到 2.6 版本才无中生有的新概念 | 其业务概念诞生极早,最初长期蛰伏于 source.ext.schain 之下,直到 2.6 版本才媳妇熬成婆转正为 source.schain |
| 输掉竞价后意味着彻底与本次流量断联且无法获得任何反馈 | 只要合理配置 lurl + ${AUCTION_LOSS} 组合,交易所便会向你坦陈输单的深层死因,这是驱动出价模型自我进化的核心口粮 |
| 既然 3.0 架构先进,现在大家肯定都在全面拥抱 OpenRTB 3.0 | 当前线上流量的绝对霸主依然是 2.5 和 2.6;3.0 虽然架构洁癖且理念超前,但受阻于极高的迁移成本,落地态势异常迟缓 |
10. 常用字段对照
| 当你作为开发者需要关心的问题 | 应该去深挖哪个核心字段 / 嵌套对象 | 数据载体的归属方 |
|---|---|---|
| 究竟要卖的是哪些特定的广告位 | imp[](并深度解析内部的 banner/video/audio/native 分支) | request 报文 |
| 触及底线的最低竞买门槛 | imp.bidfloor 或是私有专属的 imp.pmp.deals[].bidfloor | request 报文 |
| 是否存在排他性的私有交易与专属 Deal ID | 探究 imp.pmp.deals[] 数组(其中的 id 属性即为 Deal ID) | request 报文 |
| 这股流量真实爆发的环境载体 | site(基于 Web 网页)或者 app(基于应用客户端),两者互斥择其一 | request 报文 |
| 终端硬件、广告唯一标识与是否限制跟踪 | device 对象(聚焦其内部的 ifa/lmt/dnt/geo 取值) | request 报文 |
| 受众身份在买卖双方眼中的各自倒影 | user.id(属于卖方域) 与 user.buyeruid(属于买方域) | request 报文 |
| 该笔流量库存流转过的透明供应链节点 | 锁定 source.schain(需注意其是在 2.6 版本后转正) | request 报文 |
| 悬在头顶的跨国隐私法规与合规紧箍咒 | 检视 regs 容器(包括 coppa 保护、以及延伸出的 ext.gdpr / gpp 串) | request 报文 |
| 留给 DSP 执行推理策略的生死时速 | 读取 tmax 约束边界 | request 报文 |
| 最终敲定胜负与价格博弈的底层拍卖逻辑 | 读取 at 标识(取值 1 代表激进的一价制 / 2 代表保守的二价制加量) | request 报文 |
| 本方决定出手的核心报价 | seatbid[].bid[].price 节点 | response 报文 |
| 该笔天价出价究竟瞄准了哪一个具体坑位 | 挂载于 bid.impid 之上(并确保其完美咬合前置的 imp.id) | response 报文 |
| 亟待下发渲染的真实创意数据 | 绝大多数场景下直接提取 bid.adm(采用高效的内联模式) | response 报文 |
| 广告主的护城河:品牌安全底线与竞品隔离要求 | bid.adomain[] 与标识行业的 bid.cat[] 数组 | response 报文 |
| 本次出价寄希望于命中的私有专属通道 | 绑定 bid.dealid | response 报文 |
| 主动放弃参竞的深层系统隐情 | 利用 nbr 异常码(并极力推荐搭配轻盈的 HTTP 204 无内容响应) | response 报文 |
| 赢得拍卖的首个捷报及用来对齐价格的占位符 | 构建 bid.nurl 回调链,并预埋 ${AUCTION_PRICE} 宏等待解析 | notice 通知流 |
| 基于真实展示并用以约束财务扣减的核算锚点 | 构建 bid.burl 回调链 | notice 通知流 |
| 饮恨落榜后汲取模型优化养分的复盘通道 | 构建 bid.lurl 回调链,并预埋 ${AUCTION_LOSS} 宏以捕获死因 | notice 通知流 |
| 游离于规范之外却又无处不在的私有业务逻辑 | 潜伏于任意对象外围的 ext 逃生舱(方言的绝对重灾区) | 跨越两端报文 |
行业核心黑话总结: OpenRTB:实时竞价互操作标准;bid request/response:曝光机会与出价的跨域载体;imp:具体的物理广告位;seatbid:按买方席位聚合的出价阵列;at:定夺生死的拍卖类型(一价/二价);bidfloor:卡死底价的起拍门槛;tmax:无情的时间超时上限;adm:直通渲染的内联创意标记;nurl/burl/lurl:涵盖赢单、权威计费与败北死因的三维通知流;${AUCTION_PRICE}:动态回填真实清算成本的核心解密宏;nbr:交代弃考原因的错误码;buyeruid:依赖 Cookie 同步构建的跨域用户指纹;ext:容纳各家非标方言的协议逃生舱。
在彻底掌握这份作为系统血液的底层报文结构之后,接下来我们需要深入服务端引擎的腹地——Bidder 竞价服务架构,看看这些 JSON 对象是如何在百万级并发下被高效拆解与吞吐的。
延伸阅读
同属于 买侧竞价链路 系列,当这份充满各种异构协议数据的请求正式杀入引擎内部之后:
- Bidder 竞价服务架构:这篇将宏大的 Bidder 后端系统拆解成了七级精密漏斗的总览,而本文所阐述的一切,正是驱动该漏斗运转的初始输入格式。
- Bidder 解析层(Parse):深度剖析竞价引擎究竟是如何强力解析并无情归一化这份带有各家残存
ext方言的请求报文。
程序化生态系列,无论是业务模式还是反欺诈网络,无一不是踩在这份坚实的协议基座之上:
- 程序化广告生态全景:一文看懂究竟是谁在海量的带宽中收发这份 OpenRTB 报文——DSP、SSP、ADX 错综复杂的角色站位与信息流转链路。
- 程序化交易模式:RTB / PMP / PD / PG:深度剖析协议中的
imp.pmp.deals[]与至关重要的at字段在截然不同的卖法下,是如何爆发出不一样的化学反应。 - Last Look 深挖:详细复盘
at字段是如何在血雨腥风的博弈中从二价全面倒向一价的来龙去脉,并揭示出「交易所主持」模式为何天然留出了作弊偷看的缝隙。 - 供应链透明度(下):OpenRTB schain:聚焦于
source.schain对象的逐跳核验机制剖析,本文受限于篇幅仅在字段表层点到即止。 - RTA 精讲:带你领略在 OpenRTB 正式出价之前那道极具护城河价值的私有过滤屏障,并深入探讨
buyeruid匹配率大面积坍塌的时代背景。
官方标准规范与一手技术文献:
- IAB Tech Lab. OpenRTB 2.6 Specification:这份红宝书提供了关于 bid request 与 bid response 对象模型、决定生死的
at以及各类 notice URL 与配套宏的绝对权威定义。 - IAB Tech Lab. OpenRTB 3.0 & AdCOM:深度领略分层架构设计的极致美学,以及广告通用对象模型(AdCOM)的演进思想。
- IAB Tech Lab. OpenRTB Native Ads Specification:揭秘深藏于
imp.native字段内部那份繁杂且定制化极强的原生资产拼接规格。