How to check inventory availability and forecast campaign delivery with Kevel
Kevel Forecast provides the insights you need to see what inventory is available and how a campaign will deliver. You have the ability to predict future ad traffic levels and campaign inventory availability using an unlimited number of targeting variables, including geo, keyword, key-value, user segment, or frequency capping.
For Forecast use cases and types, check out the Forecast Overview.
Getting Started
Asynchronous API
It’s important to note that the Kevel Forecast API is asynchronous. You can request a forecast to be performed via a POST to https://api.kevel.co/v1/forecaster/ with the appropriate API key and you always get back a response such as the following:
{
"id": "8a6f6bf5-f823-409f-8746-9ae047190cc3",
"status": "enqueued",
"progress": 0.0,
"desc": "Forecast is queued"
}
This object contains an id that you can use in a GET request to https://api.kevel.co/v1/forecaster/{id} to get the forecast result back. Forecasts can take a few seconds to some minutes to complete, depending on the amount of ads involved, how far into the future we’re forecasting and the sampling level. A complete forecast result is one whose response has the status “finished”. For example, the following is a response to a finished forecast:
{
"resultStatus": "success",
"result": {
"total": {
"329797406": {
"impressions": 0,
"uniqueUsers": 0
},
"336416380": {
"impressions": 76792,
"uniqueUsers": 31718
}
}
},
"id": "8a6f6bf5-f823-409f-8746-9ae047190cc3",
"status": "finished",
"progress": 100.0,
"desc": "Forecast is finished"
}
Grouping-by and time zone considerations
In GroupBy
you can define any key you can use in custom targeting, as well as some custom ones like $datetime.date
. For example, the following payload triggers a forecast request for existing ads using sampling level 3 and grouping results by site and date:
{
"Type": "existing",
"EndDate": "2023-03-20",
"Params": {
"Sampling": 3,
"GroupBy": ["$site", "$datetime.date"],
"TimeZone": "UTC"
}
}
Forecast results for requests with a GroupBy
parameter have an additional field in the result object, called grouped
, where you can see results for each ad grouped by the possible values of each tuple of grouping keys. The TimeZone
parameter in the Params
field specifies in which time zone should time-related fields in the GroupBy
be considered in, defaulting to UTC if no TimeZone
is set.
{
"resultStatus": "success",
"result": {
"total": {
"1234567891": {
"impressions": 0,
"uniqueUsers": 0
},
"1234567890": {
"impressions": 76136,
"uniqueUsers": 31680
},
"1234567899": {
"impressions": 294776,
"uniqueUsers": 97152
},
(...)
},
"grouped": [
{
"key": {
"$site": 1234567,
"$datetime.date": "2023-03-14"
},
"values": {
"1234567891": {
"impressions": 0,
"uniqueUsers": 0
},
"1234567890": {
"impressions": 38488,
"uniqueUsers": 17464
},
"1234567899": {
"impressions": 0,
"uniqueUsers": 0
},
(...)
}
},
{
"key": {
"$site": 1234567,
"$datetime.date": "2023-03-14"
},
"values": {
"1234567891": {
"impressions": 0,
"uniqueUsers": 0
},
"1234567890": {
"impressions": 0,
"uniqueUsers": 0
},
"1234567899": {
"impressions": 0,
"uniqueUsers": 0
},
(...)
}
},
{
"key": {
"$site": 1234568,
"$datetime.date": "2023-03-14"
},
"values": {
"1234567891": {
"impressions": 0,
"uniqueUsers": 0
},
"1234567890": {
"impressions": 0,
"uniqueUsers": 0
},
"1234567899": {
"impressions": 0,
"uniqueUsers": 0
},
(...)
}
},
(...)
]
},
"id": "33665016-a0e6-4bc0-98a2-ad14aca86053",
"status": "finished",
"progress": 100.0,
"desc": "Forecast is finished"
}
Note that large combinations of grouping keys can result in very large forecast results. Settings within the
Params
object can be used with any forecast type.
When no value is available for the specifiedGroupBy
key in the predicted impression, the results will be grouped by the value null for that key.
Group-by fields
The following keys are available for use in the GroupBy
parameter:
Name | Description | Examples |
---|---|---|
$adTypes | Set of Ad Type Ids present in the requests. | [1234,5678] |
$datetime.date | Calendar day, considering the Timezone set in the TimeZone parameter, in the YYYY-MM-DD format. | "2024-01-22" |
$datetime.dayofweek | Day of week, considering the Timezone set in the TimeZone parameter. Starts on Sunday, range is 1-7. | 2 |
$datetime.hour | Hour of day, considering the Timezone set in the TimeZone parameter. | 14 |
$datetime.week | Week of year, according to ISO 8601, considering the Timezone set in the TimeZone parameter. | 8 |
$datetime.month | Month of year, considering the Timezone set in the TimeZone parameter. | 2 |
$divName | Div name (DOM identifier) present in the ad request. | "div0" |
$keywords | The set of Keywords present in the ad requests. | ["bunny", "business", "tacocat"] |
$location.countryCode | Two-character country code. | "US" |
$location.dmaCode | Numeric Nielsen designated market area code (US only). | 560 |
$location.metroCode | Metro Code (US only). | 20500 |
$location.city | City name. | "Durham" |
$location.region | Two or three-character code for the state, region, or province. | "NC" |
$regs.gdpr | True if the request is GDPR-regulated. | false |
$consent.gdpr | True if the request has GDPR consent. | true |
$site.id | ID of the Kevel site. | 1234567 |
$zones | The set of IDs of the Kevel Zones. | [8675309,6318425] |
$user.custom | The set of user custom fields present at the time of the ad requests. Group by inner fields using: $user.custom.<field> . | {"age":"20","gender":"female","categories":["food","fashion"]} |
$user.interests | The set of user interests present at the time of the ad requests. | ["fashion", "food"] |
Multi-winner scenarios
In the Kevel Ad Platform you can make a single request and get multiple ads back (multi-winner placements). The actual availability and deliverability of ads depend on what you do with each individual ad selected in a multi-winner ad request. By default, the Forecast assumes a single ad will be used from each list of multi-winner ads, but you can adjust that behavior with:
Dynamic winner ratio
Setting the DynamicMultiWinnerImpressionRatio
to true
uses the previously observed winners to delivered impressions ratio for each different ad request. So if the number of delivered impressions is different for each of your placements, sites or other dimensions, this setting is likely what you'd like to use as it will simulate the delivery of a variable number of ads for each future ad request - even if you're always requesting the same number of winners. When used, it takes precedence over the static MultiWinnerImpressionRatio
if both are set.
Static winner ratio
If you want to simulate a single winner ratio across all ad requests, use the MultiWinnerImpressionRatio
parameter in the Forecast request which represents the percentage (from 0 to 1) of ads you expect to deliver from the list of returned winners. The ratio is applied to the Placement's count and rounded up to the nearest value. The resulting value is clamped between 1 and the placement's count. e.g., for a ratio of 0, the maximum number of opportunities to consider for that placement will be 1. If you always deliver all ads sent back in a multi-winner response, you would use the ratio of 1. If you tend to select for delivery 5 out of 10 you would set a ratio of 0.5.
Additional Display Rules
It's common for Additional Display Rules to be used alongside multi-winner placements, these are also taken into account when forecasting multi-winner scenarios (and other multi-request scenarios such as multiple placements on the same page).
Click forecasts
Forecast responses also return the estimated number of Clicks, alongside the Impression and Unique Users. This enables CTR can be easily derived (Clicks / Impressions) for any resulting forecast at any grouping level (e.g. CTR by Site, Geo and Placement):
{
"requestDateTime": "2023-09-22T14:14:08.540Z",
"lastUpdateDateTime": "2023-09-22T14:14:15.070Z",
"startDateTime": "2023-09-22T14:14:09.176Z",
"endDateTime": "2023-09-22T14:14:15.070Z",
"resultStatus": "success",
"result": {
"total": {
"clicks": 880,
"impressions": 135392,
"uniqueUsers": 43952
},
"grouped": [
{
"key": {
"$site": 12345679
},
"value": {
"clicks": 0,
"impressions": 168,
"uniqueUsers": 24
}
},
...
]
},
"id": "45dff65f-e4a0-4f8a-98b9-960a9f5b7240",
"status": "finished",
"progress": 100.0,
"desc": "Forecast is finished"
}
Adjusting click CTR predictions
To enable some fine-tuning over the predicted CTR in the forecast, there's two API options that live inside the optional parameter CampaignCTROverrides
:
MinimumCTR
: Sets the minimum clickthrough rate percentage for all ads
ZeroImpressionsCTR
: The default clickthrough rate for campaigns without historical impressions
E.g. for specifying a minimum of 0.15% CTR and that new campaigns that don’t have enough historical impressions should have a 0.5% CTR:
…
"Params": {
…
"CampaignCTROverrides": {
"MinimumCTR": 0.0015,
"ZeroImpressionsCTR": 0.005
}
Lag between impression and click event tracking
A drift/delay in sending Click events may result in missed and inaccurate click predictions, so it is advised to minimize the delay between a click happening and sending it to Kevel.