ContentDB is a versatile data store that can be used to store content metadata, which can then be leveraged for context-based targeting. It can also be used to activate dynamic copy with macros.


ContentDB is a server-side database that:

  1. Speeds up ad request times when incorporating metadata you already store
  2. Allows you to store metadata for contextual targeting
  3. Lets you tailor ad content in real-time to the user (dynamic ad copy)
  4. Cuts down on mobile/app bandwidth usage when making an ad request

#1 - Speeding Up Ad Request Time

This can be confusing, so let's take an example of Prometheus, a music streaming competitor to Pandora. Prometheus has an ad platform that enables advertisers to target by genre. They also have metadata in their database tied to every song that gets played, such as:

**Song 1234:**
  title: Somebody To Love
  artist: Queen
  genres: Classic Rock, Electric Guitar


**Song 6759:**
  title: Like a Rolling Stone
  artist: Bob Dylan
  genres: Folk Rock, Acoustic Guitar

Now, in order to enable genre targeting, Prometheus has to send the genre(s) with every ad request and then set up campaigns targeting those genres. This can be done in one of two ways:

Slow WayFast Way
Every time a song gets played, Prometheus checks the Song's ID with their internal database, grabs the metadata, and passes that in the ad request to Kevel.When integrating with Kevel, Prometheus uploads their metadata straight to Kevel's ContentDB, and later in the ad request passes just the Song ID. Now, there's no need for Prometheus to look in their database before every ad call, as Kevel will do that work.

Therefore, by using ContentDB, Prometheus can still do genre targeting, but greatly speed up the ad request process, improving the user experience.

#2 - Storing Metadata for Contextual Targeting

Many brands use tools for analyzing page content, which can be useful for contextual targeting. For instance, let's take the fictional example of StoryTime, a site full of user-generated stories for children.

StoryTime has over 500,000 individual stories, and they would love to offer targeting by genres/themes/other attributes, like, "Nighttime Stories", "Fairy Tales", "1st-Grade-Reading-Level".

However, going through 500K stories and identifying their themes is not feasible. So they use a 3rd-party tool that uses an algorithm to identify the story's theme/genre.

StoryTime can then integrate these results with ContentDB, something like:

Schema: Stories

**Stories 1234:**
  title: Mr. Bear Visits Hollywood
  writer: Johnny Haverton
  themes: Animals, Comical, 5th-Grade Reading Level


**Stories 6759:**
  title: Sleepytime for Little Jimmy
  writer: Meredith Jones
  themes: Nighttime, Lullaby, Kindergarten-Reading-Level

Given that this data is now in ContentDB, StoryTime can use Custom Targeting to set up campaigns that target just, say, stories involving "Animals".

#3 - Incorporating Dynamic Ad Copy

Going back to the Prometheus example, with ContentDB an advertiser could set up a macro that dynamically pulls in the artist's name to be displayed in the ad copy. This means that rather than setting up thousands of ads for each artist with static text, they can create one ad that dynamically inputs the artist name.

For instance, Fender could have dynamic ad copy of "Like {{artist}}? Then you'll love our guitars!". Then, if a user is listening to a Scorpion song, it would say, "Like Scorpion? Then you'll love our guitars!". But if it's a Queen song, it'll populate with "Queen".

These dynamic macros pull from a given contentKey JSON object that is stored in ContentDB. See below for how to set that up.

#4 - Decreasing Amount of Mobile Bandwidth, Data, and Battery Needed

One benefit of ContentDB is that by storing data server-side, in the ad request you only have to send, say, storyid: 123, versus sending over a lot more information in the request.

By cutting down on data sent, you can improve the app/mobile user experience. For app developers who are concerned about their app's battery and bandwidth usage, this could be a vital feature.

Components of ContentDB Records

A ContentDB record is made up of the schema, the contentKey, and the JSON objects within each contentkey.

schemas"Folder" that contains records, usually grouped around a themeSong
contentKeyThe record you want to tie metadata to1234
JSON Object KeyMetadata nestled in the contentkey record. Tied with the Value{"title": "Somebody To Love"}
JSON Object ValueMetadata nestled in the contentkey record. Tied with the Key{"title": "Somebody To Love"}


There can be multiple JSON objects tied to the same contentkey record

  "title": "Somebody To Love",
  "artist": "Queen",
  "genres": ["classic rock", "electric guitar"]`

Naming Rules

Schema and ContentKey rules

Case-InsensitiveNames treated as lower case
Letters, numbers, dashes, underscoresAllowed
Periods, slashes, other punctuationNot Allowed
WhitespacesAllowed, but you will need to URLEncode the schema name in the API Request, such as http://e-<network-id><network-id>/custom/track%20information/410

JSON Object Rules

Case-SensitiveNames respect cases
Double Colons ::Not allowed, reserved for macros
Double Curly Brackets {{}}Not allowed, reserved for macros

Creating Schemas & ContentKeys

You can create records in ContentDB using our Create ContentDB Record API.

You must create ContentDB records before calling schemas and content keys in your request. At this time there is no UI functionality for adding or modifying ContentDB records.

Currently, you cannot retrieve a list of all ContentDB entries in your network.

Passing ContentKey in Decision API Request

When making a Decision API Request, you'll want to use the contentKeys field in the Placements object.

The format is "contentKeys": {"schema": "contentKey"}. For example:

"placements": [{
    "divName": "frontpage",
    "networkId": 1234,
    "siteId": 12345,
    "adTypes": [5],
    "contentKeys": {"song": 1234, "station": 2345}


Notice how in this example you are sending two contentkeys. This is allowed as long as those keys are tied to different schemas. You cannot send multiple contentkeys tied to the same schema.

Passing ContentKey in ados.js Requests

Add the .setContentKeys() method to the ados_add_placement(); function in your JavaScript ad request to pass in your schemas(s) and content key(s).

The JSON object associated with the content key will be looked up in ContentDB and then be available for use by custom targeting and creatives (via a macro).

The format is .setContentKeys({"schema": "contentKey"}).

For example: ados_add_placement(1234, 12345, "atf", 5).setContentKeys({"artist": 2993, "track": 3463477});

Enabling Custom Targeting with ContentDB

To target data stored in ContentDB, use a Zerkel value with Custom Targeting based on the$content.custom Reserved Key.

For example, let's take these contentkey records under the song schema for our Prometheus example:

Schema: Song

Song 1234:
  "title": "Somebody To Love",
  "artist": "Queen",
  "genres": ["classic rock", "electric guitar"]`

Song 4567:
  "title": "Why Can't This Be Love",
  "artist": "Van Halen",
  "genres": ["classic rock", "electric guitar"]`

Prometheus will then want to set up Custom Targeting using the Zerkel template of $content.custom.[Schema].[The JSON object Key of the ContentKey].

For instance:

WhatZerkel QueryEligible to Appear For
Fender wants to target songs that are in the "classic rock" genre so as to promote their guitars$ contains "classic rock"Song 1234 & Song 4567
20th Century Fox wants to advertise their "Bohemian Rhapsody" movie to Queen listeners$ contains "queen"Song 1234
1-800-Flowers wants to target anyone who listens to a song with the word 'love' in it$ contains "love"Song 1234 & Song 4567

The benefit to Prometheus here is that all they need to do is pass the Song ID to enable this genre targeting!


You can also target only requests that have schema data via setting $content.custom.album <> null

Enabling Dynamic Ad Copy With Macros

This functionality allows you to insert any data from a contentkey record into the ad copy in real-time, enabling dynamic ad copy that will help with engagement rates.

You'll want to use Macros in a creative. The base macro is {{content.custom}}, and the format is

{{content.custom.[schema name].[the JSON object key of the contentkey]

For example, using the Queen example above, a HTML creative with this macro...

"If you like {{}}, check out the new movie Bohemian Rhapsody!"

...would be rendered, "If you like Queen, check out the new movie Bohemian Rhapsody!", assuming the API Request includes "contentKeys": {"song": 1234}. (In the above example, the record for Song 1234 includes Queen as the Artist).

The benefit of this is that if 20th Century Fox wanted to do the same thing for people listening to songs by Scorpion ("If you like Scorpion"), Styx, Van Halen, etc, they need only one creative - instead of setting up different ads for every artist!


Macros currently only support strings and numbers, so please refrain from using a macro tied to an array of objects.


If has a whitespace, to access the track information object in a macro, you must use bracket notation: {{content.custom['track information']}}