В первой части мы обсуждали экосистему программатик рекламы. Получился довольно больший лонгрид, с быстрым обзором самых основных частей. Сегодня идем глубже и останавливаемся исключительно на самом протоколе OpenRTB. Важно, что большая часть статьи построена на спецификации OpenRTB 2.6. Эта версия удобна, потому что в одном документе показывает современную object model: structured user-agent через sua, DOOH-контекст, GPP-сигналы, video/audio pod-related поля и связь с supply-chain объектами. При этом наличие версии 2.6 в спецификации не означает, что каждая SSP, DSP или exchange поддерживает все поля именно так. В проде встречаются разные версии OpenRTB, частичная поддержка полей и договоренности на уровне интеграции.

Важно! В статье разбирались только поля, описанные в OpenRTB 2.6 или в связанной спецификации. Если поле встречается у конкретной платформы только внутри ext, мы не выдаем его за стандарт OpenRTB. При этом ext нельзя считать "мусорной корзиной": в реальных интеграциях там часто живут важные vendor-specific сигналы, но их нужно описывать как расширения конкретного участника, а не как универсальную норму.

Что же, теперь вперед.

  1. OpenRTB как грамматика сделки

  2. Верхний уровень: BidRequest

    2.1. Тип аукциона: at

    2.2. Imp (impression): единица продажи

    2.3. Медиаформат

    2.4. Pmp и Deal: сделка внутри impression

    2.5. Где происходит показ: Site, App, DOOH

    2.6. Device: техническая среда

    2.7. User: идентификаторы и данные

    2.8. Regs: regulatory signals

    2.9. Прозрачность supply chain

  3. BidResponse, SeatBid, Bid

    3.1. Поля Bid: цена, креатив, уведомления

    3.2. Notification URLs и macros

    3.3. No-bid и nbr

  4. Что OpenRTB покрывает и не покрывает

OpenRTB как грамматика сделки

OpenRTB - не рекламная платформа и не биржа. Это грамматика сообщения между supply-side системой, которая описывает рекламную возможность, и buy-side системой, которая может ответить ставкой. В конкретной цепочке это могут быть SSP, exchange, ad server, wrapper, mediation layer, DSP или bidder внутри рекламной сети.

В центре два корневых объекта:

  • BidRequest - описание рекламной возможности;

  • BidResponse - ответ со ставкой, креативом и служебными идентификаторами.

Транспорт - отдельный слой. На практике OpenRTB часто передается server-to-server по HTTPS в JSON-представлении. Спецификация 2.6 в первую очередь описывает объектную модель: где лежит imp, что означает device.ua, как bid.impid связывается с imp.id, куда кладутся regs и где появляется schain.

Немного вспомним, прошлую статью схематически изобразив главных игроков рынка и запросы между ними:

Верхний уровень: BidRequest

BidRequest - корневой объект запроса. Его удобно читать не как
длинный список полей, а как набор смысловых блоков.

Блок

Поля и смысл

Идентификация

id: ID запроса. По нему связывают request, response и логи.

Что продается

imp: массив impression opportunities. Это сердце запроса.

Где показывается

site, app, dooh: сайт, приложение или digital out-of-home.

Техническая среда

device: user-agent, IP, geo, тип устройства, язык, connection type и т.д.

Пользователь

user: ID, buyer ID, data/eids, если это доступно и разрешено.

Ограничения

regs; блокировки bcat, badv, bapp; allow/block по buyer seats через wseat, bseat.

Время, валюта и тип аукциона

tmax, cur, at.

Происхождение

source: transaction ID, payment ID chain, supply chain.

Расширения

ext: место для нестандартных расширений.

Компактный пример:

{
	"id": "req-001",
    "tmax": 120,
    "at": 1,
    "cur": ["USD"],
    "imp": [
	    {
		    "id": "1",
	        "banner": {
		        "format": [{ 
			        "w": 300, "h": 250 
			    }] 
			},
	        "bidfloor": 0.80,
	        "bidfloorcur": "USD",
	        "secure": 1
        }
    ],
    "site": {
	    "domain": "example.com",
        "page": "https://example.com/article"
    },
    "device": {
	    "ua": "Mozilla/5.0 ...",
        "ip": "203.0.113.1",
        "language": "en"
    },
    "user": {
        "buyeruid": "buyer-user-123",
        "ext": { "consent": "CONSENT_STRING_EXAMPLE" }
    },
      "regs": { "gdpr": 1 }
    }

