1. Help Center
  2. Revcontent Native API

Creating Custom API Widgets

Our API enables you to receive the same ad fill you'd see in our traditional placements in a much more flexible format.

NOTE: API usage requires full Advertising Disclosure.

API Requests

Can be in either JSON or XML format

  • URI / Endpoint:
Content-Type: application/json or text/xml
GET http://trends.revcontent.com/api/v2/
  • Required Properties:
PROPERTY TYPE DEFAULT DESCRIPTION
api_key
string none Your account's API key
pub_id
integer none Your account ID
widget_id
integer none The ID of the widget associated with the request
domain
string none The URL encoded domain name associated with the widget
  • Optional Properties:
user_ip
string request IP End user's IP address. Responses are tailored to end user's location. If an IP is not provided, the request IP is used. If you're calling the API with client-side code such as Javascript, we can detect the user's IP. Otherwise you'll need to pass it. 
user_agent
string request user agent (UA) End user's UA. Used to segment stats by device type. If a UA is not provided, the request UA is used. If you're calling the API with client-side code such as Javascript, we can detect the user's UA. Otherwise you'll need to pass it. This value must be URL-encoded into valid ASCII format 
referrer
string empty End user's encoded page URL the request is being made from 
width
integer 1280 End user's screen width
img_w
integer 420 Default creative image width
img_h
integer 315 Default creative image height
format
string JSON Format of the response
sponsored_count
integer 25 Amount of sponsored content you'd like returned in the response
sponsored_offset 
integer 0 Sponsored content item you'd like the response to start from
internal_count
integer 5 Amount of internal content you'd like returned in the response
internal_offset
integer 0 Internal content item you'd like the response to start from
send_view
string empty Used to generate views on widgets and content. See the Advanced Usage section below for details & setup instructions 
tracking
string auto Setting this to "manual" will result in impressions not being tracked automatically
tracking_method
string empty "get" will return the full impression GET URL to be used in place of POST. Only applies if "tracking=manual". See the Advanced Usage section below for additional details & options
empty
string empty Used to ignore either widget or content impressions. See the Advanced Usage section below for additional details & options
pick_one
string 1 If "tracking=manual", "pick_one" can be passed to collect an impression tracking pixel
  • Sample Request:

JSON

#JSONP var script = document.createElement('script'); script.src = 'http://trends.revcontent.com/api/v2/?api_key=your_api_key&pub_id=111&widget_id=222&domain=yourdomain.com&user_ip=71.100.91.34&referer=www.revcontent.com&format=json&sponsored_count=25&sponsored_offset=0&internal_count=3&internal_offset=2&callback=my_callback&tracking=manual&pick_one=1';; document.body.appendChild(script);         

CURL

## Sample cURL call with No Cache-control curl GET -v --cookie "__ID=72a78990b27cffce148fb" -X -H "Cache-Control: no-cache" http://trends.revcontent.com/api/v2/?api_key=your_api_key&pub_id=111&widget_id=222&domain=yourdomain.com         


API Responses

The expected response values in the return payload:

PROPERTY DESCRIPTION
impression
Encoded impression string
headline
Content headline
url
Content destination URL
image
URL of the content image
brand
Content provider
content_type
Article or video
type
Sponsored or internal
uid
Content's unique ID
view

Request to be made once the content enters a user's viewport. The following variables will need to be appended:

:referrer - the URL encoded page the content is displaying on

:p[] - an array of the content that entered the user's viewport. For example, if the first 3 pieces of content came into view you'd submit "p[]=0&p[]=1&p[]=2"

  • Sample Response:

JSON

[ { "impression":"CGX0eStWi7jzeDMY5ovjO5cIxisjGeS5podquxKFp%2FwtGhttHpV3XYWKc1HdMoNOYP3ML6PtQlCtYx7r%2BPJTIjMQRDEppgtmZbaSYdPwwrL1uSGJE2Ud62NA1jAeAueyYH%2BdHfFjctFzyp...", "brand": "Trending Solutions", "headline": "20 Mind-Blowing Facts You Didn't Know", "image": "//img.revcontent.com/?url=https://revcontent-production.s3.amazonaws.com/content/images/1430757674.jpg&pos=face&h=315&w=420", "url": "//trends.revcontent.com/click.php?d=eJwVk8kVxDAIQ1tiMVs5GEz%2FJYSc5mWwsZA%2BRaQQl1ztRMQNj2nKAxGiKOivuueBPbmY8t", "type": "sponsored", "content_type": "article", "uid": "sponsored_248" }, ... ]         


Advanced Usage

"empty" parameter
The empty parameter allows a request to be made without registering either widget or creative impressions. Pass "empty=true" to return a response without registering any impressions
 
"fill" parameter and offset rule
Sometimes it's necessary to only register content impressions while ignoring the widget impression. If either the "sponsored_offset" or "internal_offset is greater than 0 the system will automatically only register content impressions. This behavior can be forced by using the "fill" parameter - "fill=true"


"tracking" parameter
By default "auto" is passed. If "manual" is passed, it will NOT record an impression until the impression data is posted back to "/v2/track.php" passing in field name of "d". A view can be triggered in the post-back as well:

  • "d={long_impression_string}"
  • "viewed=1" or do not send "viewed" at all

Impression tracking
When calling the API using a GET call with tracking parameter set to manual (tracking=manual), you are also able to pass a parameter (pick_one=1). If pick_one is set, content will get a new property attached referred to as “impression”. Users are able to use this as an impression tracking pixel when calling the track..php call. This enables a user to trigger an impression on a certain piece of content. Each content and subsequent click link would be position =1

Viewability

