Custom key value targeting
As a publisher, you might have custom data about your users (age, gender, income, interests, location, etc) or content (search keywords, contextual tags, ad positions, etc) that you would like to utilize to produce offerings that can be of value to your advertisers. There are a variety of ways to target your customer data to accomplish this, including query strings and segment targeting. Another way is through custom key/value targeting.
With custom key/value targeting, you can define your own keys and their corresponding values to make full use of your data and help advertisers reach their intended audience. As part of our ad server, we provide API services that you can use to create your own custom key value sets.
Pre-defining key value sets
Targeting with custom key/value sets involves creating keys and values, then using those keys and values to create targeting expressions. In the API you use the targeting-key and targeting-value services to create keys and their associated values. You then use the profile service to create your targeting.
Defining keys and values for
When you define keys using the targeting-key service, those keys will be available to target within our platform. Only keys that you have pre-defined using the targeting-key service will be available in. However, you do not need to predefine values. Using the targeting-value service to pre-define values will display those values in. If you don't predefine values you can either enter them directly in or import them from a CSV (comma-separated values) file when you create your targeting. Note that while it is possible to define keys in the legacy UI, you must use the API to define new keys for availability in.
Defining keys and values for the profile service
You do not need to pre-define keys or values to create your targeting in the profile service. Including keys and values in a profile service targeting expression without first defining them with the targeting-key and targeting-value services will target correctly. However, when you query on keys and values using the targeting-key and targeting-value services those keys and values that were defined in the expression will not be included in the output of that query. Those keys will also not be available for reporting.
Creating key value sets
Your first step in using the API to create custom key value targeting is to create a key. The key is a category that contains values, the elements within that category. For example, if you define a key named "fruit"
, you might create values associated with that key named "apple"
and "orange"
. Here's an example of how to create a "fruit"
key:
$ cat create_targeting_key.json
{
"targeting-key": {
"name": "fruit",
"type": "string"
}
}
$ curl -b cookies -X POST -d '@create_targeting_key.json' 'https://api.appnexus.com/targeting-key'
"targeting-key": {
"id": 4,
"type": "string",
"name": "fruit",
"label": null,
"state": "active",
"created_on": "2016-08-26 11:30:55",
"last_modified": "2016-08-26 11:30:55"
},
In this example we've given the key the name "fruit"
and specified a type of "string". You can also create keys that will have numeric values. For example, you could define a key named "age"
with values 21
, 30
, and 45
.
After you create your key, you can associate values with that key. In our example, we're going to associate the values "apple"
and "orange"
with our "fruit"
key.
$ cat create_targeting_value.json
{
"targeting-value": {
"name": "apple"
}
}
$ curl -b cookies -X POST -d @create_targeting_value.json 'https://api.appnexus.com/targeting-value?targeting_key_id=4'
This example creates a value with the name "apple"
. We specified the targeting_key_id
in our query string to associated this value with the "fruit"
key, which has an id of 4
. Run the same command but with "orange"
as the name in place of "apple"
. Your values for the "fruit"
key (targeting_key_id = 4
) now look like this:
"targeting-values": [
{
"id": 15,
"targeting_key_id": 4,
"name": "apple",
"label": null,
"created_on": "2016-08-26 11:59:26",
"last_modified": "2016-08-26 11:59:26"
},
{
"id": 16,
"targeting_key_id": 4,
"name": "orange",
"label": null,
"created_on": "2016-08-26 11:55:12",
"last_modified": "2016-08-26 11:55:12"
}
]
You can also do bulk adds of targeting values. Rather than adding the values one at a time as we did in this examples, we could have added multiple values at once, like this:
$ cat create_targeting_value.json
{
"targeting-values": [
{
"name": "apple"
},
{
"name": "orange"
}
]
}
$ curl -b cookies -X POST -d @create_targeting_value.json 'https://api.appnexus.com/targeting-value?targeting_key_id=4'
Notice that instead of using targeting-value in our JSON file, we used targeting-values (plural) as an array.
Updating your profile
Now that you have your keys and associated values defined, it's time to add them to a profile. The combinations of keys and values you'll want to associate with a profile may get complicated. We'll start with the simplest example, then show you some more complex solutions.
Creating expressions
You'll first need to decide what keys and values you want to target. We can use the preceding example to make this simple and say we want to target all fruits that are either apples or oranges:
(fruit == apple) OR (fruit == orange)
Pretty simple, right? But what if you want to get more complicated? What if you want to target based on fruit and the city in which it's eaten? Something like this:
(fruit == apple AND city == NY) OR (fruit == orange AND (city == Seattle OR city == LA))
Now you're targeting apples in NY, or oranges in Seattle or LA.
Once you know what combination of keys and values you want to target, how do you get that into your profile? By creating a targeting expression (and sub-expressions).
Using expression operators
To create your targeting expression, you'll use the exp
(expression) object on the Profile service. Here are the elements within the exp
object:
Field | Type | Description |
---|---|---|
typ |
string | The operators used in the expression. Possible values include: - and - or - not - in - eq (equal to) - gt (greater than) - lt (less than) - gte (greater than or equal to) - lte (less than or equal to) - neq (not equal to) The operators and, or, and not can be used only with sub-expressions. The in operator is used for arrays. The operators gt, lt, gte and lte can be used only with numeric values. |
sbe |
exp object | An object containing the sub-expression (the elements of the expression). |
key |
string | The name of the targeting key |
vtp |
type | This field identifies the data type of the expression value. The value you enter in this field must match the field and type of the corresponding value field. The following values are valid: - num - numeric; a value must be provided in the vnm field- str - string; a value must be provided in the vst field- nma - numeric array; a value must be provided in the vna field- sta - string array; a value must be provided in the vsa field |
vnm |
numeric value | The value as a 32-bit signed float (for example, 25.3). Numbers can be up to 13 digits (with a maximum of six digits to the right of the decimal point). |
vst |
string | The value as a string. |
vna |
array of numeric values | A set of values as an array of floats. |
vsa |
array of strings | A set of values as an array of strings. |
The value types must match the value field names. For example, you'll receive an error if you put a value defined as a string into the vnm
(numeric value) field. Also, the type you specify in the vtp
field must match your value type. If your vtp
value is str
, your value must be assigned to the vst
field.
Note
In the exp
object, values are identified as strings by assigning str to the vtp
field. Note that this is different from the way in which string values are defined in the targeting-key service. In the targeting-key service you assign string to the type field.
Parsing and building expressions
You'll use the elements in the exp
object to put together your targeting expressions. Let's go back to our original example:
(fruit == apple) OR (fruit == orange)
Here's how we would put this expression together using the exp
object:
{
"key_value_targets": {
"kv_expression": {
"header": {
"an_version": "1.0",
"client_version": "1.0"
},
"exp": {
"typ": "or",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "apple"
}
},
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "orange"
}
}
]
}
}
}
}
We start with the key_value_targets
object, followed by the kv_expression
field. Within that field is your entire expression. Because we're targeting apples OR oranges, we've set the value of the expression typ
field to "or"
.
Note
Casing all operators must be lowercase.
Valid: "typ": "or"
Invalid: "typ": "OR"
Keys and values are not case-sensitive.
Next we add a header. The header
object contains versioning information about your expression. As you'll see in our next example, these expressions can take a certain amount of work to plan out and implement, so you don't want to worry about it breaking later because of API updates. To avoid any issues, you'll need to add a header to your key_value_targets
field with your versioning information. The header
object has two fields:
Field | Type | Value | Description |
---|---|---|---|
an_version |
string | 1.0 |
The version of the back-end engine evaluating the expression. Current version is 1.0. |
client_version |
string | 1.0 |
The version of the client-facing implementation of the expression (the format we're walking you through in this document). Current version is 1.0. |
These fields are both required on PUT
and POST
calls. You must enter the values as shown here in order for your expression to be evaluated correctly. Enter your header information exactly as shown in the example, including the values.
Finally, we define our sub-expressions (sbe
) that the "or"
applies to. Our first sub-expression is fruit == apple. We have a typ
of "eq"
(equal to), a key of "fruit"
, our vtp
(value type) is a string "str"
, and our vst
(string value) is "apple"
. We then do the same thing with the sub-expression fruit == orange. In other words:
Now let's try our more complicated example:
(fruit == apple AND city == NY) OR (fruit == orange AND (city == Seattle OR city == LA))
This may seem a little daunting if you try to start stringing together expressions. One method we'd recommend is to start by creating a structure of your expressions, similar to this:
Once we have our expression drawn out, we can start building it in our targeting profile. We're going to start from the bottom right and work our way up.
Here are the expressions for city == Seattle and city == LA:
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "Seattle"
},
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "LA"
}
Next we move up one level to the OR statement, where we create city == Seattle OR city == LA.
We do this by putting both expressions within a single sub-expression (sbe
). That sub-expression is within another expression that defines the relationship of the two expressions within the sub-expression as an "or"
relationship ("typ": "or"
). In other words, there is an "or"
relationship between city == Seattle and city == LA:
"exp": {
"typ": "or",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "Seattle"
}
},
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "LA"
}
}
]
}
The preceding expression can also been created as an array. We've built out the examples here to walk you through the process of creating expressions and subexpressions, but using arrays can often simply your equations, as well as reduce your levels of expression. See Using Arrays below for examples of what the expressions here would look like as arrays.
Note that because this is an OR statment, it can also be constructed using an array. This same expression can be created using arrays. Up to this point we've walked you through creating multiple exp
. Note that the above expression can also be constructed as an array. Using an array is the preferred method because it will flatten out your structure, which can be important if you start running out of levels. (See levels of expression below for details). To create this same expression as an array, use the following:
We've now completed the lower-right corner of our diagram (city == Seattle OR city == LA).
Now let's define our expression fruit == orange:
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "orange"
}
That was pretty simple. Now we get to put it all together with an "and"
expression to create fruit = orange AND (city == Seattle OR city == LA):
"exp": {
"typ": "and",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "orange"
}
},
{
"exp": {
"typ": "or",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "Seattle"
}
},
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "LA"
}
}
]
}
}
]
}
That completes the entire right side of our diagram.
Now let's move to the left side: fruit == apple AND city == NY:
"exp": {
"typ": "and",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "apple"
}
},
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "NY"
}
}
]
}
Now that we've had some practice, that expression went pretty quickly, right? We created an exp
with a typ
"and"
that combined the sub-expressions fruit == apple and city == NY.
We've now been through both sides of our diagram; we've created (fruit == apple AND city == NY), and we've created fruit == orange AND (city == Seattle OR city == LA). It's time to add the top-level "or"
:
We tie the entire expression together at the top level by putting it all into the key_value_targets
field:
"key_value_targets": {
"kv_expression": {
"header": {
"an_version": "1.0",
"client_version": "1.0"
},
"exp": {
"typ": "or",
"sbe": [
{
As you can see, we put the expression inside the key_value_targets
object and the kv_expression
object, and added our header
object (described previously). Don't forget to add in all your closing braces.
Finally, here's what the full targeting expression looks like:
{
"key_value_targets": {
"kv_expression": {
"header": {
"an_version": "1.0",
"client_version": "1.0"
},
"exp": {
"typ": "or",
"sbe": [
{
"exp": {
"typ": "and",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "apple"
}
},
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "NY"
}
}
]
}
},
{
"exp": {
"typ": "and",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "orange"
}
},
{
"exp": {
"typ": "or",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "Seattle"
}
},
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "LA"
}
}
]
}
}
]
}
}
]
}
}
}
}
Using arrays
The preceding example was created using string values. You can also use numeric values for key/value targeting, an you can use arrays. Arrays can simplify your expression and flatten out your structure if your expression is getting too long. (See Levels of Expression below.) In our first example, we had an expression that looked like this:
{
"key_value_targets": {
"kv_expression": {
"header": {
"an_version": "1.0",
"client_version": "1.0"
},
"exp": {
"typ": "or",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "apple"
}
},
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "orange"
}
}
]
}
}
}
}
Because this is an OR expression, we can easily simplify it down to a single array expression:
{
"key_value_targets": {
"kv_expression": {
"header": {
"an_version": "1.0",
"client_version": "1.0"
},
"exp": {
"typ": "in",
"key": "fruit",
"vtp": "sta",
"vsa": ["apple","orange"]
}
}
}
}
Note some key differences:
- Removed the outer
exp
with the"or"
type."Or"
is implied with the array. - With no outer expression, we no longer need a subexpression (
sbe
) containing the two key/value string sets (fruit = apple, fruit = orange) - The
typ
in the expression has been changed from"eq"
to"in"
. We're saying "any value for fruit that is in the array apple, orange." - The
vtp
is nowsta
(string array) rather thanstr
(string). - The value is specified as
vsa
(string array value) rather thanvst
(string value).
Here's our full expression simplified down to use arrays rather than separate string values. Notice that the "and"
expressions have remained, only the "or"
expression was changed to use an array.
{
"key_value_targets": {
"kv_expression": {
"header": {
"an_version": "1.0",
"client_version": "1.0"
},
"exp": {
"typ": "or",
"sbe": [
{
"exp": {
"typ": "and",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "apple"
}
},
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "NY"
}
}
]
}
},
{
"exp": {
"typ": "and",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "orange"
}
},
{
"exp": {
"typ": "in",
"key": "city",
"vtp": "sta",
"vsa": ["Seattle","LA"]
}
}
]
}
}
]
}
}
}
}
Expression restrictions
There are restrictions as to how detailed your expression can be.
Levels of expression
The logical depth (the number of levels of logical expressions) of your expression can be a maximum of 5. A logical level is any expression with a type of and, or, or not. Or, in terms of the exp
object, it's any expression (exp
) with a subexpression (sbe
). In the example we just walked through, our logical depth is 3, as shown in the following diagram:
Often a single expression can be written in several different ways (different combinations of and, or, equal, not equal, etc. can often produce identical results). If you're finding that your expressions are going beyond five logical levels, you'll need to look for ways to either simplify the expression or attempt to rewrite it in a way that "flattens" the structure.
Maximum characters
An expression can contain up to 500,000 characters (not including white space). If your expression exceeds 500,000 characters it won't perform as expected. You can try reconfiguring your expression if you need to shorten it.
For more on individual key/value limitations and requirements, see the "Requirements" sections under Targeting Key Service and Targeting Value Service.
Maximum values per Key
Each key can have a maximum of 999 values. It is possible to increase the number of values by creating multiple key/value expressions for the same key and using OR the tie them together. For example, if key1 has 999 values, you can add another key1 to your expression with up to 999 more values. Here is a simple example that shows how to combine multiple sets of values together for the same key:
"kv_expression": {
"exp": {
"sbe": [
{
"exp": {
"key": "key1",
"typ": "in",
"vsa": [
"1",
"2",
"3"
],
"vtp": "sta"
}
},
{
"exp": {
"key": "key1",
"typ": "in",
"vsa": [
"4",
"5",
"6"
],
"vtp": "sta"
}
},
{
"exp": {
"key": "key1",
"typ": "in",
"vsa": [
"7",
"8",
"9"
],
"vtp": "sta"
}
}
],
"typ": "or"
},
Using wildcards
You can use the * wildcard symbol to target multiple values in a single expression. For example, let's say that rather than targeting apples, you wanted the option to target specific types of apples. You could have a key of fruit with values that include granny smith apple, red delicious apple, honeycrisp apple. To target a specific type of apple you would use the expression fruit == honeycrisp apple. If you decide you want to target all apples, you would use a wildcard: fruit == *apple. In our exp
object that would look like this:
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "*apple"
}
You can use up to two wildcards in a value. So if for some reason your values had been entered as delicious apple red and delicious apple golden, you could find all delicious apples with the expression fruit == delicious apple*, and all apples of any type with the expression fruit == *apple*.
Troubleshooting
You've probably already figured out that it's fairly easy for something to go wrong in creating an equation. There are a few steps you can take that might help you resolve problems in your equations.
Invalid JSON expression
The profile service provides error-handling that will help you determine the cause of any problems you might be having with your JSON expression. However, you won't receive a full report of all problems at once. The service traverses the equation in the same way we walked through it in this document. It starts at the innermost equation (the bottom of the equation tree) and checks that equation. If there are any errors in that part of the equation, those errors will be reported. After you fix those errors and run the service again, it will continue to the next level in the tree and again check for errors.
Expression not editable in
It's possible to create an expression in the API that can't be displayed or edited in . If you want to be able to modify expressions through after you've created them with the API, you should follow these guidelines:
Do not create an expression more than two levels deep
Although you can create an expression up to five levels in depth through the API, can only work with up to two levels. You must simplify your expressions and keep them to a maximum of two levels to be able to modify them in. See Expression Restrictions for a description of levels of depth.
Alternate AND and OR levels
In our examples above, we alternated AND and OR levels:
"exp": {
"typ": "or",
"sbe": [
{
"exp": {
"typ": "and",
"sbe": [
You can create expressions where you repeat operators at subsequent levels. For example, in this expression you can see we start with an AND, followed immediately by another AND:
"exp": {
"typ": "and",
"sbe": [
{
"exp": {
"typ": "and",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "apple"
}
},
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "NY"
}
}
]
}
},
{
"exp": {
"typ": "and",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "orange"
}
},
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "LA"
}
}
]
}
}
}
}
However, those expressions won't be editable in. Try to simplify your expressions - if you're nesting subsequent AND expressions, you may not need to nest them. The preceding expression, written out, looks like this:
(fruit == apple AND city == NY) AND (fruit = orange AND city == LA)
It could be simplified - and made editable in - by writing it like this:
"exp": {
"typ": "and",
"sbe": [
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "apple"
}
},
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "NY"
}
}
{
"exp": {
"typ": "eq",
"key": "fruit",
"vtp": "str",
"vst": "orange"
}
},
{
"exp": {
"typ": "eq",
"key": "city",
"vtp": "str",
"vst": "LA"
}
}
]
}
Incorrect targeting
You've followed the preceding steps and the profile service runs without errors, but your targeting isn't delivering the results you'd expected. Here are some steps you can take to determine the problem:
- Double-check your equation: Are you ANDing where you should be ORing? Do you have double-negatives (not (x neq y)) that could be returning the opposite results of what you'd thought?
- Draw out the equation tree: If you didn't actually draw out an equation tree, we suggest you give it a try. This format can be very helpful in walking through each step of the equation.
- Compare your equation tree to your equation: Walk through one node at a time and make sure you have all your eq and neq and and and or all where they belong.