Это не шаблон для копирования. Это карта вложенности, от которой мы пойдем к самим объектам. CONSENT_STRING_EXAMPLE в ext здесь именно placeholder: реальную consent string и место ее передачи нужно брать из privacy-фреймворка и интеграционной документации.

Далее рассмотрим наиболее важные объекты в модели.

Тип аукциона: at

OpenRTB 2.6 содержит request-level поле at. По спецификации OpenRTB 2.6 оно не описывает весь алгоритм аукциона, но передает базовый auction type:

  1. first-price auction;

  2. second-price plus auction.

Это важный мост к следующей части: at говорит DSP, как интерпретировать ставку на уровне протокола, но не раскрывает dynamic floors, bid shading, правила округления, комиссии, приоритет ad server или post-auction проверки. Поэтому at нельзя читать как полную формулу списания. Упростим, at конкретно говорит DSP по какому аукциону будет действовать SSP, но не раскрывает какие-либо детали расчета.

Imp (impression): единица продажи

imp - массив объектов Imp. Каждый Imp описывает одну рекламную возможность. Технически DSP возвращает ставку на конкретный Imp; модель ценности при этом может учитывать сайт, приложение, пользователя, контент, креатив, кампанию, deal, частоту и другие сигналы.

Ключевые группы полей Imp:

Группа

Примеры

Идентификатор

id; в ответе ему соответствует bid.impid.

Медиаформат

banner, video, audio, native.

Цена и условия

bidfloor, bidfloorcur, secure.

Private marketplace

pmp, внутри него deals.

Технические признаки

tagid, instl, rwdd, metric, ext.

Самая важная связка:

BidRequest.imp[0].id = "1"
BidResponse.seatbid[0].bid[0].impid = "1"

Так DSP говорит: "моя ставка относится именно к этому impression".

Когда imp несколько

imp - массив, поэтому один request может описывать несколько рекламных возможностей. DSP не обязана отвечать на каждую из них: она может вернуть ставку только на один impid, несколько ставок на разныеimpid или no-bid.

BidRequest.imp[0].id = "banner-1"
BidRequest.imp[1].id = "video-1"

BidResponse.seatbid[0].bid[0].impid = "video-1"

Такой ответ означает: buyer участвует только в аукционе за video-1.

Если в SeatBid используется group, он указывает, должны ли ставки в этой группе рассматриваться вместе. Детальные правила, можно ли выиграть несколько impressions из одного request, нужно проверять по OpenRTB 2.6 и конкретной интеграции.

Медиаформат

Banner

Объект Banner обычно воспринимают как «укажи размер и всё». Но он несёт четыре совершенно разных смысловых нагрузки одновременно.

Теперь по каждой зоне подробнее.

  1. Геометрия. Типичная ошибка — передавать только w и h как скаляры. Это работает, но лишает издателя гибкости. format[] — это массив: [{"w":300,"h":250},{"w":728,"h":90}]. SSP может подобрать подходящий размер сам, а байер не обязан держать под каждый размер отдельный imp.

  2. Ограничения. btype и battr — это блок-листы на стороне издателя, которые байер обязан уважать. btype говорит «не присылай флеш-баннеры» или «не присылай всплывающие окна» (числовые enum по IAB). battr точнее: «без автозапуска звука», «без анимации длиннее 30 секунд» — всё это атрибуты креатива. mimes ограничивает допустимые форматы файлов.

  3. Окружение. pos — это IAB-enum позиции на странице: above the fold, below the fold, header, footer, sidebar и т.д. Влияет на CPM — above the fold исторически дороже. topframe сообщает, находится ли рекламный слот в корневом window или внутри iframe: важно для верификационных скриптов, которые пытаются прочитать window.top. expdir нужен для expandable-форматов — баннер может раскрываться влево, вправо, вниз и т.д.

  4. API и идентификация. api — список поддерживаемых фреймворков: MRAID 1/2 для мобильных rich media, ORMMA, VPAID. Без этого поля байер не знает, можно ли использовать интерактивные форматы. id нужен только если в одном imp несколько Banner-объектов — иначе ссылаться на конкретный баннер в ответе невозможно.

Video

Video — самый богатый объект из четырёх форматов. Баннер описывает что показать, видео описывает ещё когда, как и в каком окружении.