Views should always be tracked. A view occurs when a widget and its content enter the user's viewport. The "send_view" parameter is used to generate a view hash. By passing "send_view=true" into your request, a view parameter will be returned in your response with a unique hash as the value. To record the view, you'll need to post back the view hash returned in the response to https://trends.revcontent.com/view.php. The "view" parameter also accepts 2 additional parameters:

  1. view_type - the type of view event. Either "widget" or "fill"
  2. p[] - the content's panel position index starting from "0"
  • Sample Post Body:
{"view":"View_Hash_Goes_Here","view_type":"fill","p[]":"0"} 


In order to determine which elements have entered a user's viewport and are considered viewed, we recommend using something like the Intersection Observer API.

The following is an example of the entire view tracking process setup in Javascript:

const settings = {
url: '//trends.revcontent.com/api/v2/',
widgetId: {Your_Widgets_ID},
apiKey: '{Your_API_Key}',
pubId: {Your_Account_ID},
domain: '{Your_Widgets_Domain}',
sponsoredOffset: 0,
sponsoredCount: 4
};

const widgetData = {
items: [],
view: '',
queue: [],
widgetView: false
};

const observerOptions = {
threshold: 0.95
};


const createItem = (item, index) => document.createRange().createContextualFragment(`
<div class="rc-item" data-index="${index}">
<a href="${item.url}">
<div>
<img src="${item.image}" />
<h3>${item.headline}</h3>
</div>
</a>
</div>
`);



const buildWidget = async () => {

const data = await fetch('${settings.url}?api_key=${settings.apiKey}&pub_id=${settings.pubId}&domain=${settings.domain}&widget_id=${settings.widgetId}&sponsored_count=${settings.sponsoredCount}&sponsored_offset=${settings.sponsoredOffset}&send_view=true`, {
credentials: 'include'
}).then(res => res.json());

widgetData.items = data.content;
widgetData.view = data.view;
widgetData.impression = data.impression;

const target = document.getElementById('app');

if (target) {
widgetData.items?.forEach((item, index) => target.appendChild(createItem(item, index)));
}

// Ideally, delay observation for a moment until html is rendered. Intersection Observer will trigger
// as true when the element is first created if in the view port. This delay gives your ad panel a chance
// to generate the markup before observation.
setTimeout(() => {
createObserver();
}, 150);
}

// Call the view event
const trackView = () => {

// Only fire an event if the queue has items. If not, do nothing.
if (widgetData.queue.length > 0) {


// Create a search params and append data to it. The view payload expects the following:
// view: The view hash received from the API. This requires decoding via decodeURIComponent
// p[]: The index to be tracked as viewed. This can be multiple entries of an index such as
// p[]: 0
// p[]: 1
// p[]: 2
const data = new URLSearchParams();
data.append('view', decodeURIComponent(widgetData.view));

if (!widgetData.widgetView) {
widgetData.widgetView = true;
data.append('view_type', 'widget');
}
else {
data.append('view_type', 'fill');
}

widgetData.queue.forEach(entry => {
data.append('p[]', entry);
})

// Once the data is added to the search params, perform a post. This must be
// form encoded as shown here, with the search params being sent as the body
// of the request. This will return 200 for successful calls.
fetch(`https://trends.revcontent.com/view.php`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
body: data
})

// Clear the queue, the items currently in the queue have been tracked and
// can be removed.
widgetData.queue = [];
}
}

// This is the callback that is ran whenever an element is observed.
const onIntersection = (entries, observer) => {
entries.forEach(entry => {
if (entry.intersectionRatio >= observerOptions.threshold) {
// If this is hit, it means the element meets the intersection ratio value
// The ratio is a percentage between 0 and 1 with 1 being 100% in view. Push
// the index into the queue in preparation of sending a view event.
widgetData.queue.push(parseInt(entry.target.dataset.index, 10));

// The position has been queue for a view event and we no longer need to observe
// this element. Remove it from observation.
observer.unobserve(entry.target);

// Delay firing the event briefly. This gives your observer a chance to aggregate
// indexes into the queue before firing an event. For example, if the first and
// second panel are in view at the same time, you can send them as a single event
// with the payload holding the indexes for each.
setTimeout(() => {
trackView();
}, 50);
}
})
}

// Create a observer and then query the DOM to get the elements to watch.
const createObserver = () => {
const observer = new IntersectionObserver(onIntersection, observerOptions);

// Get the elements to observe via a class name. Set this to whatever is appropriate.
const elements = document.querySelectorAll('.rc-item');

// If any elements are found, iterate and observe them.
if (elements) {
elements.forEach(element => observer.observe(element));
}
}


buildWidget();

If running multiple widget placements, treat them each as independents when it comes to tracking views. For example, if you have two spots on a page where the API is showing ads, each of them should be treated as their own unique widget and will need to be managed as such for the view tracking logic. We track views via an index which represents the position of the ad in regards to where it's displayed for that batch of items. Each ad shown is its own panel with an index of 0 (the very first ad shown in that group of ads) to whatever the amount of items requested is (0/1/2/3 for 4 sponsored items, etc). In other words, you might have 2 places where you want to show results from the API. That would essentially give you the following:

Placement 1 (4 sponsored items requested)

  • Panel indexes: 0/1/2/3
  • View hash: (unique hash sent back from request)
  • Unique queue for this placement: (empty array on initialization, will hold indexes of items queued for view event)


Spot 2 (another 4 sponsored items requested)

  • Panel indexes: 0/1/2/3
  • View hash: (unique hash sent back from request)
  • Unique queue for this placement: (empty array on initialization, will hold indexes of items queued for view event)

Then, in the observer/view tracking logic - determine which placement it is ➡️ push it to the appropriate queue ➡️ get the hash for that queue ➡️ fire the event ➡️ then clear the appropriate queue for the placement. You may not require this behavior depending on your needs, but it's something to consider.