Decision Explainer
Overview
The Decision Explainer is a mode of invoking the Decision API that returns information explaining why each candidate ad was or was not chosen. The most common use is to investigate why an ad did not serve.
Warning
Decision Explainer requests should ONLY be used as a debugging and development tool. Explainer requests add significant overhead to latency, bandwidth, and compute resources. As such, you should NOT use them in your production application.
Making an Explainer Request
To make a Decision Explainer request, you will need your NetworkId, SiteId, API Key, and AdType. You may make a call to the Decision API directly as usual, but set the X-Adzerk-Explain
header with your API key. If you are using one of Kevel's many Decision SDKs they handle this feature for you automatically.
You may also want to explore Kevel's Decision Explainer UI which is a user interface built to quickly and easily make explainer requests.
export API_KEY="YOUR_API_KEY"
curl -X POST \
-H "X-Adzerk-Explain:${API_KEY}" \
-H "Content-Type:application/json" \
-d '{"placements": [{"divName": "div0", "networkId": 23, "siteId": 667480, "adTypes": [5]}], "user": {"key": "abc"}, "keywords": ["keyword1", "keyword2"]}' \
"https://e-23.adzerk.net/api/v2"
import { Client } from "@adzerk/decision-sdk";
const apiKey = process.env.ADZERK_API_KEY;
// Demo network, site, and ad type IDs; find your own via the Adzerk UI!
let client = new Client({ networkId: 23, siteId: 667480 });
let request = {
placements: [{adTypes: [5]}],
user: {key: "abc"},
keywords: ["keyword1", "keyword2"]
};
const options = {
includeExplanation: true,
apiKey
};
client.decisions.get(request, options).then(response => {
console.dir(response, {depth: null})
});
require "adzerk_decision_sdk"
# Demo network, site, and ad type IDs; find your own via the Adzerk UI!
client = AdzerkDecisionSdk::Client.new(network_id: 23, site_id: 667480)
request = {
placements: [{ adTypes: [5] }],
user: { key: "abc" },
keywords: ["keyword1", "keyword2"],
}
options = {
include_explanation: true,
api_key: ENV["ADZERK_API_KEY"]
}
pp client.decisions.get(request, options)
import adzerk_decision_sdk
import os
# Demo network, site, and ad type IDs; find your own via the Adzerk UI!
api_key = os.environ.get("ADZERK_API_KEY")
client = adzerk_decision_sdk.Client(23, site_id=667480)
request = {
"placements": [{"adTypes": [5]}],
"user": {"key": "abc"},
"keywords": ["keyword1", "keyword2"],
}
options = {
"include_explanation": True,
"api_key": api_key
}
response = client.decisions.get(request, **options)
print(response)
import java.util.*;
import com.adzerk.sdk.*;
import com.adzerk.sdk.generated.ApiException;
import com.adzerk.sdk.generated.model.*;
import com.adzerk.sdk.model.DecisionResponse;
public class FetchAds {
public static void main(String[] args) throws ApiException {
// Demo network, site, and ad type IDs; find your own via the Adzerk UI!
Client client = new Client(new ClientOptions(23).siteId(667480));
Placement placement = new Placement().adTypes(Arrays.asList(5));
User user = new User().key("abc");
DecisionRequest request = new DecisionRequest()
.placements(Arrays.asList(placement))
.keywords(Arrays.asList("keyword1", "keyword2"))
.user(user);
AdditionalOptions options = new AdditionalOptions()
.includeExplanation(true)
.apiKey("YOUR_API_KEY");
DecisionResponse response = client.decisions().get(request, options);
System.out.println(response.toString());
}
}
(ns readme-explainer
(:import (com.adzerk.sdk AdditionalOptions Client ClientOptions)
(com.adzerk.sdk.generated.model DecisionRequest Placement User)))
(defn -main []
; Demo network, site, and ad type IDs; find your own via the Kevel UI!
(let [client (Client. (doto (ClientOptions. (int 23)) (.siteId (int 667480))))
options (doto (AdditionalOptions.)
(.includeExplanation true)
(.apiKey (System/getenv "ADZERK_API_KEY")))
request (doto (DecisionRequest.)
(.placements [(doto (Placement.) (.adTypes [5]))])
(.keywords ["keyword1" "keyword2"])
(.user (doto (User.) (.key "abc"))))]
(print (-> client (.decisions) (.get request options)))))
Note that the Decision Explainer supports all features of the Decision API -- single placement, multiple placements, multi-winner, etc.
Note
The iOS and Android SDKs do not support the Decision Explainer request mode. Kevel encourages mobile developers to retrieve explainer output via Kevel's Decision Explainer UI , via a Decision SDK request, or via a direct Decision API request.
Requesting information about Desired Ads
Kevel's Decision Explainer also supports requesting information about specific ads. This enables you to gather detailed information about why a certain ad was filtered out of the results. To include desired ad IDs in the request, the X-Adzerk-Explain
header needs to be modified to contain a stringified JSON object containing both the API Key and the desired ad IDs.
curl -X POST \
-H "X-Adzerk-Explain:{\"apiKey\":\"$API_KEY\",\"desiredAdMap\":{\"div0\":[123,456]}}" \
-H "Content-Type:application/json" \
-d '{"placements": [{"divName": "div0", "networkId": 23, "siteId": 667480, "adTypes": [5]}], "user": {"key": "abc"}, "keywords": ["keyword1", "keyword2"]}' \
"https://e-23.adzerk.net/api/v2"
The format of the JSON inside of the header should follow the following format:
// Each placement in the Decision Request should have a corresponding entry.
// in the desiredAdMap. If you want results for a given ad id for multiple
// placements, you need to ensure that id is included in each array in the map.
{
"apiKey": "YOUR_API_KEY",
"desiredAdMap": {
"div0": [123, 456],
"div1": [789, 1001]
}
}
API Response
In the response, you will receive an object per Decision. Each Decision object contains a Placement object, which contains the normal decision response, a Results array with information per candidate, a Buckets array with information for the channels, priorities that might contain candidates, and an optional RTB Log array.
Placement
Placement represents a decision in which the ad may be served and contains information about the ad relative to the placement. This section follows the same rules as a typical Decision API response.
{
"div0": {
"placement": {
"id": "fb399927ba14477ab129fcaa32cf0ed8",
"adTypes": [
5
],
"zoneIds": [],
"properties": {},
"contentKeys": {},
"divName": "div0",
"networkId": 10424,
"siteId": 1117631,
"eventMultiplier": 1,
"contentProperties": {
"schemas": []
}
},
"buckets": [],
"rtb_log": [],
"results": []
}
}
Results
The Results array contains your list of ads and information on why they were or were not selected to serve. If Desired Ads were requested, you will also have a desiredAds
array matching the same format as the Results array.
{
"div0": {
"placement": {},
"buckets": [],
"rtb_log": [],
"results": [
{
"phase": "selection",
"channel": 44840,
"priority": 180733,
"advertiser": 737031,
"campaign": 1390404,
"flight": 11169884,
"ad": 19233247,
"ecpm": 0,
"weight": 1,
"flightWeight": 1,
"useFlightWeight": false,
"info": "not selected to serve"
},
{
"phase": "selection",
"channel": 44840,
"priority": 180733,
"advertiser": 737031,
"campaign": 1389814,
"flight": 11168241,
"ad": 19230089,
"ecpm": 0,
"weight": 1,
"flightWeight": 1,
"useFlightWeight": false,
"info": "selected to serve"
}
]
}
}
Property | Description |
---|---|
phase | Refers to how long into the ad serving process this ad was still considered a candidate:targeting means the ad declined to be a candidate for this request because one or more of its targeting rules were not met (i.e. it was filtered out).selection means the ad had all its targeting rules satisfied, and it made it to the Priority's auction or lottery. |
channel | The integer channel ID |
priority | The integer priority ID |
advertiser | The integer advertiser ID |
campaign | The integer campaign ID |
flight | The integer flight ID |
ad | The integer ad ID |
ecpm | (effective Cost-Per-Mille) the value for this ad if used in an auction priority. |
weight | How much the ad wants to serve to make its goal; higher weight ads are more likely to win in a lottery priority. |
flightWeight | How much the ad wants to serve to make its goal; higher weight ads are more likely to win in a lottery priority. |
useFlightWeight | When useFlightWeight is true, the ad will use the flightWeight instead of the weight of the ad. |
info | Explanation about why the ads did or did not serve. |
Warning
Kevel is constantly improving the Decision API and its internals, and this can directly affect Explainer output. As such, the Decision Explainer output is subject to change without notice and should not be relied upon for anything other than manual debugging. Info messages especially are meant to be human-readable and not computer-readable.
Buckets
The Buckets array contains objects with Channel and Priority information. Each bucket is a Channel and Priority pair, sorted by highest Channel weight and then by highest Priority weight. The ad server logically looks at each bucket for a possible ad decision before moving on to the next bucket.
{
"div0": {
"placement": {},
"buckets": [
{
"channel": {
"id": 44840,
"adTypes": [4,5,6,9],
"votingEnrollmentPercentage": 100,
"payoutType": 0,
"impressionType": 1,
"customTargetingCompiled": "",
"affinity": 10,
"priorities": [...]
},
"priority": {
"id": 180730,
"order": 1,
"type": "lottery",
"isKeywordOptimized": false,
"isSecondPricing": false,
"isDeleted": false,
"minBidIncrement": 0.01
}
},
{
"channel": {...},
"priority": {...}
}
],
"rtb_log": [],
"results": []
}
}
RTB Logs
RTB Logs are defined as Real Time Bidding logs and return information about any RTB bid traffic resulting from your decision request. Your account must be RTB-enabled to use these features.
Troubleshooting
Here are a few reasons why an ad may lose a decision request.
- The Ad was disabled and needs to be enabled
- The Ad has a start date in the future
- The Ad has an end date in the past
- The Ad may have targeting rules applied to it which makes it ineligible to serve for the given request
- The Ad has a really low impression goal and a very long time frame to serve the impressions in (i.e. very low weight)
- The Ad is in a lower priority, and a higher priority ad has been selected to serve instead
Updated 6 months ago