Editorial dashboard
The document contains guides on setting up tracking for General project.
If you would like to migrate from the previous version, please contact your manager beforehand.
Add tracking code
To load tracker, add a version of the following code as close as possible to the opening <head>
tag on all pages, including:
- Home page
- Category pages
- Article pages
- All other pages of mobile website
- All other pages of desktop website
Đ•nsure the script <script async src="https://cdn.onthe.io/io.js/{id}"></script>
is presented only one time on each page.
Configuration fields
For values to be compatible with JavaScript object syntax, you need to replace "
with "
, and remove line break characters from them.
page_
fields are intended to be used on all pagesarticle_
fields are intended to be used only on pages with articles
Name | Type | Description |
---|---|---|
page_url | String | URL that is displayed in a browser window. Including URL parameters. |
page_url_canonical | String | Canonical URL of a page. Without utm_source or any other query parameters. Used to combine pages that may be available through multiple URLs into one entity. |
page_title | String | Title that you prefer to see in a dashboard. |
page_type | String | Use main value for the home page, an initial page of a website. Use article for pages with articles. Use default for all other pages. |
page_language | String | Two latter language code of a page according to ISO 639-1 (e.g. en , ru ). |
user_status | String | Status of a user. |
country | String | User's country. |
city | String | User's city. |
tags | Array | List of tags for a specific page. |
article_authors | Array | List of authors for a specific article. |
article_categories | Array | List of categories for a specific article. |
article_subcategories | Array | List of subcategories for a specific article. |
article_type | String | Type of a specific article. Used to filter articles. Can hold arbitrary values. |
article_word_count | String | Number of words in a specific article. |
article_publication_date | String | Publication date of an article according to RFC-822 Date and Time Specification with the exception that the year should be expressed as four digits. |
Publication date examples
Variant | Description |
---|---|
Fri, 23 Feb 2018 13:00:00 GMT |
In UTC |
Fri, 23 Feb 2018 08:00:00 EST |
With a local timezone |
Fri, 23 Feb 2018 15:00:00 +0200 |
With a local offset to UTC |
Tracking code examples
Example for an article page. You need to replace values of the configuration object with information about your page
<script async src="https://cdn.onthe.io/io.js/{id}"></script>
<script>
window._io_config = window._io_config || {};
window._io_config["0.2.0"] = window._io_config["0.2.0"] || [];
window._io_config["0.2.0"].push({
page_url: "https://example.com/article-url-actual?utm_source=example",
page_url_canonical: "https://example.com/article-url-canonical",
page_title: "My page title",
page_type: "article",
page_language: "en",
user_status: "My user status",
tags: ["Tag 1", "Tag 2"],
article_authors: ["John Appleseed", "Marie Curie"],
article_categories: ["Category 1", "Category 2"],
article_subcategories: ["Subcategory 1", "Subcategory 2"],
article_type: "longread",
article_word_count: "101",
article_publication_date: "Fri, 09 Feb 2018 12:39:12 GMT"
});
</script>
Example for a non-article page
<script async src="https://cdn.onthe.io/io.js/{id}"></script>
<script>
window._io_config = window._io_config || {};
window._io_config["0.2.0"] = window._io_config["0.2.0"] || [];
window._io_config["0.2.0"].push({
page_url: "https://example.com/non-article-url-actual?utm_source=example",
page_url_canonical: "https://example.com/non-article-url-canonical",
page_title: "My page title",
page_type: "default",
page_language: "en",
user_status: "My user status",
tags: ["Tag 1", "Tag 2"]
});
</script>
Example for the home page
<script async src="https://cdn.onthe.io/io.js/{id}"></script>
<script>
window._io_config = window._io_config || {};
window._io_config["0.2.0"] = window._io_config["0.2.0"] || [];
window._io_config["0.2.0"].push({
page_url: "https://example.com/",
page_url_canonical: "https://example.com/",
page_title: "My page title",
page_type: "main",
page_language: "en",
user_status: "My user status",
tags: ["Tag 1", "Tag 2"]
});
</script>
Mark up article containers
To track scroll depth of articles, surround them with a block that has the data-io-article-url
attribute which value matches with URL from browser's location bar (same as a value of window.location.href
).
Example
<div data-io-article-url="https://example.onthe.io/article-url-actual">
<!-- body of an article goes here -->
</div>
AMP
If you want to track data from Google AMP pages, you need to insert additional HTML code into your website.
1. Add canonical URL
Make sure link rel="canonical"
tag is added into <head>
tag and contains different values in href
on every page.
1. Canonical URL example
<link rel="canonical" href="https://example.onthe.io/article-url-canonical">
2. Enable AMP analytics
Include analytics framework by adding <script>
tag into <head>
tag of your website pages.
2. AMP analytics code example
<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
3. Add tracking code
Add tracking code into <body>
tag of on an every article page.
3. Default tracking code
<amp-analytics>
<script type="application/json">
{
"requests": {
"pageview": "https://tt.onthe.io/?k[]={app_id}:pageviews[user_id:${clientId(_io_un)},author:${article_authors},referrer_uri:${documentReferrer},url:${canonicalPath},domain:${canonicalHostname},user_agent:${userAgent},page:${page_title},platform:amp,language:${page_language},category:${article_categories},type_article:${article_type},word_count:${article_word_count},pub_date:${article_publication_date},page_type:${page_type},session_id:${pageViewId64}]",
"read_top": "https://tt.onthe.io/?k[]={app_id}:read_top[user_id:${clientId(_io_un)},author:${article_authors},referrer_uri:${documentReferrer},url:${canonicalPath},domain:${canonicalHostname},user_agent:${userAgent},page:${page_title},platform:amp,language:${page_language},category:${article_categories},type_article:${article_type},word_count:${article_word_count},pub_date:${article_publication_date},page_type:${page_type},session_id:${pageViewId64}]",
"read_middle": "https://tt.onthe.io/?k[]={app_id}:read_middle[user_id:${clientId(_io_un)},author:${article_authors},referrer_uri:${documentReferrer},url:${canonicalPath},domain:${canonicalHostname},user_agent:${userAgent},page:${page_title},platform:amp,language:${page_language},category:${article_categories},type_article:${article_type},word_count:${article_word_count},pub_date:${article_publication_date},page_type:${page_type},session_id:${pageViewId64}]",
"read_bottom": "https://tt.onthe.io/?k[]={app_id}:read_bottom[user_id:${clientId(_io_un)},author:${article_authors},referrer_uri:${documentReferrer},url:${canonicalPath},domain:${canonicalHostname},user_agent:${userAgent},page:${page_title},platform:amp,language:${page_language},category:${article_categories},type_article:${article_type},word_count:${article_word_count},pub_date:${article_publication_date},page_type:${page_type},session_id:${pageViewId64}]",
"read_finished": "https://tt.onthe.io/?k[]={app_id}:read_finished[user_id:${clientId(_io_un)},author:${article_authors},referrer_uri:${documentReferrer},url:${canonicalPath},domain:${canonicalHostname},user_agent:${userAgent},page:${page_title},platform:amp,language:${page_language},category:${article_categories},type_article:${article_type},word_count:${article_word_count},pub_date:${article_publication_date},page_type:${page_type},session_id:${pageViewId64}]",
"time": "https://tt.onthe.io/?k[]={app_id}:time[platform:amp,url:${canonicalPath}]"
},
"vars": {
"page_title": "${title}",
"page_type": "article",
"page_language": "",
"article_authors": "",
"article_categories": "",
"article_type": "",
"article_word_count": "",
"article_publication_date": ""
},
"triggers": {
"trackPageview": {
"on": "visible",
"request": "pageview"
},
"trackReadTop" : {
"on" : "scroll",
"scrollSpec": {
"verticalBoundaries": [25]
},
"request": "read_top"
},
"trackReadMiddle" : {
"on" : "scroll",
"scrollSpec": {
"verticalBoundaries": [50]
},
"request": "read_middle"
},
"trackReadBottom" : {
"on" : "scroll",
"scrollSpec": {
"verticalBoundaries": [75]
},
"request": "read_bottom"
},
"trackReadFinished" : {
"on" : "scroll",
"scrollSpec": {
"verticalBoundaries": [90]
},
"request": "read_finished"
},
"pageTimer": {
"on": "timer",
"timerSpec": {
"interval": 10
},
"request": "time"
}
},
"transport": {
"beacon": false,
"xhrpost": false,
"image": true
}
}
</script>
</amp-analytics>
Variables
If you want to track some specific data instead of the default one, you can customize values inside vars
block using server-side scripts.
Variables description
Use |
symbol as a separator for multiple values in article_author
and article_categories
.
page_
fields are intended to be used on all pagesarticle_
fields are intended to be used on pages with articles
Name | Description |
---|---|
page_title | Title that you prefer to see in a dashboard. |
page_type | Use article for pages with articles. Use default for all other pages. |
page_language | Two latter language code of a page according to ISO 639-1 (e.g., en , ru ). |
article_authors | List of authors for a specific article. |
article_categories | List of categories for a specific article. |
article_type | Type of a specific article. Used to filter articles. |
article_word_count | Number of words in a specific article. |
article_publication_date | Publication date of an article according to RFC-822 Date and Time Specification with the exception that the year should be expressed as four digits. |
Publication date examples
Variant | Description |
---|---|
Fri, 23 Feb 2018 13:00:00 GMT |
In UTC |
Fri, 23 Feb 2018 08:00:00 EST |
With a local timezone |
Fri, 23 Feb 2018 15:00:00 +0200 |
With a local offset to UTC |
Example of a variables block
"vars": {
"page_title": "${title}",
"page_type": "article",
"page_language": "en",
"article_authors": "John Appleseed|Marie Curie",
"article_categories": "Category 1|Category 2",
"article_type": "longread",
"article_word_count": "101",
"article_publication_date": "Fri, 09 Feb 2018 12:39:12 GMT"
},
Facebook Instant Articles
To track data from Instant Articles pages add HTML block into pages you provide to Facebook using RSS feed.
Add block
<figure class="op-tracker">
<iframe>
<script async src="https://cdn.onthe.io/io.js/{id}"></script>
</iframe>
</figure>
Yandex Turbo
To track data from Yandex Turbo pages add the code to your RSS feed
Add code
<turbo:analytics type="custom" url="https://tt.onthe.io/?k[]={app_id}:pageviews[platform:yxturbo_pixel,href:{originalURL},page:{pageTitle},referrer_uri:{referrer},user_id:{clientId},session_id:{requestId}]&{random}"></turbo:analytics>
Multimedia
Video
To indicate which HTML5 videos you want to track, add data-io-video-title
attribute on video
tags. Value of the attribute would be used as title for currently playing video.
Videos embedded using YouTube and Vimeo will be tracked automatically.
Example of a video tag with an attribute to specify title
<video data-io-video-title="My video title" src="/video-1"></video>
Galleries
Firstly, you need to enable Browser API. To do it you need to have an additional script tag before the one that loads code from cdn.onthe.io
.
- The value of
window.ioObject
host property could be customized in casewindow.io
is already in use by some other library. window[window.ioObject]
object would be updated once main library code loaded.
Enable Browser API
<!-- Additional script tag to enable API, added before the main CDN code -->
<script>
window.ioObject='io';
(function(i){window[i]=window[i]||function(){(window[i].a=window[i].a||[]).push(arguments)}})(window.ioObject);
</script>
<!-- An existing script tag used for general tracking -->
<script async src="https://cdn.onthe.io/io.js/{id}"></script>
Add configuration
You need to define configuration of the gallery placed in the current page. url
field should contains canonical url for the article page where gallery is presented.
You need to add all the slides presented in the gallery.
Configuration example
<script>
window._io_gallery_config = [
{
gallery_id : 'unique_gallery_id',
gallery_title : 'Gallery title',
url: 'https://example.com/article-url-canonical',
slides : [
{
img_src : 'src_of_the_slide_image1.jpg',
slide_position : 1,
},
{
img_src : 'src_of_the_slide_image2.jpg',
slide_position : 2,
},
{
img_src : 'src_of_the_slide_image3.jpg',
slide_position : 3,
}
]
}
];
</script>
Sending metrics
To send gallery photo view event you need to call API for gallery_photo_viewed
event.
Parameters for API call:
Key | Type | Description |
---|---|---|
event | String | gallery_photo_viewed |
gallery_id | String | The unique gallery id from window._io_gallery_config |
img_src | String | Image link for current slide from window._io_gallery_config array |
The window.io API should be called on slide change. The gallery is meant to be completed when you called window.io function for each of the img_src from window._io_gallery_config.
After completing the setup, please contact you IO manager to ensure that you are getting the latest version of tracking code and everything configured well.
Example of sending gallery_photo_viewed
event
<script>
window.io({
event:'gallery_photo_viewed',
gallery_id: 'unique_gallery_id',
img_src: 'src_of_the_slide_image1.jpg'
});
</script>
Homepage optimization
CTR and A/B testing
To track CTR and use Headline Testing on your website you would need to mark up sections, blocks, and headlines.
Attribute name | Description |
---|---|
data-vr-zone | Marks a block that contains blocks with articles |
data-vr-contentbox | Marks a block with title/link/description of an article. Specifies a unique position of a block. |
data-vr-contentbox-url | Specifies the canonical URL of an article inside a block marked with data-vr-contentbox . |
data-vr-headline | Marks the headline inside data-vr-contentbox . |
Mark up example
<div data-vr-zone="Header section">
<!-- Default example -->
<div data-vr-contentbox="With a hero image" data-vr-contentbox-url="http://www.example.com/page1.html">
<h2 data-vr-headline>Article Title</h2>
<a href="http://www.example.com/page1.html">Link</a>
</div>
<!-- With a nested headline -->
<div data-vr-contentbox="Second position" data-vr-contentbox-url="/page2.html">
<a href="/page2.html">
<h2 data-vr-headline>Article Title</h2>
</a>
</div>
<!-- With everything nested in an anchor -->
<a data-vr-contentbox="Position 3" data-vr-contentbox-url="/page3.html">
<h2 data-vr-headline>Article Title</h2>
</a>
</div>
API
You could get data from API by sending POST-requests. To get information on request limits, please contact IO support.
Your endpoint: https://api.onthe.io/{hash}
.
Your secret key: {your secret key is visible only to logged-in users}
.
Key | Type | Description |
---|---|---|
key | String | Your secret key |
entities | Object | An array of parameters you want to get could contain multiple number of entities to select |
options | Object | An array of parameters, which will be added for each entity-element from entities array can consist of such elements |
Option | Type | Description |
---|---|---|
period | Object | A time frame to extract the data |
per_page | Number | Number of elements on one page (max 2500) |
offset | Number | Entity number (starts from 0) |
Entities | Type |
---|---|
articles | Object |
sources | Object |
summary | Object |
categories | Object |
authors | Object |
Details | Type | Description |
---|---|---|
author | String | Author of publication * |
String | Total number of this category articles shares on Facebook from 12 a.m. to the current time * | |
page | String | Article's title * |
pageviews | String | Number of pageviews * |
reference_time | String | Time to read the article through * |
timeread_total | String | Total timeread * |
url | String | Article's canonical URL * |
recirculation | String | Percentage of users who proceeded to another website page |
readability | String | Percentage of users who scrolled an article to the end |
timeread | String | Average time users spend on article page |
type_article | String | Atricle types |
date_pub | String | Date of article publication |
category | String | Article's category |
sources | String | Article's sources |
social | String | Social actions |
domain | String | Domain |
trend | String | Trend compared to the same time 7 days ago ** |
value | String | Total number of pageviews from 12 a.m. to the current time(on summary only) *** |
uniques | String | Total number of uniques from 12 a.m. to the current time (on categories only) |
count_pub | String | The total number of articles published by the author from 12 a.m. to the current time (on authors only) |
* - response by default for articles
** - response by default for categories and authors
*** - response by default for summary
Request body example: TOP-20 articles for today
{
"key" : "{your secret key is visible only to logged-in users}",
"entities" : {
"articles" : {
"entity" : "articles",
"details" : ["pageviews", "author", "category", "timeread", "social", "sources"]
}
},
"options" : {
"period" : {
"name" : "today"
},
"per_page" : 20
}
}
Request body example: Categories data for a week
{
"key" : "{your secret key is visible only to logged-in users}",
"entities" : {
"categories" : {
"entity" : "categories",
"details" : ["title", "pageviews"]
}
},
"options" : {
"period" : {
"name" : "week"
}
}
}
Request body example: TOP-10 authors for today
{
"key" : "{your secret key is visible only to logged-in users}",
"entities" : {
"authors" : {
"entity" : "authors",
"details" : ["pageviews", "readability", "count_pub"]
}
},
"options" : {
"period" : {
"name" : "today"
},
"per_page" : 10
}
}
Request body example: List of articles filtered by author
{
"key" : "{your secret key is visible only to logged-in users}",
"entities" : {
"articles" : {
"entity" : "articles",
"details" : ["pageviews", "author", "category", "timeread", "social", "sources"],
"filters" : {
"author" : "author_id-value-from-authors-entity"
}
}
},
"options" : {
"period" : {
"name" : "today"
},
"per_page" : 20
}
}
Request body example: List of articles filtered by category
{
"key" : "{your secret key is visible only to logged-in users}",
"entities" : {
"articles" : {
"entity" : "articles",
"details" : ["pageviews", "author", "category", "timeread", "social", "sources"],
"filters" : {
"category" : "category_id-value-from-categories-entity"
}
}
},
"options" : {
"period" : {
"name" : "today"
},
"per_page" : 20
}
}
Period option examples (one per line)
{"period": {"name": "realtime"}}
{"period": {"name": "now"}}
{"period": {"name": "hour"}}
{"period": {"name": "today"}}
{"period": {"name": "week"}}
{"period": {"name": "month"}}
{"period": {"name": "range", "at_from": "2017-05-20", "at_to": "2017-05-25"}}
{"period": {"name": "today", "at": "2017-07-01"}}
Example in JavaScript: TOP-10 authors for today
var io={ajax:function(e,n,t,r){var o=new XMLHttpRequest;o.open(r||"POST",e,!0),o.onreadystatechange=function(){4==o.readyState&&200!=o.status&&t&&t(null),4==o.readyState&&200==o.status&&t&&t(o.responseText?JSON.parse(o.responseText):null)},o.setRequestHeader("Content-type","application/x-www-form-urlencoded"),o.send(n?io.query(n):null)},urlencode:function(e){return e=(e+"").toString(),encodeURIComponent(e).replace(/!/g,"%21").replace(/'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/\*/g,"%2A").replace(/%20/g,"+")},query:function(e,n,t){var r,o,u=[],a=this,c=function(e,n,t){var r,o=[];if(!0===n?n="1":!1===n&&(n="0"),null!==n&&"object"==typeof n){for(r in n)null!==n[r]&&o.push(c(e+"["+r+"]",n[r],t));return o.join(t)}return"function"!=typeof n?a.urlencode(e)+"="+a.urlencode(n):"function"==typeof n?"":void 0};t||(t="&");for(o in e)r=e[o],n&&!isNaN(o)&&(o=String(n)+o),u.push(c(o,r,t));return u.join(t)}};
var params = {
"key" : "{your secret key is visible only to logged-in users}",
"entities" : {
"authors" : {
"entity" : "authors",
"details" : ["pageviews", "readability", "count_pub"]
}
},
"options" : {
"period" : {
"name" : "today"
},
"per_page" : 10
}
};
io.ajax("https://api.onthe.io/{hash}", params, function (r) {
console.log(r);
});
Example in PHP: TOP-10 authors for today
<?php
$params = [
'key' => '{your secret key is visible only to logged-in users}',
'entities' => [
'authors' => [
'entity' => 'authors',
'details' => ['pageviews', 'readability', 'count_pub']
]
],
'options' => [
'period' => [
'name' => 'today'
],
'per_page' => 10
]
];
$url = 'https://api.onthe.io/{hash}';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
Other
Application tracking
Sending data is as simple as sending simple HTTPS GET requests.
String pageviews
is an example of a metric, strings page
and category
are slices.
All values in slices should be URL-encoded and be in UTF-8 encoding.
Metrics Description
Name | Event |
---|---|
pageviews | User opens an article |
read_top | A user has scrolled an article to the 25% |
read_middle | A user has scrolled an article to the 50% |
read_bottom | A user has scrolled an article to the 75% |
read_finished | A user has scrolled an article to the bottom |
time | It's been 10 seconds after a user opened the article or previous time metric was sent |
Slices Description
Name | Status | Description |
---|---|---|
domain | required | hostname of a page's URL |
url | required | pathname of a page's URL |
pr | required | pathname of a previous page's URL |
page | required | Title of a page |
category | optional | Category of an article |
sub_category | optional | Subcategory of an article |
tag | optional | Tag of an article |
type_article | optional | Type of an article |
author | required* | Author of an article. * only for articles |
platform | required | Platform of an app |
referrer | required | Referrer domain without TLD |
user_id | optional | Randomized identifier that's issued to a user/device for 12 months. New sessions for the same user reset the duration |
session_id | optional | Randomized session identifier. Expires after 30 minutes of inactivity |
user_status | optional | User's status |
country | optional | User's country |
city | optional | User's city |
device | optional | Type of user's device from which a specific article was viewedy |
Example of sending pageviews
metric
https://tt.onthe.io/?k[]={app_id}:pageviews[domain:example.com,url:/article,pr:/article_previous,page:Cows%20introduce%20new%20Milk-themed%20uniforms,category:Sportsub_category:Basketball,type_article:Longread,author:John%20Doe,platform:iOS,referrer:app]&s=a63fa7bd0cb420398c55518ee76b0a3f
# https://tt.onthe.io/
# ?k[]={app_id}:
# pageviews[
# domain:example.com,
# url:/article,
# pr:/article_previous,
# page:Cows%20introduce%20new%20Milk-themed%20uniforms,
# category:Sport,
# sub_category:Basketball,
# type_article:Longread,
# author:John%20Doe,
# platform:iOS,
# referrer:app,
# user_id:e1618d113.9322add28_1615908872955,
# session_id:cd6191734.0d0e2dcb6_1616511533300
# ]
# &s=a63fa7bd0cb420398c55518ee76b0a3f
Track authors using IDs
To track authors of articles using IDs instead of names, please contact your .io manager and provide them a link to a document that will be used for name matching. JSON document should follow the structure from the example, hosted on your end and be openly accessible.
Example of https://example.onthe.io/authors.json
[
{
"1": "John Doe"
},
{
"2": "Peter Parker"
}
]
Browser API
You could use Browser API to send custom events into IO. To enable it, you need to have an additional script tag before the one that loads code from cdn.onthe.io
.
- The value of
window.ioObject
host property could be customized in casewindow.io
is already in use by some other library. window[window.ioObject]
object would be updated once main library code loaded.
After completing the setup, please contact you IO manager to ensure that you are getting the latest version of tracking code and everything configured well.
To enable Browser API
<!-- Additional script tag to enable API, added before the main CDN code -->
<script>
window.ioObject='io';
(function(i){window[i]=window[i]||function(){(window[i].a=window[i].a||[]).push(arguments)}})(window.ioObject);
</script>
<!-- An existing script tag used for general tracking -->
<script async src="https://cdn.onthe.io/io.js/{id}"></script>
Sending events
The window.io
function accepts one argument - an object with the following schema:
Property name | Value description |
---|---|
event | Name of the desired metric. Default supported values: pageviews . |
config_page_url | URL that is displayed in a browser window. Including URL parameters. The same one you use in page_url property of window._io_config object. |
By default, IO tracks page views with each pathname
change (using AJAX or otherwise). If you would like to disable that functionality and track all page views by yourself, please contact your IO manager.
Example of sending pageviews
event
<script>
window.io({
event: 'pageviews',
config_page_url: 'https://example.com/article-url-actual?utm_source=example'
});
</script>