Несколько нюансов, которые не влезают в ячейки. placement и plcmt — это два разных поля с пересекающейся семантикой: первое из OpenRTB 2.x, второе добавлено в 2.6 как более точная классификация. Для CTV и SSAI важно смотреть на версию спеки и конкретные требования площадки. startdelay = 0 значит pre-roll, -1 — generic mid-roll, -2 — generic post-roll. protocols — это список VAST-версий, которые принимает слот; байер обязан прислать крeatив в одном из перечисленных форматов.

Audio и Native

Audio структурно близок к Video, но без геометрии и placement-классификации. Поды работают так же: maxseq, poddur, rqddurs.

Native устроен принципиально иначе: сам объект в OpenRTB — это тонкая обёртка. Всё содержательное находится в отдельной Native Ad Specification, на которую request служит мостом.

На стороне bid response симметрично: nativeResponse тоже читается через Native Ad Spec, а не как произвольный JSON. assets[] в ответе должны отвечать assets[] из запроса по id.

Pmp и Deal: сделка внутри impression

Private marketplace в OpenRTB живет внутри Imp:

"imp": [
	{
		"id": "1",
        "banner": { "w": 300, "h": 250 },
        "pmp": {
	        "private_auction": 1,
	        "deals": [
		        {
	              "id": "deal-777",
	              "bidfloor": 2.50,
	              "bidfloorcur": "USD",
	              "at": 1,
	            }
	        ]
        }
    }
]

Объект Pmpсодержит два поля:

  • private_auction - флаг, 0 - открытый аукцион, 1 - только invited buyers по deals[].id. Без dealid в ответе — бид отклоняется exchange'ем.

  • deals - массив объектов Deal. Один imp может нести несколько сделок одновременно: например, preferred deal для одного байера и PMP-аукцион для другого.

Давайте поподробнее о Deal:

  • bidfloor + bidfloorcur — специфический floor для этой сделки, независимо от imp.bidfloor. Если оба заданы, SSP обычно применяет максимум из двух, но конкретное поведение закрепляется в интеграционных правилах — в спеке это не нормировано жёстко.

  • at — тип аукциона именно для этой сделки

  • wseat — whitelist seat-ов, которым разрешено биддить по этой сделке. Если пустой, ограничений нет.

  • wadomain — whitelist доменов рекламодателей. Не все exchange реализуют проверку на своей стороне — иногда это ответственность DSP.

  • ext — стандартное место для нестандартных полей. Часто используется для передачи audience segment ID, приоритета сделки или флагов SSAI.

Где происходит показ: Site, App, DOOH

OpenRTB 2.6 разделяет среды показа.

Site

Site используется для web-инвентаря. Важные группы:

  • идентификация: id, name, domain;

  • страница: page, ref, search;

  • категории: cat, sectioncat, pagecat;

  • связи: publisher, content;

  • прочее: keywords, mobile, privacypolicy, ext.

App

App используется для in-app инвентаря. В нем важны id, name, bundle, domain, storeurl, cat, publisher, content, keywords, ext. Главное отличие: приложение идентифицируется не только доменом, а bundle/store-контекстом.

DOOH

OpenRTB 2.6 включает DOOH для digital out-of-home. Это важно именно для версии 2.6: inventory context шире, чем site и app. В DOOH нет привычного браузерного пользователя, поэтому иначе устроен контекст места и аудитории. Например, экран в торговом центре описывается не как web-страница с cookie, а через место, экран, контент/паблишера и оценку аудитории. OpenRTB 2.6 также содержит связанные DOOH-объекты вроде Venue, Publisher, Content Producer; для DOOH особенно важно не переносить web-логику one-to-one impression без проверки спецификации и интеграции.

Device: техническая среда

Device описывает не человека, а техническую среду. Среди полей OpenRTB 2.6: ua, sua, geo, dnt, lmt, ip, ipv6, devicetype, make, model, os, osv, h, w, ppi, pxratio, js,language, carrier, connectiontype, ifa, ext.

Особенно важен sua - structured user-agent. Классический ua - строка, которую разные системы могут парсить по-разному. sua дает структурированный способ передавать user-agent-информацию. При этом legacy ua все еще встречается очень часто; поддержка sua зависит от
участников цепочки.

Типичная ошибка: воспринимать Device как набор данных для fingerprinting. В OpenRTB это объект описания среды; какие поля можно передавать и использовать, решается не только спецификацией, но и privacy-правилами, платформенными ограничениями и договорами.

User: идентификаторы и данные

User описывает пользователя в той мере, в какой это доступно и разрешено. В OpenRTB 2.6 среди полей: id, buyeruid, yob, gender,keywords, customdata, geo, data, eids, ext.

Поля вроде yob и gender существуют в спецификации, но это не означает, что они часто, надежно или правомерно передаются в современной privacy-среде. Важное различие:

  • id - exchange-specific user ID;

  • buyeruid - buyer-specific user ID, если он известен отправителю;

  • eids - external identifiers.

В первой части мы говорили о cookie syncing и identity как процессе. Здесь фиксируем только результат: где в OpenRTB может лежать идентификатор, если его разрешено и возможно передать.

Regs: regulatory signals

Regs предназначен для сигналов регулирования. В OpenRTB 2.6 описаны:

  • coppa — флаг, что запрос от ребёнка (закон США о детской приватности).

  • gdpr — флаг, что запрос из Европы и подпадает под GDPR.

  • us_privacy — строка для законов штатов США (CCPA и подобные).

  • gpp и gpp_sid — это глобальная штука, которая объединяет разные законы в одном месте. GPP — сама строка, gpp_sid — ID секций (какие законы вообще затронуты).

  • ext — Расширения.

Regs - только container для regulatory signals. Consent string для GDPR/TCF обычно передается не самим regs.gdpr, а через расширения вродеuser.ext.consent; GPP используеть regs.gpp и regs.gpp_sid. Enforcement logic остается на стороне участников цепочки и их договоров.

Важно: Regs не дает юридического разрешения использовать данные. Он передает downstream-системам сигналы, которые они должны интерпретировать по своим правилам, договорам и применимому праву.

Прозрачность supply chain

Один schain не закрывает всю тему прозрачности, подробнее о chain. В связке с ним обычно проверяют несколько официальных механизмов IAB Tech Lab:

  • ads.txt - список авторизованных продавцов web-инвентаря;

  • app-ads.txt - аналогичная идея для app-инвентаря;

  • sellers.json - сведения о продавцах в рекламной системе;

  • SupplyChain Object - цепочка участников, переданная внутри конкретного bid request.

Практический смысл: DSP может сверять, кто продает inventory, авторизован ли продавец и как выглядит путь supply. Но эти механизмы не являются математическим доказательством качества трафика; они дают проверяемую структуру для аудита.

Source и schain: происхождение запроса

Source описывает источник request. Среди полей: fd, tid, pchain, schain, ext. schain - SupplyChain Object. Он описан отдельной спецификацией IAB Tech Lab

Пример:

"source": {
	"tid": "transaction-123",
    "schain": {
	    "ver": "1.0",
        "complete": 1,
        "nodes": [
	        {
	            "asi": "ssp.example",
	            "sid": "publisher-42",
	            "rid": "req-001",
	            "hp": 1
	        }
        ]
    }
}

Ключевые поля nodes:

  • asi - advertising system identifier;

  • sid - seller/reseller ID within that system;

  • rid - request ID, если передается;

  • hp - участвует ли узел в payment flow (что это);

  • name, domain - дополнительные сведения, если переданы.

schain не доказывает честность трафика. Он дает структуру, по которой
DSP и другие участники могут проверять путь supply.

На этом закончим рассмотрения BidRequest и перейдем к модели ответа.

BidResponse, SeatBid, Bid

Часть про ответ DSP будет существенно короче, так как модель ответа содержит существенно меньше полей.

Если DSP участвует, она возвращает BidResponse. В OpenRTB 2.6 верхний уровень ответа включает id, seatbid, bidid, cur, customdata, nbr, ext.

Пример:

{
	"id": "req-001",
    "seatbid": [
	    {
	        "seat": "buyer-seat-1",
	        "bid": [
		        {
		            "id": "bid-123",
		            "impid": "1",
		            "price": 1.25,
		            "adm": "<!-- creative markup -->",
		            "crid": "creative-456",
		            "cid": "campaign-789",
		            "adomain": ["advertiser.example"],
		            "w": 300,
		            "h": 250,
		            "nurl": "https://dsp.example/win?...",
		            "burl": "https://dsp.example/billing?...",
		            "lurl": "https://dsp.example/loss?..."
	            }
            ]
        }
    ],
    "cur": "USD"
}

Три уровня:

  • BidResponse отвечает на request через id;

  • SeatBid группирует ставки по buyer seat;

  • Bid описывает конкретную ставку на конкретный Imp.

SeatBid.group управляет пакетной логикой: при group: 1 выигрыш одной ставки аннулирует остальные в группе — это нужно когда байер хочет купить ровно один слот из нескольких предложенных. Детали применения group нужно проверять в интеграционных правилах конкретного exchange.

Поля Bid: цена, креатив, уведомления

Ключевые группы полей Bid:

Группа

Поля

Связь и цена

id, impid, price, dealid.

Креатив

adm, adid, crid, cid, adomain, iurl.

Формат

w, h, mtype, dur, api, protocol.

Классификация

cat, attr, language.

Уведомления

nurl, burl, lurl.

Прочее

exp, tactic, ext.

adomain особенно важен для проверки рекламодателя и политик площадки. crid нужен для creative review и логирования. adm может быть HTML, VAST XML, native response или другой markup в зависимости от media object. nurl, burl, lurl связывают аукцион с событиями после ответа: победа, биллинг, проигрыш. Их фактическое срабатывание, macro substitution и шифрование цены задаются спецификацией и интеграционными правилами exchange; win notice не всегда равен billing event.

Поле mtype явно указывает тип markup: 1 — banner, 2 — video, 3 — audio, 4 — native. Без него exchange определяет тип по содержимому adm — это ненадёжно. crid позволяет exchange кешировать результаты creative review: при повторном появлении того же crid полная проверка adm не нужна.

Notification URLs и macros

Поля nurl, burl и lurl могут содержать URL с macros. После аукциона exchange может подставить в них значения вроде clearing price, auction ID или loss reason, если такие macros поддерживаются конкретной интеграцией и описаны в спецификации/документации.

nurl — победа в аукционе. Слот мог не отрендериться, пользователь закрыть страницу — показа ещё нет. burl — подтверждённый факт показа, именно он служит основой для финансовой сверки. lurl опционален, но полезен для DSP-аналитики: macro ${AUCTION_LOSS_REASON} передаёт причину проигрыша — перебит ценой, заблокирован badv, не прошёл battr и т.д. Поддержка конкретных macros и формат пинга закрепляются в интеграционных правилах exchange.

Практический вывод: Bid.price - это ставка в ответе DSP, а фактическая цена закрытия может прийти позже через win/billing notification. Поэтому нельзя читать bid response как полный финансовый лог сделки.

No-bid и nbr

Если DSP не делает ставку, это тоже часть протокола. BidResponse.nbr предназначен для no-bid reason.

Важно не переобещать: в реальных интеграциях no-bid может быть пустым ответом, HTTP 204, HTTP-ответом без ставки или объектом с nbr. Но если причина передается в OpenRTB-ответе, стандартное место для нее -nbr. Значения берутся из enum no-bid reason в спецификации OpenRTB 2.6.

Что OpenRTB покрывает и не покрывает

OpenRTB покрывает

OpenRTB не покрывает

Структуру BidRequest

Как DSP обучает ML-модель.

Описание Imp и медиаформатов

Как рекламодатель задает стратегию.

Контекст Site, App, Device, User

Как SSP выбирает routing.

Regs, Source, schain

Точный алгоритм аукциона, хотя at, bidfloor, price и notification URLs передают часть параметров.

BidResponse, SeatBid, Bid

Как происходят атрибуция, коммерческие комиссии и финансовая сверка.

OpenRTB - язык торгового сообщения. Он не гарантирует, что выигранная ставка превратится в показ: пользователь мог уйти, слот мог не отрендериться, креатив мог не пройти политику, ad server мог выбрать другой источник спроса. Аукцион, bid shading, clearing price и оптимизация ставок - следующая часть.

Итог

OpenRTB-запрос можно читать так:

Вот рекламная возможность: Imp. Вот среда: Site, App или DOOH. Вот устройство: Device. Вот пользовательские и regulatory-сигналы: User, Regs. Вот происхождение запроса: Source. Если хочешь участвовать, ответь через BidResponse.

OpenRTB-ответ можно читать так:

Я ставлю price на impid. Вот креатив, advertiser domain, campaign ID, creative ID и URL для уведомлений.

На этом OpenRTB заканчивается и начинается аукционная математика: кто победит, почему проиграют остальные и почему списанная цена не всегда равна ставке.

Для более подробного ознакомления