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:
string none Your account's API key
integer none Your account ID
integer none The ID of the widget associated with the request
string none The URL encoded domain name associated with the widget
  • Optional Properties:
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. 
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 
string empty End user's encoded page URL the request is being made from 
integer 1280 End user's screen width
integer 420 Default creative image width
integer 315 Default creative image height
string JSON Format of the response
integer 25 Amount of sponsored content you'd like returned in the response
integer 0 Sponsored content item you'd like the response to start from
integer 5 Amount of internal content you'd like returned in the response
integer 0 Internal content item you'd like the response to start from
string empty Used to generate views on widgets and content. See the Advanced Usage section below for details & setup instructions 
string auto Setting this to "manual" will result in impressions not being tracked automatically
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
string empty Used to ignore either widget or content impressions. See the Advanced Usage section below for additional details & options
string 1 If "tracking=manual", "pick_one" can be passed to collect an impression tracking pixel
  • Sample Request:


#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=';; document.body.appendChild(script);         


## 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:

Encoded impression string
Content headline
Content destination URL
URL of the content image
Content provider
Article or video
Sponsored or internal
Content's unique ID

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:


[ { "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


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:

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}">
<img src="${item.image}" />

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(() => {
}, 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.

// 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(() => {
}, 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));


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.