Skip to content
Charles Shao
Go back

OpenRTB 协议精讲:对象模型与拍卖结算语义

Updated:
–views

在程序化广告生态全景中,无论是海量业务角色的日常交互、交易模式所划分的 RTB/PMP 架构,还是围绕 last look 机制展开的拍卖博弈与供应链 schain 的多节点核验,其底层无一例外都建立在同一套数据交互语言之上。面对如此复杂的交易网络与庞杂的对接需求,系统是如何在百毫秒内完成信息握手、价格确认并最终触达受众的?这一切的答案,都藏在 OpenRTB 协议的底层规范之中。

本文是 买侧竞价链路 系列的第 2 篇(协议)。 全系列 9 篇:

  1. Bidder 竞价服务架构
  2. OpenRTB 协议精讲
  3. Bidder 解析层
  4. Bidder 定向过滤
  5. RTA 竞价前过滤
  6. Bidder 频次控制:五维配置与身份降级
  7. Bidder 频次控制工程篇
  8. Bidder 预算 Pacing
  9. Pacing 目标曲线

一句话定位:OpenRTB 是需求方与供给方在实时竞价中互操作的标准协议,本文将系统剖析 bid request 与 bid response 对象模型、基于 at 的结算语义及多维通知回传机制。

总览篇曾将 Bidder 架构拆解为「解析 → 定向 → 召回 → 打分 → 出价 → 排序 → 填充」七级漏斗。作为整个链路的入口,第一级解析层(Parse)的核心任务便是将外部传入的「原始竞价请求」转化为引擎内部的可计算对象,而这份原始请求的格式规范,正是本篇的主角——OpenRTB。

正因如此,本篇将剥离具体业务引擎的工程实现细节,专注于深度剖析 Bidder 每天必须吞吐的这份底层协议本身:探讨一次广告曝光(Impression)机会如何被编码为 bid request,需求方平台(DSP)又是如何构造 bid response 进行应答的。同时,我们还会深入解析交易所的结算规则、不同竞价结果下的多维通知机制、成交价的宏替换原理,以及这套 JSON 对象模型在多年间的演进脉络。

为了保持聚焦,本篇仅在字段层面点到库存的售卖方式与供应链核验等分支话题,不对其进行重复展开。读完本篇再回到解析层,你将清晰地认识到竞价引擎到底在对什么数据进行归一化处理。

OpenRTB 对象模型总览:左侧蓝色大面板 BidRequest(顶层字段 id / at / tmax / cur / test),向下挂出核心子对象——imp[](每个广告位,内含 banner / video / native / audio 四选一、bidfloor、pmp)、site 或 app(二选一,描述媒体环境)、device(ua / ip / ifa / geo / lmt)、user(id / buyeruid / consent)、source(fd / tid / schain)、regs(coppa / gdpr / gpp);右侧绿色大面板 BidResponse(顶层 id / cur / nbr),向下挂出 seatbid[](按买方席位分组)→ bid[](price / impid / adm / nurl / burl / lurl / adomain / crid / dealid);中间一条粗箭头从 BidRequest 指向 BidResponse,标注「交易所 → DSP:~100ms 内一来一回」;底部旁注:请求描述「这是一次什么样的曝光机会」,响应描述「我出多少钱、投什么创意、赢了怎么通知我」 读图提示:左侧 BidRequest 的核心对象负责描述广告曝光的具体环境特征与合规约束;右侧 BidResponse 则通过 seatbid[].bid[] 打包了需求方的出价金额、创意内容及通知接收地址。

TL;DR

Table of contents

Open Table of contents

1. 为什么需要一份协议:从「两两私约」到 OpenRTB

回顾在 last look 深挖中所提及的发展历程:在实时竞价起步之初,每一家需求方在接入不同交易所时,都必须私下约定请求的数据结构、字段命名以及货币单位。随着整个生态中需求方与供给方数量的激增,这种原始的对接模式迅速演变为一场灾难性的 (N \times M) 矩阵陷阱——接入一个新的流量源几乎等同于重写一遍解析模块。

正因如此,OpenRTB 协议应运而生。它所发挥的底层作用与 HTTP 类似:通过建立一套业界公认的标准接口,将巨大的对接成本坍缩为 (N + M)。在此框架下,DSP 仅需实现一次标准解析方案,便可无缝对接所有遵循该规范的交易所。自 2010 年起,IAB Tech Lab 持续迭代这份规范,使其成为了支撑当今每日数以万亿计广告竞价的坚实基座。

从本质上看,OpenRTB 仅仅聚焦并规范了两项核心内容:

除此之外,它还定义了一套标准的结算与通知语义(涵盖胜负判定、成交价格确认及回调逻辑),并预留了 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 节点内部,最具决定意义的是以下四选一的媒介形态:

除此之外,还有两个直接干预买方出价策略的业务级控制字段:

重点风险提示: 在研发竞价解析模块时,开发人员必须极其严谨地区分 imp.id 与全局 bid request.id。前者仅仅是当前请求内对应具体广告位的局部编号,必须与最终响应里的 bid.impid 精准缝合;一旦发生越界混用,将导致整个链路的串号崩溃。

2.2 site vs app:二选一,别同时出现

由于一次真实的广告曝光只能依托于一个确定的物理宿主环境,协议层通过硬性约束确保 site(网页端)与 app(移动应用或智能终端)对象绝对互斥,两者绝不可在同一请求中并存。虽然它们的结构树高度相似——均挂载了代表媒体主体的 publisher、内容特征的 content 以及所属行业的 cat 标签,但两者在界定自身来源真实性时存在着本质的区别:

对比这两种环境标识,它们不仅决定了防作弊策略的走向,更直接指示了买方系统后续应当挂载哪套归因链路:Web 流量深度绑定 Cookie 与广告主自身的 Ad Server 体系,而 App 流量则无可避免地需要依赖设备层标识(如 IDFA 或 OAID)并协同第三方移动归因平台(MMP)进行转化追踪。

2.3 user.id vs user.buyeruid:cookie matching 的产物

在 user 节点中并列存在的两套用户标识,往往是初涉广告研发者的理解盲区。事实上,它们分别代表了同一自然人在不同利益主体眼中的数字身份:

为何必须维持这种双向冗余结构?根本原因在于买卖双方各自圈禁了独立的用户数据孤岛,缺乏天然互通的追踪介质。为了打通这一商业闭环,双方必须通过前置的 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[] 数组体系中:

字段核心业务含义工程备注说明
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。

OpenRTB 一次竞价往返时序图:五个参与者(媒体客户端、交易所 / SSP、DSP·A、DSP·B、DSP·C 胜者)纵向生命线;步骤自上而下——① 媒体触发广告请求;② 交易所构造 bid request 并广播给三个 DSP(标注 tmax=120ms 时间预算);③ 三个 DSP 各自在 100ms 内返回 bid response(A 出 2.1、B 出 2.8、C 出 3.2,或 204 不出价);旁注「超过 tmax 的响应直接丢弃」;④ 交易所跑拍卖:按 at 结算、校验底价/adomain/尺寸,选出 C 为胜者;⑤ 交易所回调胜者 C 的 nurl(把 {AUCTION_PRICE} 替换成结算价);⑥ 交易所把 C 的 adm 交给媒体客户端渲染;⑦ 曝光被渲染/度量后,交易所触发 C 的 burl 计费通知;⑧ 交易所回调落选者 A、B 的 lurl(带 {AUCTION_LOSS} 输价原因);底部旁注:报价的是 DSP,主持拍卖与定成交价的是交易所——这正是 last look 得以存在的协议前提 读图提示:从广播到应答、从交易所内部清算到最终的三维通知流转,注意 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 在接收到这段密文后,必须调用事前线下互换的解密密钥与校验算法将其还原。除此核心宏之外,协议还附赠了一整套周边配套的占位符家族,以丰富回调上下文:

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 这片试验田:

然而,凡事皆有代价,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.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[].bidfloorrequest 报文
是否存在排他性的私有交易与专属 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.dealidresponse 报文
主动放弃参竞的深层系统隐情利用 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 对象是如何在百万级并发下被高效拆解与吞吐的。

延伸阅读

同属于 买侧竞价链路 系列,当这份充满各种异构协议数据的请求正式杀入引擎内部之后:

程序化生态系列,无论是业务模式还是反欺诈网络,无一不是踩在这份坚实的协议基座之上:

官方标准规范与一手技术文献:


–views
Share this post on:

Previous Post
Header Bidding:Prebid 客户端与服务端架构解析
Next Post
广告出海地区分层:T1 / T2 / T3 市场分析与打法