资讯详情

资讯详情

OpenRTB 2.5对接实战:中文文档解读与避坑指南

简介OpenRTB 2.5中文翻译文档是面向国内程序化广告开发者、算法工程师和广告运营人员的协议参考手册解决英文原版阅读门槛高、术语理解成本高的问题。内容完整覆盖Bid Request与Bid Response、多Impression竞价机会、User与Device定向信息、Site/App环境描述、Ext扩展字段、Bidder Seat出价席位及Price Floor价格保护机制等核心对象同时讲解GDPR隐私合规、视频音频富媒体扩展和精准地理位置定位等新特性。文档以JSON格式示例穿插说明便于读者对照实际接口联调。资源为单个PDF文件大小约1.36MB已有709人学习适合广告交易平台、DSP/SSP方向的技术人员作为日常开发与方案评审时的速查材料。1. 为什么你在对接实时竞价时需要一份 OpenRTB 2.5 中文文档做程序化广告的后端同学十有八九都经历过这种阶段对方交易平台扔过来一份 OpenRTB 2.5 英文规范几百页 PDF字段密密麻麻光一个 BidRequest 就嵌套了十来个对象。有人硬啃一周把 impression 当成“印象”把 seatbid 当成“座位竞价”等到联调的时候才发现请求里少了 bidfloor 的币种、响应里没解析 nbr整个排期全砸在翻字段表上。这时候你最需要的其实是一份靠谱的 OpenRTB 2.5 中文翻译文档——它不是让你扔掉英文原版而是让你在查字段、拼 JSON、对错误码的时候能直接看中文语义快速定位省掉来回翻译的时间。这篇内容我按“能落到代码和排查上”的标准把中文文档怎么看、怎么用、有哪些坑讲清楚适合刚接触 RTB 的广告后端、客户端 SDK 开发以及要评估流量变现平台的技术负责人。2. OpenRTB 2.5 协议骨架把 BidRequest、Imp、SeatBid 理成四张表拿到中文翻译文档别从头到尾当小说读那是浪费时间。OpenRTB 2.5 整个协议本质上就是两张请求表加两张响应表请求侧是 BidRequest 和 Imp响应侧是 BidResponse 和 SeatBid/Bid。先把这四个对象的关系拆清楚后面所有字段都只是往里填内容。2.1 请求侧BidRequest 与 Imp 是两个正交的维度BidRequest 是 RTB 请求的最外层容器它描述的是“一次拍卖请求”的全局信息请求 id、拍卖类型、超时时间、屏蔽类目、对竞价方的白名单。而真正描述“这个广告位值多少钱、要什么样式”的是它里面的 imp 数组。imp 全称是 impression中文文档通常把它翻译成“展示”或“广告位请求”一个 BidRequest 里可以挂多个 imp每个 imp 是独立的售卖单元。我一般会跟团队讲一个类比BidRequest 是拍卖会的会场信息imp 是会场里一个个待拍卖的展位。会场可以说今天来了哪些买家、屏蔽哪些品类每个展位自己说清楚要多大尺寸、底价多少、是不是私有交易。这两个维度是正交的——同一个 app、同一个设备、同一个用户完全可以在一次请求里带两个尺寸不同的 imp也可以拆成两次请求各带一个 imp。理解了这个后面查字段归属就不会乱。BidRequest 顶层字段里项目上最常打交道的就这么几个我按中文文档里常见译法列一下字段类型必填说明与中文文档译法idstring是请求唯一 ID交易平台用它做日志关联常见译法“请求 ID”impobject 数组是展示对象数组至少一个常见译法“广告位请求/展示”site / appobject二者至少一个媒体环境app 指 App 内流量site 指网页流量deviceobject否设备信息包括 UA、IP、机型、屏幕尺寸userobject否用户信息包括用户 id、年龄、性别、兴趣atinteger否拍卖类型1 为第一价格2 为第二价格默认 2tmaxinteger否最大响应超时单位毫秒建议显式设置bcat / badvstring 数组否屏蔽的广告类目 / 屏蔽的广告主域名regsobject否合规字段包含 coppa 以及 GDPR 的 ext 扩展extobject否扩展字段各交易平台自定义Imp 对象下面又有自己的子对象banner、video、native、audio 分别对应横幅、视频、原生、音频四类广告形式。一个 imp 里至少出现其中一种。bidfloor 和 bidfloorcur 是一对核心组合前者是底价后者是底价币种默认 USD。很多新手只填 bidfloor 不填币种交易平台按美元算流水直接差七倍。2.2 响应侧BidResponse、SeatBid、Bid 的嵌套关系响应侧的层级比请求侧更绕因为它是三层嵌套BidResponse 下面挂 seatbid 数组每个 SeatBid 下面再挂 bid 数组。中文文档里 seat 常被翻译成“席位”指的是广告交易平台上某个竞价方所在的席位通俗理解就是“哪个买家出的价”。一个交易平台的一次拍卖可能有多个席位同时出价所以 seatbid 是数组。Bid 对象才是真正装着“谁要投、出多少钱、广告素材是什么”的地方。它的核心字段是 id、impid、price其中 impid 必须对上请求里 imp 的 idprice 是竞价价格单位跟随 BidResponse 里的 cur默认 USD。如果请求里有三个 imp响应里通常会有多组 bid每个 bid 通过 impid 指明自己竞的是哪一个。没有 impid 的 bid最好的情况是被丢进日志最坏的情况是匹配错广告位直接展示错素材。BidResponse 顶层字段里有两个容易看漏nbr 和 cur。nbr 是“没出价原因码”交易平台在 no bid 的时候会通过它告诉请求方为什么没出价cur 是货币代码。联调出现大量 no bid第一件事就是去看 nbr而不是怀疑自己网络被墙或者超时设置错了。2.3 中文文档里最容易产生歧义的翻译对照我翻过不止一版社区翻译文档发现同一份 OpenRTB 2.5不同人翻出来的术语能差出十万八千里。最典型的是 impression有人翻“展示”有人翻“展现”还有人直译成“印象”。语义上都不算大错但你在中文文档里找不到一个词再去回查英文原版来回成本很高。这里把我在几个项目里遇到过的翻译对照整理成表你手头的中文文档如果和这个不一致建议以英文键名为准中文只辅助理解。英文键名常见中文翻译实际含义impression / imp展示、展现、印象一次广告位售卖请求的对象seat席位、座位竞价方在交易平台里的投标席位bidfloor底价、最低出价广告位的最低成交价nurl通知 URL、回传地址竞价成功后的通知回调地址deal交易、私有交易PMP 里约定好的私有购买条款bundle包名、应用包名App 的应用包名或安卓包名adm广告物料、广告标记广告创意内容可能是 JSON 或 HTMLcid / crid广告主 ID / 创意 ID投放主体标识实践经验是中文文档只用来建立第一遍认知真正写代码、配字段、对日志一定要看英文 JSON 键名。我见过一个项目把文档里的“底价”理解成“当前最高价”竞价逻辑反了三天。不是翻译错了而是看中文的人把术语带到了自己熟悉的语境里。协议字段之间是有严格语义边界的中文只能是拐杖不能当标准。3. 从中文文档到能跑的代码用 Java 解析 BidRequest 与 BidResponse学完对象骨架下一步就是把文档翻译成能跑的代码。这一章我用一个相对通用的场景Java 后端 Gson 序列化/Josn 解析。你手头如果用 Fastjson、Jackson原理完全一样只是注解不同。3.1 先按中文文档拼一个最小可用的 BidRequest JSON我建议所有对接都从“最小可用请求”开始。所谓最小就是只保留必填字段和当前业务要用的字段先别把文档里的字段全堆上去。多了反而不好定位问题。下面这个 JSON 是一个比较典型的 App 横幅广告位竞价请求{ id: test-req-001, at: 2, tmax: 120, imp: [ { id: imp-001, tagid: adslot-home-banner, bidfloor: 0.5, bidfloorcur: USD, banner: { w: 320, h: 50, pos: 1 }, secure: 1 } ], app: { id: app-10001, name: DemoApp, bundle: com.example.demo, publisher: { id: pub-88 } }, device: { ua: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X), ip: 203.0.113.10, devicetype: 4, os: iOS, osv: 15.0, w: 375, h: 812 }, user: { id: user-abc-123 } }这个请求的意图翻译成人话就是请交易平台帮我拍一个 320x50 的横幅广告位底价 0.5 美元媒体是某款 DemoApp设备是一台 iPhone用户是 user-abc-123。字段里 at2 表示第二价格拍卖tmax120 告诉对方 120 毫秒内必须响应。至于 app.name 和 app.bundle虽然是可选字段但很多交易平台拿去接广告主白名单校验建议只要知道就填上。3.2 用 Gson 做反序列化数据类字段名必须对齐 JSON接下来是把这段 JSON 映射成 Java 对象。关键点只有一个字段名必须和协议 JSON 键名完全一致否则 Gson 反序列化出来全是 null。Gson 默认按字段名匹配而协议的键名全是 snake_case比如 bidfloor、impid、seatbidJava 字段名如果用 camelCase就得加 SerializedName 注解public class BidRequest { SerializedName(id) public String id; SerializedName(imp) public ListImpression imp; SerializedName(app) public App app; SerializedName(site) public Site site; SerializedName(device) public Device device; SerializedName(user) public User user; SerializedName(at) public Integer at; SerializedName(tmax) public Integer tmax; } public class Impression { SerializedName(id) public String id; SerializedName(banner) public Banner banner; SerializedName(video) public Video video; SerializedName(native) public NativeAd nativeAd; SerializedName(bidfloor) public Double bidfloor; SerializedName(bidfloorcur) public String bidfloorcur; }序列化的时候用同一个类Gson 也能正确输出 snake_case 的 JSON。有一点要特别提醒浮点字段请用 Double 或者 BigDecimal不要用 Float。bidfloor 和 price 这类字段在真实流量里经常出现 0.37、0.625 这种精度Float 会丢精度对账的时候差一分钱都查得头疼。Integer 字段全部用包装类型而不是 int这样空值可以被序列化成 null 而不是 0避免把“没传”和“传了 0”混淆。3.3 字段默认值与必填项文档表格没有明写的三处代码风险中文文档会给你字段表但默认值、条件必填这种细节有时表格里没写全。这三个坑是代码层面最常翻车的先列出来第一at 字段的默认值是 2也就是第二价格拍卖。但国内不少交易平台实际用的是第一价格。如果你不显式传 at对方服务端可能按第二价格处理而你的收入模型按第一价格测算最后的结算差价就是纯亏。代码里显式设置 at不要依赖默认值。第二bidfloorcur 默认是 USD。你可以不传但你的广告位如果是境内人民币流量请显式传 CNY。否则请求发出去交易平台理解成美元底价而实际上你的目标客户是按人民币出价的两边不在一个坐标系里要么流拍要么低价成交。第三imp 里至少要有一个广告形式子对象。banner、video、native、audio 至少出现一个否则这个请求非法。很多交易平台面对这种请求直接忽略整个 imp甚至直接 no bid。代码里在拼请求时要校验 imp 内部是否为空不能只校验 imp 数组不为空。4. OpenRTB 2.5 对接避坑清单五个让人翻车的字段与流程问题这一章是纯踩坑记录。每一个都是我在不同项目里真实遇到过的现象前因后果和解决方案都走了一遍。你对照着排查至少能省掉三分之二的联调时间。4.1 现象请求发出去交易平台一直不回 bid联调第一天请求发过去等了半分钟一个响应都没有超时设置从 120ms 改到 500ms照样空手而归。抓包看请求也发出去了状态码 200。问题出在请求体本身而不是网络。原因请求里的 at 没传tmax 也没传交易平台默认按 2 处理但我们的竞价策略机只在第一价格拍卖时出价服务端判断完直接拒了。另外没有 tmax 时某些交易平台会默认一个很小的超时值你的服务端还没来得及处理对方已经放弃了。解决显式设置 at 和 tmax。at 按你和交易平台商务确认的拍卖模式来tmax 设置成 200 或 300 毫秒。如果你们的竞价策略是抖动的tmax 要覆盖策略机的最坏耗时。这个坑在中文文档里不算细节但极其容易漏。4.2 现象adomain 传错层级导致 DSP 拒投响应里明明返回了 bid也收到了 nurl 回调但广告主侧迟迟不起量。查交易平台后台看到不少 bid 被标记为“广告主域名校验失败”。这里问题出在响应方DSP 侧填了 adomain但填的是 App 名称而不是广告主域名。原因adomain 字段在 Bid 对象里类型是 string 数组存放的是广告主的域名列表比如 example.com。它被交易平台和媒体侧拿去做过滤和品牌安全校验。很多新手把应用商店的开发者名称或者 App 名填进去校验自然不过。解决回到中文文档里看 adomain 的字段描述确认它强调的是“广告主域名”。把正确的投放域名放进数组。如果你们是中间件上游传来的 adomain 也要做一层过滤把明显是应用名的值剔除掉避免污染下游。4.3 现象price 用 float 解析导致金额对不上一个响应里返回的价格是 3.725用 float 接回来变成 3.7249999存到数据库再取出来下游对账系统直接报不平。这种问题最玄学白天看不出来一到月底对账就炸。原因OpenRTB 2.5 里 price、bidfloor 都是 float 类型但 JSON 反序列化的时候如果用 Float、float精度丢失是必然的。尤其竞价价格这种带小数的金额float 只有 7 位有效数字0.625 这种还能存下3.725 这种就完了。解决所有金额类型统一用 Double 接收入库用 Decimal 或把金额扩大成整数存储。线上代码里禁止用 float 表示任何金额字段。我在团队里定的规矩是出现 float 关键字做金额的代码直接打回。4.4 现象nurl 宏替换把价格丢了竞价成功以后请求方拿到 nurl然后把 nurl 里的宏替换成实际成交价再回传给广告主。结果日志里看到的成交价是 0或者干脆原样输出了 ${AUCTION_PRICE}广告主侧统计不到真实价格。原因nurl 是通知 URL协议规定竞价成功后请求方要主动访问它并把里面的宏替换掉。文档里写法是${AUCTION_PRICE}但实际项目中有的交易平台用%%AUCTION_PRICE%%有的用[AUCTION_PRICE]。你按文档的宏去替换替换了个寂寞。解决对接前先问清交易平台实际支持的宏语法或者干脆抓一单真实成功的 nurl 回调来看。替换完以后在日志里把替换前后的 URL 都打出来肉眼确认价格已经落在参数里。这个坑是联调后期最容易踩的因为它影响的是数据回传不是投放流程很多项目上线了大半年才发现。4.5 现象响应里出现文档没写过的 ext 字段解析直接崩了正常响应解析一切正常某天交易平台在响应里加了一个 ext.custom_data 字段我们的解析代码直接抛异常整个线程废弃。排查下来是用了严格模式的反序列化不允许未知字段出现。原因OpenRTB 2.5 文档里的 ext 字段是各交易平台自己扩展的不写进标准。有的平台把 ext 用在响应里传竞价失败原因有的传广告主 ID格式各不相同。严格模式解析器看到未知字段就抛异常等于把协议扩展变成了事故入口。解决反序列化配置要允许未知字段。Gson 默认就是忽略未知字段的用 Jackson 时记得关掉 FAIL_ON_UNKNOWN_PROPERTIES。ext 字段统一用 JsonObject 或 Map 接收不要一开始就强绑定具体类型。你不知道别人明天会往里塞什么。5. 用中文文档反向校验线上请求NBR 错误码与必填字段自查联调通过不代表万事大吉。上线以后交易平台返回 no bid 的比例突然升高这时候最怕的是靠肉眼翻日志。正确做法是把中文文档的必填字段表转成一个静态校验工具再把 NBR 错误码接进告警。5.1 把必填字段列成自检脚本OpenRTB 2.5 的字段表里没有那种完整的 JSON Schema但每个对象都标了必填项。把这些必填项转成脚本在请求发出前做一次静态检查能拦截掉大部分低级错误。我用 Python 写过一个简单的检查器核心就是这个思路import json import sys REQUIRED_TOP_LEVEL {id, imp} REQUIRED_IMP {id} AD_TYPE_KEYS {banner, video, native, audio} def check_bid_request(path): with open(path, encodingutf-8) as fp: req json.load(fp) missing REQUIRED_TOP_LEVEL - set(req.keys()) if missing: print([FAIL] BidRequest missing:, missing) sys.exit(1) for idx, imp in enumerate(req.get(imp, [])): imp_keys set(imp.keys()) if not (REQUIRED_IMP imp_keys): print(f[FAIL] imp[{idx}] missing id) if not (imp_keys AD_TYPE_KEYS): print(f[FAIL] imp[{idx}] has no banner/video/native/audio) if bidfloor in imp and bidfloorcur in imp: print(f[PASS] imp[{idx}] bidfloor{imp[bidfloor]} {imp[bidfloorcur]}) print([PASS] basic check done) if __name__ __main__: check_bid_request(sys.argv[1])这个脚本不长但把最容易出问题的三件事都查了顶层 id 和 imp 数组、每个 imp 必须要有 id、广告形式必须至少出现一个。脚本跑完再用文档字段表人工过一遍业务字段基本能把低级的拼错字段问题挡在测试环境。参数说明很简单第一个参数是 JSON 文件路径脚本只做检查不发出网络请求可以打进 CI。5.2 NBR 错误码对照表交易平台说 no bid 时的排查入口如果交易平台在响应里带了 nbr 字段那是最有价值的排查线索。中文文档里通常都会附 NBR 错误码表我把常见码整理成下面这份你排查时直接对照nbr含义排查方向0未知错误看交易平台日志联系对方接口人1技术错误请求格式是否有异常2无效请求必填字段缺失或类型错误3已知设备但被屏蔽检查设备过滤白名单4未授权设备设备未通过认证或未授权5屏蔽的设备设备在黑名单一般是作弊名单6广告类目被屏蔽检查 bcat 和广告物料的类目7广告主被屏蔽检查 badv 或广告主域名黑名单8出价低于底价检查 bidfloor 和竞价的 price9未找到可用创意广告物料没有通过审核或尺寸不匹配实际排查时对照 nbr8 的场景最多。不是你的价格算错了而是请求里的 bidfloor 币种和竞价价格币种不一致两边压根不在一个数量级。优先检查 bidfloorcur 与响应 cur 是否一致。5.3 从日志里还原一次请求-响应流程线上定位问题的时候只靠交易平台后台的报错截图是不够的。我的习惯是每次请求都打一条结构化日志包含请求 id、imp id、请求发出时间、响应返回时间、bid 数量、nbr 值。日志统一进 ELK出了问题直接按一个 request id 把请求和响应拼起来看。# 用 curl 模拟一次请求把响应原样保留 curl -s -X POST \ -H Content-Type: application/json \ -d bid_request.json \ https://adx.example.com/openrtb/bid \ -o bid_response.json # 格式化响应并提取关键字段 python3 -c import json resp json.load(open(bid_response.json)) print(response id :, resp.get(id)) print(nbr :, resp.get(nbr)) print(seatbid count :, len(resp.get(seatbid, []))) for seatbid in resp.get(seatbid, []): for bid in seatbid.get(bid, []): print(bid impid/price :, bid.get(impid), bid.get(price)) 这里的关键是不要只看响应整体。日志里必须拆到 bid 粒度否则你不知道哪个 imp 被投了、哪个被放弃了。联调阶段把这段脚本放进一个 shell 脚本里跑效率比在 IDE 里打断点高得多。6. 把中文文档当协议词典维护团队的长期打法与扩展用法聊完落地和排查最后一件事是让这份文档的价值持续增值。OpenRTB 2.5 是一个标准但各家交易平台都有私有扩展文档本身会过期翻译也可能不准。我的做法是把中文文档当作团队的“协议词典”来维护而不是一次性的参考资料。具体来说团队里可以约定三条规矩。第一所有对接项目的代码注释里涉及协议字段的地方统一引用文档里的中文术语不要各写各的“座位”“位置”“席位”。第二每个项目联调结束后把发现的字段歧义、坑、特殊 ext 回写到共享文档的附录里后面的人不用重新踩一遍。第三每隔一段时间对照英文原版做一次 diff——重点不是看翻译有没有错而是看有没有漏掉后来发布的补充说明和修订。除了当词典它还有一个扩展用法生成离线字段速查表。把 BidRequest、BidResponse 的所有常用字段整理成 Markdown 表挂在项目 Wiki 里。字段不多的时候不觉得等广告形式增加到视频、原生、激励视频之后你会发现手写 JSON 完全靠翻文档效率太低直接查团队维护的速查表会快很多。另一个用途是给新来的同事做 30 分钟的协议讲解拿一个真实请求例子从发起竞价到竞价成功走一遍比背诵字段定义有效得多。最后说个我自己的教训。很早之前接过一个移动广告聚合 SDK 的需求我当时仗着看过几页文档没细读把 Banner 的 w/h 在代码里偷懒默认成 0结果线上大量交易平台返回错误码排查了一整天才发现是尺寸匹配问题。从那以后我不管对接哪个平台都会先把最小请求跑通、再逐步加字段每加一个都在日志里确认一次而不是直接梭哈全量字段。这个习惯后来帮我节省了不知道多少个夜班。OpenRTB 2.5 这份协议不复杂但细节多翻译文档给了你一个快速入口真正能走多远还是靠你在每个字段上多较真一次。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →