Bidding API

Open Bidder's bidding API uses the OpenRTB standard. It provides an industry-standard data model that is portable across exchanges. But OpenRTB is not the only choice, many exchanges have their own proprietary RTB protocol and even those using OpenRTB usually need some extensions. Open Bidder supports native protocols and OpenRTB extensions through exchange connectors.

Ad Exchange is dual-protocol, supporting both the proprietary AdX Protocol and OpenRTB. Individual bidders can be configured for either protocol. The bidding logic code you will write will be different for each protocol (at least the code involved in the inspection of request messages and building of response messages).

Generally the AdX protocol is more complete, while OpenRTB is more portable. The "AdX/OpenRTB" protocol is an automatic mapping from AdX's own data model to OpenRTB's, which means the OpenRTB messages are a subset of AdX since some fields from AdX may not have an OpenRTB equivalent. When this happens, AdX often uses OpenRTB Extensions, so important fields are still available even if not in a completely portable way (the OpenRTB standard allows such extensions, but each exchange will support different extensions).

The good news is that the Open Bidder API supports both options:

  • The BidRequest class provides access to the bid request, in either native or OpenRTB format.
  • The BidResponse class allows to create a bid response, in either native or OpenRTB format.

To learn more about the differences among the native and OpenRTB models, read:

OpenRTB model

The OpenRTB model is the portable choice. BidRequest.openRtb() returns an OpenRtb.BidRequest object, the request model root. The Open Bidder BidRequest class also provides many convenience methods to dig into the OpenRTB bid request; in particular, you can look up Imp and Banner objects by their IDs, or by custom Predicates (for example, to filter only impressions for a specific creative size).

The response is similar, except that BidResponse.openRtb() returns a OpenRtb.BidResponse.Builder, so you can add bids to it. There are also some utilities for the response; in particular, BidResponse.addBid(Bid) (see the following sample code) is a convenient way to add a new Bid.

The following execute method uses the OpenRTB BidRequest and BidResponse:

public void execute(InterceptorChain<BidRequest, BidResponse> chain) {
  for (Imp imp : chain.request().imps()) {
    chain.response().addBid(Bid.newBuilder()
        .setId(imp.getId())
        .setImpid(imp.getId())
        .setPrice(imp.getBidfloor() * 2)
        .setCrid("creative-77")
        .setAdm("...ad snippet...")
        .addAdomain("https://wikipedia.org"));
  }

  chain.proceed();
}

The code iterates all Imps in the request. For each Impression, it adds a new Bid with a price equal to twice the minimum accepted price for the Imp to the response.

OpenRTB Extensions

The OpenRTB model cannot represent all information of every exchange. For example, DoubleClick sends a list of pretargeting configs in the request's billing_id, and requires a similar field in the response to be populated if the request lists multiple pretargeting configs. But OpenRTB has no no field to map billing_id in the request. You solve this with code like:

for (Imp imp : chain.request().imps()) {
  Bid.Builder bid = Bid.newBuilder()
      .setId(imp.getId())
      .setImpid(imp.getId())
      .setPrice(imp.getBidfloor() * 2)
      .setCrid("creative-77")
      .setAdm("...ad snippet...")
      .addAdomain("https://wikipedia.org");
  if (imp.getExtension(AdxExt.imp).getBillingIdCount() > 1) {
    bid.setCid(String.valueOf(imp.getExtension(AdxExt.imp).getBillingId(0)));
  }
  chain.response().addBid(bid);
}

chain.proceed();

In the snippet above checks if the request contains a list of billing IDs and if this list has more than one ID. If so, we arbitrary pick the first ID of that list&emdash;not a sophisticated criteria, but good enough to make the bid valid. You might also return multiple bids, customized in some way for each pretargeting config. The billing_id is an extension in the impression, but in the response we set it in the standard field Bid.cid.

OpenRTB supports extension fields, inside ext nodes (JSON) of most objects. In our model, OpenRTB extensions take advantage of Protobuf extensions, which you can manipulate with methods like getExtension() and setExtension(). The first parameter for these methods is a "key" object, also provided by generated code. In the example, the key is AdxExt.imp and the value is a String. The AdxExt class groups all "Ad Exchange extensions".

There are two kinds of extensions in Open Bidder:

  • AdxExt: AdX/OpenRTB extensions. Supported by DoubleClick's native (on-wire) OpenRTB protocol, but some of them also used by the AdX->OpenRTB mapper.
  • ObExt: Open Bidder extensions. These are specific to the Open Bidder framework, assisting in features like macro processing. They will be preprocessed and purged from the response message before being sent to the exchange.

Native model

You can process AdX's native protocol (or, for that matter, any protocol from other non-OpenRTB exchanges that offer an Open Bidder connector):

public void execute(InterceptorChain<BidRequest, BidResponse> chain) {
  NetworkBid.BidRequest dcRequest = chain.request().nativeRequest();

  for (NetworkBid.BidRequest.AdSlot dcSlot : dcRequest.getAdslotList()) {
    NetworkBid.BidResponse.Builder dcResponse = chain.response().nativeResponse();

    for (MatchingAdData requestAdData : dcSlot.getMatchingAdDataList()) {
      dcResponse.addAd(Ad.newBuilder()
          .addAdslot(NetworkBid.BidResponse.Ad.AdSlot.newBuilder()
              .setId(dcSlot.getId())
              .setBillingId(requestAdData.getBillingId(0))
              .setMaxCpmMicros(requestAdData.getMinimumCpmMicros() * 2))
          .setHtmlSnippet("...ad snippet...")
          .addClickThroughUrl("http://wikipedia.org"));
    }
  }

  chain.proceed();
}

This code has the same effect as the previous pure-OpenRTB sample. Notice that there are no convenience methods to navigate the object trees— all you get is the getters and setters provided by the protobuf-generated model. Thus you need an extra nested loop to get the slot's MatchingData objects.

Notice that OpenRTB or Open Bidder macros are not supported here: the response data that you populate will be sent as-is to the exchange. You can of course use AdX macros (either here or with the OpenRTB model), which will be expanded by the exchange.

For more information about OpenRTB, see the openrtb-doubleclick Wiki page, which discusses the AdX/OpenRTB mapping.

Send feedback about...

Open Bidder (Beta)