# Chartbeat Documentation

Find documentation to integrate our user-friendly content analytics and optimization tools with ease.

## 📘 Set up Chartbeat tracking

Whether you are implementing Chartbeat tracking for the first time or simply adding Chartbeat to your latest new website, get started with our **integration guide for standard websites:**

{% content-ref url="/pages/-LYD9ZhIIEnHYRqGh7SL" %}
[Adding chartbeat.js to Your Site](/cbp/tracking/standard-websites/our-javascript)
{% endcontent-ref %}

If your teams will also make use of our Headline Testing tool for your website, check out our **headline testing integration guide** next:

{% content-ref url="/pages/-LYDQfr9k8khui4dU8lN" %}
[Adding chartbeat\_mab.js to Your Site](/cbp/feature-integrations/testing/homepage-testing-code)
{% endcontent-ref %}

For all other integration guides, including Google AMP, mobile apps, and more, look through our site menu (left) or search this site using the search bar in the top right corner.

## 💡 Build with our APIs

Gather data from across Chartbeat's suite of tools with our APIs, and add our unique and comprehensive reader data to your internal tools. Your product team can also use our server-to-server connection to make new custom experiences for your readers, like recommendation engines or most-popular modules.

* [Get your historical data](/cbp/api/historical-api)
* [See your real-time data](/cbp/api/real-time-apis)
* [Build a recirculation widget for your site](https://chartbeat.zendesk.com/hc/en-us/articles/360045337214-Guide-to-Chartbeat-APIs#h_3a0daa7c-ca8d-4db2-b54e-f495436525ed)

## 🍎 Learn about Chartbeat

The [**Chartbeat Help Center**](https://chartbeat.zendesk.com) is your go-to resource for learning more about our metrics, tools, and how your teams can make the most of them. From metrics glossaries to product guides, sign-in troubleshooting and FAQs, you can find the answer to most any Chartbeat question there. Here's a list of some of our most viewed help articles to get you started:

* [Enable additional integrations (Slack, Wordpress, Apple News, and more)](https://help.chartbeat.com/hc/en-us/articles/360050985293-What-platforms-does-Chartbeat-integrate-with-)
* [Enable Single Sign On (SSO)](https://help.chartbeat.com/hc/en-us/sections/4403324745243-Single-Sign-On)
* [Chartbeat product guides](https://chartbeat.zendesk.com/hc/en-us/articles/360026405873-Chartbeat-features-detailed-guides)
* [Guide to Real-Time](https://chartbeat.zendesk.com/hc/en-us/articles/360017785694-Guide-to-Real-Time)
* [Real-Time metrics glossary](https://chartbeat.zendesk.com/hc/en-us/articles/208982958-Real-Time-metrics-glossary)
* [Integration FAQs](https://chartbeat.zendesk.com/hc/en-us/sections/201761078-Integration-resources-FAQs)

## 🙋‍♀️ Get Help

Questions? Problems? Need more info? Get in touch! We're glad to help.

{% content-ref url="/pages/-M6Dm88L23MRluk14hru" %}
[Broken mention](broken://pages/-M6Dm88L23MRluk14hru)
{% endcontent-ref %}


# Standard Websites

A guide to integrating Chartbeat tracking for your website.

## Articles included in this guide:

1. [Adding chartbeat.js to Your Site](/cbp/tracking/standard-websites/our-javascript)
2. [Customize Tracking Settings](/cbp/tracking/standard-websites/configuration-variables)
3. [Tracking Virtual Page Change](/cbp/tracking/standard-websites/single-page-apps)
4. [User Subscriber Status](/cbp/tracking/standard-websites/subscriber-engagement)
5. [Alternative Integrations](/cbp/tracking/standard-websites/alternative-integrations-web)
6. [Integration QA Steps: Website](/cbp/tracking/standard-websites/qa-web-integration)
7. [Additional Page Metadata](/cbp/tracking/standard-websites/additional-page-metadata)

{% hint style="success" %}
Chartbeat tracking for websites is a feature included in all customer plans.&#x20;
{% endhint %}


# Adding chartbeat.js to Your Site

An introduction to our analytics code snippet for standard web tracking.

## Our JavaScript tracking snippet

Below is our JavaScript code for standard web tracking, including our recommended [**configuration variables**](/cbp/tracking/standard-websites/configuration-variables). The below script tags should be loaded in your site's global header before the closing `<head>` tag, *after* the `rel="canonical"` link element in your HTML.

The [**UID variable**](/cbp/tracking/standard-websites/configuration-variables#uid), `_sf_async_config.uid`, needs to be assigned to your unique Chartbeat account ID, available from your Account Executive, Customer Success Manager, or the Technical Solutions team at [**support@chartbeat.com**](mailto:support@chartbeat.com).

The [**domain variable**](/cbp/tracking/standard-websites/configuration-variables#domain), `_sf_async_config.domain`, should be assigned to your site ID as it appears in your Chartbeat account — usually your website's root domain. If implementing for more than one domain, the domain variable in this code should also be updated to the site id of each unique site.

```markup
<script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.uid = #####; //CHANGE THIS TO YOUR ACCOUNT ID
        _sf_async_config.domain = 'domain.com'; //CHANGE THIS TO YOUR SITE ID
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.flickerControl = false;
        _sf_async_config.sections = ''; //SET PAGE SECTION(S)
        _sf_async_config.authors = ''; //SET PAGE AUTHOR(S)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
     })();
</script>
```

The above code serves two primary purposes:

1. Sets a list of configuration variables that determine how we track your site. Note that the domain variable *must* be set correctly to the site name(s) in your Chartbeat account for your traffic data to begin populating in our Real-Time and Historical dashboards. All of the other variables included above are optional but strongly recommended to ensure accurate tracking and full functionality of your Chartbeat tools. More details on each of these variables can be found in our [**configuration variables guide**](/cbp/tracking/standard-websites/configuration-variables), up next.
2. Creates a `<script>` element that starts asynchronously downloading the chartbeat.js tracker from `static.chartbeat.com/js/chartbeat.js`.

{% hint style="info" %}
**Note:** For websites that will make use of our Headline Testing and/or Video Tracking integrations, the above snippet will need to be modified. See additional instructions in our [**Headline Testing**](/cbp/feature-integrations/testing) and [**Video Tracking**](/cbp/feature-integrations/video-engagement) integration guides.&#x20;
{% endhint %}

### Data collected

When you add this code to your website, we'll begin tracking all visitors to your site with each new page that is loaded. Immediately after chartbeat.js loads on a given page, it will begin making requests to ping.chartbeat.net every 15 seconds. These "pings" are HTTP requests consisting of field and value pairs encoded as a query string. The first ping of a given page session registers a pageview in Chartbeat, and the subsequent pings carry real-time data about the user's active session on your site so we can tell you precisely when each visitor arrives, how long they engage with your page, and when they make their exit. From this data, Chartbeat can infer information such as:

* The total time a user spends actively engaged with your page.
* What page the visitor arrived from.
* What internal links were clicked.
* The geographic location of the user.
* The device type of the user (mobile/desktop/tablet).
* How far the user scrolls down the page.
* Page title, URL, and max height in pixels.

## Next steps

For a basic implementation and tracking, the above snippet can suffice, but we strongly encourage reviewing each of the articles within this guide prior to implementing our code so you can fine-tune your Chartbeat tracking settings for each of your properties.

The next article in this series explains how to set the [**configuration variables**](/cbp/tracking/standard-websites/configuration-variables) included in the snippet above (lines 5–10), as well as other optional variables that may be appropriate for your integration.


# Customize Tracking Settings

Use our JavaScript configuration variables to fine-tune your pageview tracking and send key page properties to Chartbeat.

## Overview

Our [**JavaScript tag**](/cbp/tracking/standard-websites/our-javascript#our-javascript-tracking-snippet) includes required and recommended configuration variables which determine how we will measure each pageview to your website. When chartbeat.js loads, it collects certain data about the page automatically, such as the page title from `document.title`. Other page properties such as a page's section or author will only be tracked by populating those values via our [**section and author**](/cbp/tracking/standard-websites/configuration-variables#sections-and-authors) configuration variables in your Chartbeat tracking snippet.

Review the descriptions and example values for each of our required and recommended configuration variables listed below to determine the optimal tracking settings for your site.

## Required fields

These variables must be set in your snippet for data to appear in your dashboard.

### Domain

This value should be set to the Chartbeat site id (site name) for your website. This is usually the root domain without http or '[www](http://www).' prepended.

| Field Name | Value Type | Default Value | Max Length | Example Value |
| ---------- | ---------- | ------------- | ---------- | ------------- |
| `domain`   | text       | *None*        | *None*     | `mysite.com`  |

Example usage:

```javascript
_sf_async_config.domain = 'mysite.com';
```

### UID

A numerical id value associated with your Chartbeat account, three to six digits in length. A single organization in Chartbeat with multiple sites has only one UID that can be used in the JavaScript tag for all of the sites belonging to that account. If you do not know your organization's account id, you can request it from your Chartbeat Account Executive, Customer Success representative, or the Technical Solutions team at [**support@chartbeat.com**](mailto:support@chartbeat.com).

| Field Name | Value Type | Default Value | Max Length | Example Value |
| ---------- | ---------- | ------------- | ---------- | ------------- |
| `uid`      | integer    | *None*        | *None*     | `12345`       |

Example usage:

```javascript
_sf_async_config.uid = 12345;
```

## Recommended fields and settings

We recommend that all Chartbeat tracked websites utilize the following variables to pass consistent page URLs and section/author data into your Chartbeat tools: `sections`, `authors`, `useCanonical`, `useCanonicalDomain`.&#x20;

If section or author variables are left unpopulated, your teams will not be able to utilize our section and author dashboard filters and site traffic reports. If your webpage templates do not contain canonical URLs, your team may choose to utilize our [**custom path variable**](/cbp/tracking/standard-websites/configuration-variables#custom-path) instead of the `useCanonical` and `useCanonicalDomain` settings.

### Sections & authors

Our tools prominently feature section and author data and the ability to filter on these values so your users can easily focus on the data that matters for their roles. To make use of this feature, you will need to populate the sections and authors config variables in our snippet with data from your site's data layer.

<table data-header-hidden><thead><tr><th>Field Name</th><th width="142">Value Type</th><th width="138">Default Value</th><th>Max Length</th><th>Example Value</th></tr></thead><tbody><tr><td>Field Name</td><td>Value Type</td><td>Default Value</td><td>Max Length</td><td>Example Value</td></tr><tr><td><code>sections</code></td><td>text</td><td><em>None</em></td><td>10 sections max per page, 63 characters per section</td><td><code>news,sports</code></td></tr><tr><td><code>authors</code></td><td>text</td><td><em>None</em></td><td>10 authors max per pagee, 63 characters per author</td><td><code>Jane Doe</code></td></tr></tbody></table>

Example usage:

```javascript
_sf_async_config.sections = 'US Politics';
_sf_async_config.authors = 'Bob Johnson';
```

These variables accept one or multiple string values parsed by comma, so the examples directly above and below would both be acceptable formats.

```javascript
_sf_async_config.sections = 'News,Sports,Local';
_sf_async_config.authors = 'Megan Summers,Kevin Smith';
```

{% hint style="warning" %}
**Tip:** Chartbeat uses the "|" character for a splitting process when organizing section and author values, so it is recommend to avoid this character when defining these variables.
{% endhint %}

Additional detail:

You can populate these fields dynamically by tying them to a variable in your CMS which stores section and author data for all pages of your site. If your site uses a data layer that contains these values, you can assign those variables as well directly to the `_sf_async_config.sections` or `authors` fields in your Chartbeat tag.

Your team may also use DOM API methods like `document.getElementsByClassName` or `document.location.href.split` to parse these section and author values directly from existing metadata elements in your pages.

If there is no section or author tag needed on a page, set the variable to "no section", "no author", or a blank string (e.g. `_sf_async_config.authors = "").`

### Canonical page path

Use these variables to have chartbeat.js collect your Chartbeat page paths from the canonical URL `<link>` element in your page HTML. `useCanonical` set to `true` tells our tracker to collect the path portion of your canonical URL for the Chartbeat page path. `useCanonicalDomain` set to true tells our tracker to collect the domain portion of your canonical URL as well for the Chartbeat page path.

| Field Name           | Value Type | Default Value | Max Length | Example Value |
| -------------------- | ---------- | ------------- | ---------- | ------------- |
| `useCanonical`       | boolean    | `false`       | *N/A*      | `true`        |
| `useCanonicalDomain` | boolean    | `false`       | *N/A*      | `true`        |

Example usage:

```javascript
_sf_async_config.useCanonical = true;
_sf_async_config.useCanonicalDomain = true;
```

Additional detail:

Web analytics tools rely on a single value sent from each page of your site as the unique page identifier to enable traffic data aggregation at the page level. In Chartbeat, this field element is the **Chartbeat** **page path** which we collect from each page of your site where our tracking code is configured. Most often, the page path consists of the domain name and path portion of your website URLs. For example, **`yourdomain.com/index.html`** would be the expected Chartbeat page path we receive from a visitor who views this page in their browser:

```
 https://www.yourdomain.com/index.html?source=email
```

Duplicate page tracking is a common issue for web analytics implementations, frequently caused by inconsistent page paths collected by the tracker across unique page views for the same article. This leads to inaccurate traffic reports as metrics for a single page of your site end up attributed across multiple separate pages in your analytics data. The `useCanonical` and `useCanonicalDomain` variables provide a simple mechanism for sending consistent URLs as your Chartbeat page paths across all pages of your site. To use these variables, your site HTML should already include [**canonical links**](https://support.google.com/webmasters/answer/139066?hl=en\&rd=1) for every page. It's important that a given page which can be accessed via multiple unique URLs maintain an unchanging canonical URL set in the HTML of that page and all its versions.&#x20;

{% hint style="success" %}
**Tip:** We strongly encourage implementing canonical links in your site HTML to ensure consistent tracking of pages and to prevent seeing duplicate listings of the same page in your reports. If you're not familiar with canonical links, check out [**Google's Guide to Canonical Links**](https://support.google.com/webmasters/answer/139066?hl=en\&rd=1).
{% endhint %}

If you have a mobile site on a separate subdomain, m.domain.com for example, but you're setting canonical URLs on these pages to the desktop URL (not including the 'm.' subdomain), using these recommended canonical path variables in your Chartbeat configuration should ensure that we properly aggregate data for pages across your mobile and desktop instead of tracking them as separate pages with unique subdomains.

{% hint style="success" %}
**Tip:** To prevent duplicate page entries in your Chartbeat reports, a unique article or landing page on your site should send us the same unchanging canonical URL across all Chartbeat-tracked platforms where a user can access that page.
{% endhint %}

If you are unable or prefer to not use canonical links for your website, you may alternatively use our [**custom path variable**](/cbp/tracking/standard-websites/configuration-variables#custom-path) to assign custom Chartbeat page paths (more information below).

## Additional fields and settings

### Custom path

Use this variable **only** if your team is unable or prefers to not use [**canonical URL variables**](/cbp/tracking/standard-websites/configuration-variables#canonical-page-path) above. You may use this path variable to assign consistent page URLs in your Chartbeat data. We strongly recommend that you use standard, canonical page paths for your site instead of passing in custom page id values.

| Field Name | Value Type | Default Value | Max Length | Example Value  |
| ---------- | ---------- | ------------- | ---------- | -------------- |
| `path`     | text       | *None*        | *None*     | `site.com/foo` |

Example usage:

```javascript
_sf_async_config.path = 'domain.com/news/article-xyz.html';
```

### Page title

By default, chartbeat.js collects page titles from `document.title`. Use this variable **only** if you need to override the default page title collected by our tracker (the value contained in the `<title>` tags in your page HTML).&#x20;

| Field Name | Value Type | Default Value | Max Length     | Example Value |
| ---------- | ---------- | ------------- | -------------- | ------------- |
| `title`    | text       | *None*        | 100 characters | `My Title`    |

Example usage:

```javascript
_sf_async_config.title = 'Live Updates: 2020 Election';
```

### Content type

Use this variable **only** if your team utilizes Advanced Queries or Data Lab and would benefit from audience engagement reports in Chartbeat segmented by your custom content-type values, such as 'gallery page', 'standalone video page', 'article page', etc. **Note** that this variable only accepts one value — it does not parse on commas like our section and author variables.

| Field Name | Value Type | Default Value | Max Length    | Example Value |
| ---------- | ---------- | ------------- | ------------- | ------------- |
| `type`     | text       | *None*        | 64 characters | `video page`  |

Example usage:

```javascript
_sf_async_config.type = 'photo gallery page';
```

### Mobile app

Use this variable **only** if your mobile app for smartphone devices uses web views to display your mobile website content pages. `mobileApp` should be set to true for users of your webview mobile app in order to distinguish app users from mobile website visitors in your Chartbeat reports.

{% hint style="warning" %}
**Important:** Chartbeat tracking for mobile apps is an add-on feature. Contact your Chartbeat Customer Success representative or [**support@chartbeat.com**](mailto:support@chartbeat.com) to learn more.
{% endhint %}

| Field Name  | Value Type | Default Value | Max Length | Example Value |
| ----------- | ---------- | ------------- | ---------- | ------------- |
| `mobileApp` | boolean    | `false`       | *None*     | `true`        |

Example usage:

```javascript
_sf_async_config.mobileApp = true;
```

### No cookies

Use this variable **only** to prevent our JavaScript from using first-party cookies. Note that we only use first-party cookies, and disabling these will prevent headline testing functionality in addition to other Chartbeat measurements like visitor frequency. Learn more about using our cookieless mode on your site [**here**](/cbp/tracking/standard-websites/alternative-integrations-web#implement-cookieless-tracking).

| Field Name  | Value Type | Default Value | Max Length | Example Value |
| ----------- | ---------- | ------------- | ---------- | ------------- |
| `noCookies` | text       | `false`       | *None*     | `true`        |

Example usage:

```javascript
_sf_async_config.noCookies = true;
```

### Path alias

Use this variable **only** if the article URLs linked in your homepage HTML do not match the expanded URLs *nor* the Chartbeat page paths of those target article pages. Set `alias` on the article pages to the full URL as it appears in your homepage HTML to ensure proper referrer tracking as visitors move from your homepage to these article pages. This should only be needed to resolve missing pin issues in our Heads Up Display tool.

| Field Name | Value Type | Default Value | Max Length | Example Value  |
| ---------- | ---------- | ------------- | ---------- | -------------- |
| `alias`    | text       | *None*        | *None*     | `site.com/foo` |

Example usage:

```javascript
_sf_async_config.alias = 'mysite.com/article-xyz.html';
```

### Sponsor name

Use this variable **only** if your team utilizes our Advanced Queries tool and would benefit from audience engagement reports in Chartbeat segmented by your sponsored content (advertorial) campaign name IDs, such as 'Mercedes', 'Mastercard-Jun2020', 'Netflix-Q1-2021-CID123', etc. Note that this variable only accepts one value — it does not parse on commas like our section and author variables.

| Field Name    | Value Type | Default Value | Max Length    | Example Value |
| ------------- | ---------- | ------------- | ------------- | ------------- |
| `sponsorName` | text       | *None*        | 64 characters | `Mercedes`    |

Example usage:

```javascript
_sf_async_config.sponsorName = 'Spotify';
```

### Scroll element

Use this variable **only** for pages with custom scrollable elements where our tracking script will not automatically measure scrolled pixel depth of a user scrolling through the embedded article. You will also need to need to add a special attribute to your scrollable div so our tracker can find it on the page: `data-cb-scroll-element="1"`

| Field Name      | Value Type | Default Value | Max Length | Example Value |
| --------------- | ---------- | ------------- | ---------- | ------------- |
| `scrollElement` | boolean    | `false`       | *None*     | `true`        |

Example usage:

```javascript
_sf_async_config.scrollElement = true;
```

```markup
<div data-cb-scroll-element="1">
```

### Cookie domain

Use this variable **only** on a page using a subdomain (e.g. subdomain.mysite.com) when you want visitor  cookies to be assigned to the subdomain and not the root domain (e.g. mysite.com). To learn more about whether this setting is right for your site, check out our [**Help Center article**](https://help.chartbeat.com/hc/en-us/articles/4404408150171-August-12-2021-Change-in-scope-of-Chartbeat-s-first-party-cookies-on-customer-websites) on this topic.

If your site uses multi-level subdomains, like sub2.sub1.mysite.com, the cookie domain can be set to sub2.sub1.mysite.com but it cannot be set to sub1.mysite.com.

| Field Name     | Value Type | Default Value | Max Length | Example Value          |
| -------------- | ---------- | ------------- | ---------- | ---------------------- |
| `cookieDomain` | text       | *None*        | *None*     | `subdomain.mysite.com` |

Example usage:

```javascript
_sf_async_config.cookieDomain = 'subdomain.mysite.com';
```

### IP truncation

For clients in the EU, add this additional config value to your Chartbeat snippet in order to point traffic to our proxy layer that de-identifies IP addresses within the EU:&#x20;

| Field Name   | Value Type | Default Value          |
| ------------ | ---------- | ---------------------- |
| `pingServer` | text       | `'pong.chartbeat.net'` |

Example usage:

```javascript
_sf_async_config.pingServer = 'pong.chartbeat.net';
```

## Next steps

Use the above guide to review your site's [**canonical URL**](/cbp/tracking/standard-websites/configuration-variables#page-ids-canonical-urls-vs-custom-paths) implementation, and identify how best to configure our [**section and author**](/cbp/tracking/standard-websites/configuration-variables#page-sections-and-authors) variables for your website.

For sites that make use of [**virtual page change**](/cbp/tracking/standard-websites/single-page-apps) (single page apps and sites with infinite scroll page change) use the instructions in the next article to implement our virtual page change tracking method. If your site does not make use of virtual page change, you may skip to our [**subscriber engagement**](/cbp/tracking/standard-websites/subscriber-engagement) article or [**alternative implementations**](/cbp/tracking/standard-websites/alternative-integrations-web) article if your site does not allow users to subscribe or create user profiles.


# Tracking Virtual Page Change

This article covers using our virtualPage function to track virtual page change on your site. This implementation is only required for sites that use virtual page changes.

## Single page apps & virtual page change implementation

If your site is a single-page application (SPA), uses infinite scroll, serves up content dynamically, or pages change without the DOM refreshing, you will need to do some additional steps to capture those pageviews.

Anytime a visitor internally navigates to a new page via virtual page change, the `virtualPage` function should be called. This function is specifically designed to register dynamic page change events with our tracker and can be attached to click/swipe events, or to a pixel that is used to trigger content changes.&#x20;

Best practice is to ensure that the `virtualPage` function is called each and every time that your website serves up new changes to users dynamically. Each time the function is called.

You’ll need to set up logic to handle these lines of code:

```javascript
pSUPERFLY.virtualPage({
      sections: "New Section 1,New Section 2",
      authors: "New Author",
      path: "/newpath",
      title: "New Title"
    });
```

The section, author, path and title of the newly-loaded page must be consistent with those values [**configured by your team**](/cbp/tracking/standard-websites/configuration-variables) in our JavaScript tag. If we are not passed the updated section, author, path, or title information for the new page, our tracker will attribute the values received from the user's previous page to this new pageview triggered by the `virtualPage` call.&#x20;

If for example the dynamically loaded page has no section or author data, simply set that variable to an empty string to prevent our tracker from associating metadata from the previously viewed page of your site with the newly loaded page. For example:

```javascript
authors:"",
```

`virtualPage` should never be called when a user initially arrives on a page from an external source, and should only be called when a user navigates to subsequent pages without causing the DOM to be refreshed. The initial pageview event will be captured instead by our tracker, [**chartbeat.js**](/cbp/tracking/standard-websites/our-javascript).

### Additional Variables

<table><thead><tr><th width="207">Name</th><th width="423">Description</th></tr></thead><tbody><tr><td><code>virtualReferrer</code></td><td>This variable can only be used to override the referring page URL for internally-referred pageviews for <code>virtualPage</code> calls  (e.g. <code>virtualReferrer: "site.com/path"</code>)</td></tr></tbody></table>

### Optional: End Tracking

<mark style="color:red;">**Note: Most integrations will never need to use the**</mark><mark style="color:red;">**&#x20;**</mark><mark style="color:red;">**`endTracking()`**</mark><mark style="color:red;">**&#x20;**</mark><mark style="color:red;">**function**</mark>

If your website has virtual pages you don't wish to track please call `pSUPERFLY.endTracking()` when a user arrives to the non-tracked page via an internal page on your website. If the user then moves on to another dynamic page (i.e. a page that loads without a page refresh) and you want to track it, call `virtualPage` again for that new dynamic page.

**Sample flow:**

<table><thead><tr><th width="78.33333333333331">Step</th><th>User Action</th><th>Chartbeat Action</th></tr></thead><tbody><tr><td>1</td><td>User arrives on homepage from an external source</td><td>The chartbeat.js tracker handles the first pageview; <code>virtualPage</code> should <em>not</em> be called.</td></tr><tr><td>2</td><td>User moves from homepage to a dynamic article page (i.e. an article page that loads without a page refresh)</td><td>Call <code>virtualPage</code> for the new page</td></tr><tr><td>3</td><td>User moves from the article page to a <strong>dynamic</strong> page you don't want to track. </td><td>Call <code>pSUPERFLY.endTracking()</code> on the dynamic page you don't want to track. <em>Note: This non-tracked page should also not load our chartbeat.js snippet</em></td></tr><tr><td>4</td><td>User moves from non-tracked page to another <strong>dynamic</strong> page you want to track</td><td>Call <code>virtualPage</code> for the new page</td></tr></tbody></table>

## Next steps

The next article covers how to populate users' subscriber status data in your Chartbeat snippet so your team can filter on Guest, Registered, or Subscribed visitors to your website in our Real-time Dashboard.


# User Subscriber Status

This article details the steps to implement your site's user subscriber status data into Chartbeat by adding a few lines to our standard tracking tag.

## Tracking users' subscriber status

If you allow users to login to your site, or provide subscriber-only content, add the below configuration to your Chartbeat tag to enable traffic data segmentation by user-type in our tools. First, you'll need to declare a global `_cbq` variable with the following code:

```javascript
var _cbq = window._cbq = (window._cbq || []);
```

Then, based on what type of user is currently browsing your site, push the following array elements that constitute a key-value pair to the `_cbq` array:

```javascript
_cbq.push(['_acct', 'user_type']);
```

Always push `_acct` as the first element in the array, with that representing the key of a key-value pair. The second element in the array represents the value of the key-value pair. For the `user_type` value, please use one of the three standardized values below:

| Subscriber Status | user\_type Value |
| ----------------- | ---------------- |
| Guest             | `anon`           |
| Registered        | `lgdin`          |
| Subscriber        | `paid`           |

Generally, it's best practice to declare `_cbq` and perform your `_cbq.push()` right after your `_sf_async_config` setup. For example, if you wanted to push a user type of `paid`, your implementation might look like this (see lines 11 & 12):

```markup
<script type='text/javascript'>
    (function() {
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        /** CONFIGURATION START **/
        _sf_async_config.uid = #####; //CHANGE THIS
        _sf_async_config.domain = 'domain.com'; //CHANGE THIS
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        var _cbq = window._cbq = (window._cbq || []);
        _cbq.push(['_acct', 'paid']);
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
     })();
</script>
```

It's up to your team to create the custom logic to automatically pass in one of our three user-type values each time our tag is loaded, based on the current user's status in your site's user tracking mechanism.

{% hint style="success" %}
**Tip:** If your user subscriber status data only becomes available later in the page load, you can load the Chartbeat tag lower in the page HTML instead of placing our tag in the page's header. However, if your site will utilize our Headline Testing integration where some of our code is required to load before the closing `<head>` tag, we suggest using our [**two snippet solution**](/cbp/tracking/standard-websites/alternative-integrations-web#loading-our-code-in-the-less-than-body-greater-than) detailed in the next page of this guide.
{% endhint %}

## Next steps

You've learned about our JavaScript tag and tracking script, how to customize your pageview tracking settings with our configuration variables, tracking virtual pageviews, and user subscriber status. Now review our options for alternative integrations of our website tracking code in the next page of this guide to see if any of these options apply to your setup.


# Alternative Site Integrations

This page details alternative options for implementing our tracking snippet to better fit your site's existing model for loading third-party JavaScript.

## Using tag managers

We do not currently offer pre-built templates with our standard JavaScript tracking snippet for any tag manager platforms. However, teams that would prefer to use a tag manager to load our code can simply create a custom tag in their chosen platform, following the instructions from [**page 1**](/cbp/tracking/standard-websites/our-javascript) of this guide to load our snippet as early in the page load sequence as possible, ideally in the header.

If you are not able to load our snippet in your site's header as a limitation of your tag manager settings, it is acceptable for sites that are not making use of our Headline Testing functionality to load our full JavaScript snippet in the `<body>`.

### Two snippet solution

For sites that will use our Headline Testing tool, there's a portion of our JavaScript code that needs to load  in the `<head>`. In this case, we recommend splitting our code into the following two snippets with the first snippet being implemented directly in your source code, after canonical URLs are defined in your header HTML.

#### Header snippet

```markup
<script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.uid = #####; //CHANGE THIS
        _sf_async_config.domain = 'domain.com'; //CHANGE THIS
        _sf_async_config.flickerControl = false;
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        /** CONFIGURATION END **/
     })();
</script>
<script async src="//static.chartbeat.com/js/chartbeat_mab.js"></script>
```

The above code needs to load early in the `<head>` after canonical URLs are loaded because it is responsible for altering the headline text in your homepage template to display test variant headlines to your visitors.

The below code includes the rest of our tracking snippet, and can be loaded via a custom tag near the top of the `<body>` , or whenever your data layer is available with the necessary [**section & author**](/cbp/tracking/standard-websites/configuration-variables#sections-and-authors) or [**user subscriber status**](/cbp/tracking/standard-websites/subscriber-engagement) data.

#### Body snippet

```markup
<script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
     })();
</script>
```

## Loading our tag in the `<body>`

We recommend the same configuration as described in the instructions [**directly above**](/cbp/tracking/standard-websites/alternative-integrations-web#tag-managers-and-headline-testing) for sites using Headline Testing *without* a tag manager, where certain required page data like section & author or subscriber data is not available to our JavaScript if it were placed in the `<head>`. For these sites, we also recommend implementing the above [**header snippet**](https://app.gitbook.com/@chartbeat/s/developer/~/drafts/-LgkDqy7mJk1_WU0d13u/primary/integration-guides/web-tracking/alternate-implementations#header-snippet) (containing our Headline Testing JS) separately in the `<head>` after canonical URLs are loaded. [**The rest of our code**](/cbp/tracking/standard-websites/alternative-integrations-web#body-snippet) can be called later in the page load sequence, whenever your preferred page data is available.

If your site will *not* make use of your Headline Testing feature, our full snippet can be called later in the page load sequence, skipping the steps above to split the snippet in two. Note that calling our tracking script later in the page load sequence may cause missed visitor pageviews which start and end prior to page load completion.

## GDPR & CCPA compliance

As a first stop resource for understanding how Chartbeat tracking complies with GDPR regulations, check out our [**Data Protection resources page**](https://chartbeat.com/data-protection/).

**Chartbeat does not set third-party cookies.** We also do not take any steps to identify specific visitors (for example, we do not do browser fingerprinting or collect device IDs). Chartbeat was designed with privacy in mind, and is a first-party analytics platform. This means that publishers who use our service can set [**first-party cookies**](https://chartbeat.zendesk.com/hc/en-us/articles/360044659014) on their sites using Chartbeat code, or they can run in [**cookieless mode**](/cbp/tracking/standard-websites/configuration-variables#no-cookies).

### Implement cookieless tracking

Customers who would prefer not to use cookies can set an additional configuration variable in their Chartbeat snippet prior to chartbeat.js loading to prevent our JavaScript from using first-party cookies:

```javascript
_sf_async_config.noCookies = true;
```

**Instead of running our tracking in cookieless mode across your site, we recommend that teams implement conditional logic to prevent Chartbeat cookies&#x20;*****only*****&#x20;for users that have not yet provided consent to our cookies**. Cookies are required for the our Headline and Image Testing tool to work, as well as our Unique Visitors and Visitor Frequency metrics, so using our cookieless mode *conditionally*— only for users who have not provided consent to cookies— allows your team to use our homepage testing tools and other metrics that rely on cookies for the subset of your website visitors that accept cookies. If your site uses a cookie consent banner which allows users to provide explicit consent by clicking a button to agree, or inexplicit consent by clicking into an additional page of your site, we recommend making use of our cookieless tracking mode as follows:&#x20;

* Hook into your site's unique logic to recognize new un-cookied users to your site, and add conditional logic to your analytics tags that sets our `noCookies` configuration variable to true for these pageviews *only.*&#x20;
* As soon as the visitor provides consent by accepting cookies, your custom code should recognize the cookied user and no longer set the `noCookies` variable in your Chartbeat snippet.
* &#x20;For visitors that deny cookies, your code should continue setting the `noCookies = true` variable in your Chartbeat snippet for all of these users' subsequent views, until they update their cookie preferences to allow Chartbeat's cookies.

{% hint style="success" %}
**Tip:** If you choose to implement the above configuration, also ensure that our headline testing script, chartbeat\_mab.js, does not get loaded in the `<head>` for pageviews where the user has not yet provided consent to cookies. This way, you can still load this code for users who have already provided consent and our Headline Testing tool will still work for this user group.
{% endhint %}

## IP Truncation

Clients in the EU can point traffic to our proxy layer that de-identifies IP addresses within the EU. Please refer to our platform-specific integration instructions for more details:&#x20;

* [Standard web](/cbp/tracking/standard-websites/configuration-variables#ip-truncation)
* [Google AMP](/cbp/tracking/google-amp/amp-configuration-variables#ip-truncation)
* Mobile SDKs - [iOS](/cbp/tracking/mobile-app-sdks/ios-sdk#ip-truncation) and [Android](/cbp/tracking/mobile-app-sdks/android-sdk#ip-truncation)

## Legacy code snippet

Some of our partner sites that have been with us for a while are using our previous snippet for basic tracking functionality, included below. The primary differences between our previous recommended code and our [**current snippet**](/cbp/tracking/standard-websites/our-javascript) are twofold:

1. This snippet includes code that delays chartbeat.js from being called until the page has finished loading, on the `window.onload` or `document.ready` event. Our new snippet loads chartbeat.js immediately, asynchronously.&#x20;
2. We previously recommended loading our snippet near the bottom of the `<body>`. We've since adjusted our default implementation to load our script early in the `<head>` to ensure we capture brief user pageviews which end prior to page load completion.

For partners still using this older version of our tracking code, it will still work but we recommend updating to our latest implementation specs for the reasons detailed above.

#### Legacy code snippet (for reference only)

```markup
<script type='text/javascript'>   
    var _sf_async_config = _sf_async_config || {};
    /** CONFIGURATION START **/
    _sf_async_config.uid = #####; //CHANGE THIS
    _sf_async_config.domain = 'domain.com'; //CHANGE THIS
    _sf_async_config.useCanonical = true;
    _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
    _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
    /** CONFIGURATION END **/
    (function() {
        function loadChartbeat() {
            var e = document.createElement('script');
            e.setAttribute('language', 'javascript');
            e.setAttribute('type', 'text/javascript');
            e.setAttribute('src', '//static.chartbeat.com/js/chartbeat.js');
            document.body.appendChild(e);
        }
        var oldonload = window.onload;
        window.onload = (typeof window.onload != 'function') ?
            loadChartbeat : function() {
                oldonload();
                loadChartbeat();
            };
    })();
</script>
```

## Next steps

Having reviewed all of the previous articles in this guide, identifying the configuration options and tracking methods that best fit your site, you're now ready to add our snippet to your website using our integration QA article (up next) to check your work for completeness.&#x20;


# Integration QA Steps: Website

Steps to check that your website integration is working as expected.

## Configure QA/staging sites in Chartbeat

If you are adding new Chartbeat code to a website, we recommend first adding our code in a controlled QA/staging environment. The recommended way to handle this is to have the `domain` value in your Chartbeat tag set to your staging site ID (e.g. staging.mysite.com) when running on staging, and your production site ID (e.g. mysite.com) when in production.&#x20;

So the production code line would be:

```javascript
_sf_async_config.domain = "mysite.com";
```

And the staging code would be:

```javascript
_sf_async_config.domain = "staging.mysite.com";
```

This way, traffic data from your integration QA tests will stay confined in a separate test site in Chartbeat.

{% hint style="info" %}
**Note**: The test Site will have to be added to your organization's Chartbeat account to test your data in this sandbox dashboard. If you run into any issues adding this sandbox site to your account, contact [**support@chartbeat.com**](mailto:support@chartbeat.com) and we'll get it added for you.
{% endhint %}

## Check your Implementation Review

Our Implementation Review lets Admin users view data snapshots from the latest full calendar day we have on hand.

Please note that a 5-10% difference between platforms is generally expected. If data is not coming through as expected, check out common issues in troubleshooting pageviews and unique discrepancies page.

For details on general use of the dashboard please see our Implementation Review Introduction in our documentation &#x20;

#### To access your Implementation Review:

1. [**Sign in**](https://chartbeat.zendesk.com/hc/en-us/articles/360017718093) to your Chartbeat account.&#x20;
2. Navigate to Settings from the menu top right header&#x20;
3. Implementation Review pages on the left side menu
4. View the Platform Audit and Traffic Audit Tabs

{% hint style="warning" %}
**Tip:** If you cannot access the Implementation Review, verify with your organization if your account has the required Admin role. &#x20;
{% endhint %}

Once you're able to access the dashboard, here are some useful QA steps to ensure proper tracking:

#### **Platform Audit**

1. Navigate to the [Platform Audit Tab](https://chartbeat.com/publishing/settings/implementation-review/platform-audit) to view the Implementation Summary Table
2. Locate the "**Web**" column as your platform of interest and row for the site

If there is a green checkmark in the corresponding cell, that means Chartbeat is receiving significant web traffic from the site at the time snapshot was taken.

#### **Traffic Audit**

1. Select a site and verify the timezone and snapshot date. &#x20;
2. Summary table: reference Web pageviews breakdown to validate snapshot counts
3. Device breakdown by platform:  Select Web as an option in the dropdown to see data on visitors viewing your site across unique device types.&#x20;
4. Top Articles by pageviews: View the top 20 [canonical paths](/cbp/tracking/standard-websites/configuration-variables#page-ids-canonical-urls-vs-custom-paths) and pageviews across all platforms

If data in the Implementation Review snapshots are as expected, your site is implemented correctly for Web. If more QA or troubleshooting is needed please continue with the additional QA steps below to assess your implementation.

## Check your Real-Time Dashboard

Our Real-Time dashboard lets you see current activity on your site. If this view shows data after adding our tag to your site and loading your webpage in a browser, then your snippet is collecting data and sending it to Chartbeat as expected. This dashboard displays activity for specific pages, so if you haven’t added the snippet to all pages on your site, you can check check to see whether the snippet is sending data from the pages you have tagged.&#x20;

To check out your Real-Time dashboard:

1. [**Sign in**](https://chartbeat.zendesk.com/hc/en-us/articles/360017718093) to your Chartbeat account.
2. From the [**Sites page**](https://chartbeat.com/publishing/sites/), find the site you've recently implemented, hover over the site entry to reveal the 'Real-Time' button, and select it.

{% hint style="success" %}
**Tip:** If you do not have a Chartbeat user account, request access from one of your account admins.
{% endhint %}

Here are some useful QA steps to ensure proper tracking:

#### Top Pages

* [**Page titles**](/cbp/tracking/standard-websites/configuration-variables#page-titles) are populating as expected
* Hover over page titles in this list to view the [**canonical paths**](/cbp/tracking/standard-websites/configuration-variables#page-ids-canonical-urls-vs-custom-paths) received from these pages. Alternatively, click into the article view and right-click the title to copy the link address
* Check for duplicate page entries in this list, and identify the discrepant paths being received from these pages to determine the fix in your [**configuration settings**](/cbp/tracking/standard-websites/configuration-variables)

**Other Real-Time QA Checks**

* Open the section & author drop-down filters to ensure [**section & author data**](/cbp/tracking/standard-websites/configuration-variables#page-section-and-author) is populating as expected
* Check the Device module in the left-hand column to ensure data is tracking for visitors viewing your site across unique device types

## Use DevTools to monitor pings

You can use your browser's site inspector tools to monitor network activity from your site, filtering on "chartbeat" to view all chartbeat related scripts loading on the page, or on "ping?" to find requests to URLs beginning with **ping.chartbeat.net**.

Upon loading a page of your site, you should be able to view the requests here for chartbeat\_mab.js (if you've included our headline testing code), chartbeat.js (or chartbeat\_video.js), and the subsequent pings to our servers every 15 seconds, carrying most of the data we collect from your site.&#x20;

Clicking into one of these ping requests to ping.chartbeat.net in DevTools will display a list of the included query string parameters in this call which contain data determined by the configuration variables in your code snippet. Below is a ping key that describes some of these query string fields that are important to check for in your review.

| QSP/Key | Description                                                                                                                                                                                                                                                                                                                                                            |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| h       | The site id, or "host" where your site's traffic data will appear in your Chartbeat account. This string is pulled from the value assigned to the`config.domain` variable in our snippet.                                                                                                                                                                              |
| p       | The page's path. This string should match your pages' canonical URL paths if your snippet sets `config.useCanonical = true`, or it should match the value assigned to `config.path` if your site makes use of our custom path variable. Sometimes this value is just the path portion of the URL, and sometimes it includes your domain name as well.                  |
| d       | With this key, we collect the actual domain of your page, which may be different from the h-key host/site name in your Chartbeat account. If `config.useCanonicalDomain` is set to `true` in your Chartbeat code, this key should carry the domain set in your canonical URL. If that variable is not set to true, we will access the domain from `document.location`. |
| g       | Account ID, pulled from the value assigned to `config.uid` in our snippet.                                                                                                                                                                                                                                                                                             |
| g0      | A comma-delimited list of sections received from the page via the `config.sections` variable in our snippet.                                                                                                                                                                                                                                                           |
| g1      | A comma-delimited list of authors received from the page via the `config.authors` variable in our snippet.                                                                                                                                                                                                                                                             |
| i       | Page title. Only appears in the very first ping from each unique page session on your site.                                                                                                                                                                                                                                                                            |
| r       | Will carry the referring URL of the current pageview IF the referring page is an external site.                                                                                                                                                                                                                                                                        |
| v       | Will carry the referring URL of the current pageview IF the referring page is your own site.                                                                                                                                                                                                                                                                           |
| t       | Unique identifier for the page-session. Each unique t-key value represents a separate pageview in your Chartbeat data.                                                                                                                                                                                                                                                 |

## Next steps

Your website integration is now complete! 🎉

One final request before you jump to your next integration guide (see [**Google AMP**](/cbp/tracking/google-amp), [**Mobile App SDKs**](/cbp/tracking/mobile-app-sdks), [**Headline and Image Testing**](/cbp/feature-integrations/testing), or [**Video Tracking**](/cbp/feature-integrations/video-engagement) next)...

We're starting to automatically collect even more metadata about your webpage content to power our next-generation products. Review the final page of this guide to get a head start on what's next for Chartbeat.


# Additional Page Metadata

Learn how we're beginning to collect metadata about your content to power our next generation products, and get a head start by configuring this metadata in your page HTML.

## Introduction

{% hint style="warning" %}
**Important:** We automatically collect metadata from your webpages as described below, and we're using it to inform future product development at Chartbeat. Some of this data is now being used in our [Topics and Categories tabs](https://help.chartbeat.com/hc/en-us/articles/16574320245787) in the Historical Dashboard.

We rely on your integration of our [JavaScript configuration variables](/cbp/tracking/standard-websites/configuration-variables) to populate key page properties in your Chartbeat dashboards and reports, like page sections, authors, paths, and titles.
{% endhint %}

To collect metadata about the content of a given page, Chartbeat uses a web scraper that makes an HTTP GET request to the page’s URL, then extracts certain attributes from the page’s HTML as well as the main body text. For convenience and ease of implementation, we rely on existing standards as much as possible. Metadata may be provided in the following formats:

* **JSON-LD (*****recommended*****):** Embed metadata in a `<script>` tag in the `<head>` of an HTML document, with properties and values in JSON form following the [**schema.org**](https://schema.org/) vocabulary. This standard is used by Google and others to display enriched content in search results.
* **Microdata:** Embed metadata within existing elements of an HTML document's `<head>` or `<body>`, using HTML tag properties and values following the [**schema.org**](https://schema.org/) vocabulary. This standard is also used by Google and others to display enriched content in search results.
* **Repeated `<meta>` tags:** Embed metadata in multiple `<meta>` tags in the `<head>` of an HTML document, using HTML tag properties and values following the [**Open Graph protocol**](https://ogp.me/). This standard is used by Facebook and others to display enriched content in social media posts.

## JSON-LD

The JSON-LD format embeds structured data in JSON form following a flexible, expressive vocabulary developed and maintained by [**schema.org**](https://schema.org/). Chartbeat mostly follows the official specification, but allows alternate forms of some properties for your convenience. For more info, see the [**official JSON-LD site**](https://json-ld.org/) and [**Google's structured data guide**](https://developers.google.com/search/docs/guides/search-gallery).

### **Examples**

Here's a basic example that follows the standard spec:

```markup
<script type="application/ld+json">
  {
    "@context": "http://schema.org",
    "@type": "NewsArticle",
    "headline": "Data, platforms, and subscriptions: What you read (by Total Engaged Time) in 2019",
    "description": "We dove into our own Historical Dashboard to understand the topics that garnered the most total engaged time on the Chartbeat blog in 2019. See more on our website.",
    "articleBody": "In December, we showed you where readers spent over 294 billion minutes of Total Engaged Time through our 100 Most Engaging Stories...",
    "url": "https://blog.chartbeat.com/2020/01/16/total-engaged-time-chartbeat-blog-data/",
    "datePublished": "2020-01-16T11:07:35-05:00",
    "articleSection": "Blog,Customer",
    "author": {
        "@type": "Person",
        "name": "Nick Lioudis",
    },
    "publisher": {
        "@type": "Organization",
        "name": "Chartbeat",
    },
    "keywords": "2019,audience,data,engagement",
    "thumbnailUrl": "http://4ehuia1v75h912e6wht7ul1m-wpengine.netdna-ssl.com/wp-content/uploads/2017/09/Historical@2x.png",
  }
</script>
```

And here's the same example, using alternate properties and Chartbeat's more permissive spec:

```markup
<script type="application/ld+json">
  {
    "@context": "http://schema.org",
    "@type": "NewsArticle",
    "headline": "Data, platforms, and subscriptions: What you read (by Total Engaged Time) in 2019",
    "description": "We dove into our own Historical Dashboard to understand the topics that garnered the most total engaged time on the Chartbeat blog in 2019. See more on our website.",
    "articleBody": "In December, we showed you where readers spent over 294 billion minutes of Total Engaged Time through our 100 Most Engaging Stories...",
    "mainEntityOfPage": {
        "@type": "WebPage",
        "@id": "https://blog.chartbeat.com/2020/01/16/total-engaged-time-chartbeat-blog-data/",
    },
    "dateCreated": "2020-01-16 11:07:35-05:00",
    "articleSection": ["Blog", "Customer"],
    "creator": ["Nick Lioudis"],
    "publisher": "Chartbeat",
    "keywords": ["2019", "audience", "data", "engagement"],
    "image": {
        "@type": "ImageObject",
        "url": "http://4ehuia1v75h912e6wht7ul1m-wpengine.netdna-ssl.com/wp-content/uploads/2017/09/Historical@2x.png",
    }
  }
</script>
```

### **Properties**

#### Required properties:

* `@context`: Collection in which the schema is defined. Should always be "<http://schema.org>" or "<https://schema.org>".
* `@type`: Type of schema used. For articles, should be one of [**Article**](https://schema.org/Article), [**TechArticle**](https://schema.org/TechArticle), [**NewsArticle**](https://schema.org/NewsArticle) (or one of its sub-types), [**BlogPosting**](https://schema.org/BlogPosting) (or one of its sub-types).

#### Key properties:

* `articleSection` ([**Text**](https://schema.org/Text)): One or more sections assigned to the article. Multiple sections may be expressed as a single string delimited by commas or as an array of strings.
* `author` ([**Text**](https://schema.org/Text), [**Person**](https://schema.org/Person), or [**Organization**](https://schema.org/Organization)): One or more authors of the article. If not specified, the `creator` property is checked. Multiple authors may be expressed as a single string delimited by commas, an array of strings, or an array of Person or Organization items.
* `headline` ([**Text**](https://schema.org/Text)): Title of the article. If not specified, the `alternativeHeadline` and `name` properties are checked, in that order.
* `datePublished` ([**Date**](https://schema.org/Date) or [**DateTime**](https://schema.org/DateTime)): Date and (optionally) time the article was first published. If not specified, the `dateCreated` property is checked. This value shouldn't change over time, and should be as early or earlier than the `dateModified` property.
* `keywords` ([**Text**](https://schema.org/Text)): One or more subject matter tags associated with the article. Multiple keywords may be expressed as a single string delimited by commas or as an array of strings.
* `thumbnailUrl` ([**Text**](https://schema.org/Text)): URL of the main image associated with the article. If not specified, the `image` property ([**Text**](https://schema.org/Text) or [**ImageObject**](https://schema.org/ImageObject)) is checked.
* `url` ([**Text**](https://schema.org/Text)): Canonical URL of the article.

#### Additional properties:

* `articleBody` ([**Text**](https://schema.org/Text)): Full text of the article. If not specified, the `text` property is checked.
* `dateModified` ([**Date**](https://schema.org/Date) or [**DateTime**](https://schema.org/DateTime)): Date and (optionally) time the article was last modified.
* `description` ([**Text**](https://schema.org/Text)): Brief description of the article.
* `lang` ([**Text**](https://schema.org/Text)): Language code or locale of the article's content.
* `publisher` ([**Text**](https://schema.org/Text), [**Person**](https://schema.org/Person), or [**Organization**](https://schema.org/Organization)): Name of the article's publisher.
* `wordCount` ([**Number**](https://schema.org/Number)): Number of words in the full text of the article.

## **Microdata**

The Microdata format embeds structured data into existing HTML elements throughout the document using tag properties specified by the [**schema.org**](https://schema.org/) vocabulary. Chartbeat mostly follows the official specification, but allows alternate forms of some properties for your convenience. For more info, see the [**official Microdata specification**](https://www.w3.org/TR/microdata/) and [**Google's structured data guide**](https://developers.google.com/search/docs/guides/search-gallery).

### **Examples**

Here's a basic example that follows the standard spec:

```markup
<html>
    <body>
        <div itemscope itemtype="http://schema.org/NewsArticle">
            <h1 itemprop="headline">Data, platforms, and subscriptions: What you read (by Total Engaged Time) in 2019</h1>
            <span itemprop="datePublished" content="2020-01-16T11:07:35-05:00">2020-01-16T11:07:35-05:00</span>
            <span itemprop="description">We dove into our own Historical Dashboard to understand the topics that garnered the most total engaged time on the Chartbeat blog in 2019. See more on our website.</span><br>
            <div itemprop="image" itemscope itemtype="http://schema.org/ImageObject">
                <meta itemprop="url" content="http://4ehuia1v75h912e6wht7ul1m-wpengine.netdna-ssl.com/wp-content/uploads/2017/09/Historical@2x.png">
                <img src="http://4ehuia1v75h912e6wht7ul1m-wpengine.netdna-ssl.com/wp-content/uploads/2017/09/Historical@2x.png">
            </div>
            Author: <span itemprop="author">Nick Lioudis</span><br>
            <div itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
                <span itemprop="name">Chartbeat</span>
            </div>
            <span itemprop="articleBody">In December, we showed you where readers spent over 294 billion minutes of Total Engaged Time through our 100 Most Engaging Stories...</span>
        </div>
    </body>
</html>
```

### **Properties**

Since this format follows the same schema.org vocabulary as JSON-LD, it uses the same properties as described above for JSON-LD. However, those properties are incorporated into HTML differently.

* `itemprop`: Adds a property to an HTML element.
* `itemscope`: Works in conjunction with `itemtype` to specify the type of item associated with a particular HTML element.
* `itemtype`: Provides the URL of the (schema.org) vocabulary item associated with the HTML element, and gives the context for its constituent `itemprop` properties.

## **Repeated `<meta>` tags**

Structured data may be embedded into an HTML document with repeated `<meta>` tags in the `<head>` element, identified with tag properties specified by the [**Open Graph protocol**](https://ogp.me/). Chartbeat mostly follows the official specification, but allows alternate forms of some properties for your convenience. This format was designed to incorporate webpages into a "social graph", and as such, includes fewer properties in more restrictive data structures.

### **Examples**

```markup
<html>
    <head>
        <meta property="og:type" content="article">
        <meta property="og:title" content="Data, platforms, and subscriptions: What you read (by Total Engaged Time) in 2019">
        <meta property="og:url" content="https://blog.chartbeat.com/2020/01/16/total-engaged-time-chartbeat-blog-data/">
        <meta property="og:image" content="http://4ehuia1v75h912e6wht7ul1m-wpengine.netdna-ssl.com/wp-content/uploads/2017/09/Historical@2x.png">
        <meta property="article:published_time" content="2020-01-16T11:07:35-05:00">
        <meta property="article:author" content="Nick Lioudis">
        <meta property="og:description" content="We dove into our own Historical Dashboard to understand the topics that garnered the most total engaged time on the Chartbeat blog in 2019. See more on our website">
        <meta property="og:site_name" content="Chartbeat">
    </head>
</html>
```

### **Properties**

#### Required properties:

* `og:type` ([**enum**](https://ogp.me/#enum)): Type of article within a social graph. Must be one of `article`, `blog`, or `website`.
* `og:image` ([**url**](https://ogp.me/#url)): URL of the image used to represent the article within a social graph. If not specified, `og:image:url` is checked.
* `og:title` ([**string**](https://ogp.me/#string)): Title of the article.
* `og:url` ([**url**](https://ogp.me/#url)): Canonical URL of the article that can be used as its permanent ID in a social graph.

#### Key properties:

* `article:author` ([**string**](https://ogp.me/#string) or [**profile**](https://ogp.me/#type_profile)): One or more authors of the article.
* `article:modified_time` ([**datetime**](https://ogp.me/#datetime)): Date and (optionally) time the article was last modified. If not specified, `og:updated_time` is checked.
* `article:published_time` ([**datetime**](https://ogp.me/#datetime)): Date and (optionally) time the article was first published. If not specified, `og:pubdate` is checked.
* `article:section` ([**string**](https://ogp.me/#string)): One or more sections assigned to the article. Multiple sections may be expressed as a single string delimited by commas.
* `article:tag` ([**string**](https://ogp.me/#string) [**array**](https://ogp.me/#array)): One or more subject matter tags associated with the article. Multiple tags may be expressed as a single string delimited by commas.
* `og:description` ([**string**](https://ogp.me/#string)): Brief description of the article.

#### Additional properties:

* `article:content_tier` ([**enum**](https://ogp.me/#enum)): Access tier assigned to article by its publisher. Should be one of `free`, `metered`, or `locked`.
* `og:locale` ([**string**](https://ogp.me/#string)): Locale in which the article's tags are marked up. Should be formatted as language\_TERRITORY, e.g. `en_US`.
* `og:site_name` ([**string**](https://ogp.me/#string)): Name of the site on which the article appears. If not specified, `article:publisher` is checked.
* `og:video` ([**url**](https://ogp.me/#url)): URL of the video used to represent the article within a social graph. If not specified, `og:video:url` is checked.

## Web scraper

Chartbeat uses a web scraper to collect metadata about the pages tracked by Chartbeat's main pinger. For each page, the scraper makes a standard HTTP GET request to the URL and downloads the raw HTML, from which metadata is then extracted. Page metadata is joined to the visitor engagement data collected by our pinger using the host and path fields (aka h and p keys).

In general, Chartbeat's web scraper will only visit pages once, shortly after their first appearance in our engagement data pipeline. Load on your site's servers should be minimal; however, scraper activity will be higher at first, when many pages appear "new" to our system, before declining to match your site's usual publication rate.

The scraper identifies itself with the UserAgent `Chartbeat-ContentX/0.3 (http://www.chartbeat.com)`. Note that the version number (e.g. 0.3) will change over time. To prevent unsuccessful scrapes and ensure that every page has associated metadata, please add this UserAgent to your site's whitelist.

You can also increase our ability to detect data for [our Topics and Categories tabs](https://help.chartbeat.com/hc/en-us/articles/16574320245787) and additional metadata driven features (such as the word count pivot in the Historical Dashboard) by allowing traffic from our IP addresses to bypass any features which obfuscate the content of the page (e.g. paywalls).&#x20;

Chartbeat regularly tries to visit pages where we detect significant traffic to examine the content of the page. You may notice traffic on your page from Chartbeat (note that this traffic will not appear in your Chartbeat dashboards), and you can identify our visits as they will be from the IP addresses listed below:

52.200.230.127\
35.174.236.164\
44.211.104.80\
54.159.123.51\
52.44.209.155\
52.0.154.147\
50.17.188.178\
44.210.22.85\
54.242.4.200

Please note that while this list does not regularly change, it may be expanded in the future due to infrastructure improvements.

## Up next

#### Choose your next integration.

* ~~**Standard Website Tracking**~~ ✅
* [Google AMP Tracking](/cbp/tracking/google-amp)
* [Mobile App SDKs](/cbp/tracking/mobile-app-sdks)
* [Headline and Image Testing](/cbp/feature-integrations/testing)
* [Video Tracking ](/cbp/feature-integrations/video-engagement)&#x20;


# Google AMP

A guide to integrating Chartbeat tracking for your Accelerated Mobile Pages.

## Articles included in this guide:

1. [Chartbeat Code for AMP](/cbp/tracking/google-amp/amp-code)
2. [AMP Configuration Variables](/cbp/tracking/google-amp/amp-configuration-variables)
3. [Alternative AMP Integration](/cbp/tracking/google-amp/alternative-amp-integration)
4. [Integration QA Steps: AMP](/cbp/tracking/google-amp/qa-amp-integration)&#x20;

{% hint style="success" %}
Chartbeat tracking for Google's Accelerated Mobile Pages is a feature included within all customer plans.&#x20;
{% endhint %}


# Chartbeat Code for AMP

Instructions to add Chartbeat code to your AMP pages.

## Intro to AMP

Google’s Accelerated Mobile Pages (AMP) project aims to speed up load times on mobile web pages, industry-wide, using a special, lightweight form of HTML. As a part of this AMP project, Google has updated its search engine to point to an AMP page—if it exists—from mobile search results.

Because many mobile users exit publisher websites before a page finishes loading due to slow load times, building mobile pages with AMP can improve user experience, retain readership, and built loyalty.

Once a Chartbeat Dashboard is integrated with AMP tracking, a new field will appear under the Site filter of the Distribution module in the left-hand side of the Real-Time dashboard, labeled 'AMP'. This will display the percentage of concurrents that are engaging with content in your AMP site experience. By selecting this new filter in Real-Time, the dashboard will display only the concurrents that are engaging with your AMP content, as well as all the Chartbeat metrics specific to this audience.

## Validate your existing AMP HTML

Make sure that your site's AMP pages are properly configured according to [**AMP HTML specification**](https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml/). Your AMP pages should also be validated using the AMP Project's [**validation workflow**](https://amp.dev/documentation/guides-and-tutorials/learn/validation-workflow/validate_amp/).

You can also add this same code to your AMP [**Web Stories**](https://amp.dev/about/stories) pages.

## Steps to integrate

**Step 1:** After validating your AMP markup, you’ll need to add the "amp-analytics.js" extension within the `<head>` of the AMP page markup code if it's not already in place. This will enable analytics for your AMP pages.

```markup
<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
```

**Step 2:** Add this code to the `<body>` of your AMP webpage, replacing the placeholder values assigned to the uid, domain, sections, and authors variables below with your [**site-specific Chartbeat data**](/cbp/tracking/standard-websites/configuration-variables#required-fields), dynamically populated for each unique AMP page by your CMS. Learn more about available configuration variables for your Chartbeat AMP tracking and how to populate them in the [**next article of this guide**](/cbp/tracking/google-amp/amp-configuration-variables).

```markup
<amp-analytics type="chartbeat">
    <script type="application/json">
        {
            "vars": {
                "uid": "12345",
                "domain": "yoursite.com",
                "sections": "section 1,section 2",
                "authors": "John Doe"
            }
        }
    </script>
</amp-analytics>
```

{% hint style="warning" %}
The `domain` variable should never include "http", "https" or "www".
{% endhint %}

As all third-party JavaScript sources [**behave differently on AMP pages**](https://www.ampproject.org/docs/get_started/technical_overview.html#allow-only-asynchronous-scripts), if you are using custom metadata fields to populate the sections, authors, and content type variables in your existing Chartbeat code, you'll need to retrieve these outside of the AMP markup.

Your UID for your Chartbeat account and the site id assigned to the domain variable can be set manually for each of your sites as these values are static and will not change from article to article.

{% hint style="success" %}
**Tip:** Be sure to double check your work and ensure there is no trailing comma at the end of the final key-value pair, as demonstrated in the code snippet above in line 8.
{% endhint %}

## Complete example

This example demonstrates a complete AMP page with Chartbeat tracking enabled.

```markup
<!doctype html>
<html ⚡ lang="en">
  <head>
    <meta charset="utf-8">
    <title>Chartbeat for AMP demo</title>
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">

    <link rel="canonical" href="mysite.com/article-xyz.html" />
    <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>

    <!-- Load AMP -->
    <script async src="https://cdn.ampproject.org/v0.js"></script>

    <!-- Load amp-analytics -->
    <script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
  </head>
  <body>
    <!-- Configure Chartbeat analytics -->
    <amp-analytics type="chartbeat">
    <script type="application/json">
        {
            "vars": {
                "uid": "12345",
                "domain": "yoursite.com",
                "sections": "section 1,section 2",
                "authors": "John Doe"
            }
        }
    </script>
    </amp-analytics>

  </body>
</html>
```

## Next steps

Following the above steps will suffice for a basic AMP tracking integration, but we strongly encourage reviewing each of the articles within this guide prior to implementing our code so you can fine-tune your tracking settings for AMP to match the tracking of your standard website.

The next article in this series explains how to set the configuration variables included in the snippet above (lines 5–8), as well as other optional variables that may be appropriate for your integration.


# AMP Configuration Variables

Available Chartbeat configurations to be passed into your AMP implementation code.

## Introduction

The page property metadata (sections, authors, path, title, and content type) configured to send from your AMP pages should match exactly the data we receive from those same pages on your non-AMP website via our [**standard JavaScript tag**](/cbp/tracking/standard-websites/configuration-variables), and stay consistent across all of your other Chartbeat tracked platforms. This is important to ensure accurate page-level engagement tracking in your team's Chartbeat dashboards and audience reports.

## Required AMP variables

These variables must be set in your snippet for data to appear in your dashboards.

| Field Name | Description                                                                                                                                                                               | Example Input |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `uid`      | A five-digit numerical value associated with your Chartbeat account.                                                                                                                      | `12345`       |
| `domain`   | This value should be set to the site id of your website as it appears in your Chartbeat account homepage. This is usually the root domain without http or '[www](http://www).' prepended. | `site.com`    |

For more information on setting the UID and domain variables, see our [**customize tracking settings**](/cbp/tracking/standard-websites/configuration-variables#required-fields) article within our integration guide for non-AMP sites.

## Recommended AMP variables

| Field Name | Description                                                         | Example Input |
| ---------- | ------------------------------------------------------------------- | ------------- |
| `sections` | Accepts a comma separated list of values for the page's section(s). | `sports,news` |
| `authors`  | Accepts a comma separated list of values for the page's author(s).  | `John Doe`    |

{% hint style="warning" %}
**Tip:** Chartbeat uses the "|" character for a splitting process when organizing section and author values, so it is recommend to avoid this character when defining these variables.
{% endhint %}

Example usage:

```markup
<amp-analytics type="chartbeat">
    <script type="application/json">
        {
            "vars": {
                "uid": "12345",
                "domain": "yoursite.com",
                "sections": "News",
                "authors": "John Doe"
            }
        }
    </script>
</amp-analytics>
```

These variables accept one or multiple string values parsed by comma, so the examples directly above and below would both be acceptable formats.

```markup
<amp-analytics type="chartbeat">
    <script type="application/json">
        {
            "vars": {
                "uid": "12345",
                "domain": "yoursite.com",
                "sections": "News,Sports",
                "authors": "John Doe,Jane Doe"
            }
        }
    </script>
</amp-analytics>
```

## Additional AMP variables

| Field Name      | Description                                                                                                                                                                                                                                                                                                            | Example Input   |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| `canonicalPath` | By default, our tracker collects the page path from the `rel="canonical"` `<link>` element in your AMP HTML. If you are unable or prefer to not use the canonical link set in your site's AMP HTML, you may override this default setting. We highly recommend that you use a real path used to navigate to this page. | `site.com/path` |
| `title`         | By default, our tracker collects page titles from the `<title>` tag in your AMP HTML. You can use this variable to override this setting by assigning custom page titles in your Chartbeat for AMP code.                                                                                                               | `Article Title` |
| `contentType`   | For customers making use of our Advanced Queries tool, this variable accepts a single string value to supplement your Chartbeat historical data with additional information about the type of content a given page falls under, like 'gallery page', 'standalone video page', etc.                                     | `gallery page`  |
| `sponsorName`   | For customers making use of our Advanced Queries tool, this variable accepts a single string value to supplement your Chartbeat historical data with additional information about your sponsored content (advertorial) campaign name IDs, such as 'Mercedes', 'Mastercard-Jun2020', 'Netflix-Q1-2021-CID123', etc.     | `Mercedes`      |

Example usage:

```markup
<amp-analytics type="chartbeat">
    <script type="application/json">
        {
            "vars": {
                "uid": "12345",
                "domain": "yoursite.com",
                "sections": "News",
                "authors": "John Doe",
                "canonicalPath": "yoursite.com/article-xyz.html",
                "title": "My Article Title",
                "contentType": "article page"
            }
        }
    </script>
</amp-analytics>
```

## IP Truncation

For clients in the EU, use the following \<amp-analytics> configuration for Chartbeat to override the host parameter and point traffic to our proxy layer that de-identifies IP addresses within the EU:&#x20;

```html
<amp-analytics type="chartbeat">
    <script type="application/json">
        {
            “requests”: {“host”: “https://pong.chartbeat.net”},
                "vars": {
                "uid": "12345",
                "domain": "yoursite.com",
                "sections": "section 1,section 2", 
                "authors": "John Doe"
            }
        }
    </script>
</amp-analytics>
```

## Next steps

If your team uses Google Tag Manager for your AMP pages, you have the option to use our pre-built Chartbeat for AMP GTM tag template instead of adding our code directly to your AMP HTML. If you do not use Google Tag Manager, you can skip ahead to our [**integration QA steps article**](/cbp/tracking/google-amp/qa-amp-integration).


# Alternative AMP Integration

Alternative options for integrating our code on your AMP pages.

## Using Google Tag Manager for AMP

If you are using Google Tag Manager's pre-built Chartbeat tag template for AMP, first fill out the Chartbeat tag configuration fields using the instructions in the previous article of this guide (UID, domain, etc). When you reach the Triggering configuration, add the following triggers:

**Add a Timer trigger**

* Interval: 15 seconds
* Limit: 7200 seconds
* Enable the "Fire immediately when triggered" setting
* Enable the trigger for "All Pages"

**Add a Click trigger:**

* CSS Selector: a
* Enable the trigger for "All Pages"

Here's what that looks like in the GTM interface:

![Timer Trigger Configuration in GTM](/files/-MHGuWIX9K3I1pPmkJKG)

![Click Trigger Configuration in GTM](/files/-M66EeA4CKk02YUujhT2)

## Next steps

Having reviewed all of the previous articles in this guide, you're now ready to add our snippet to your AMP pages using our integration QA article (up next) to check your work for completeness.


# Integration QA Steps: AMP

Steps to QA your integration of Chartbeat code on AMP pages.

## Check your Implementation Review

Our Implementation Review lets Admin users view data snapshots from the latest full calendar day we have on hand.

Please note that a 5-10% difference between platforms is generally expected. If data is not coming through as expected, check out  common issues in troubleshooting pageviews and unique discrepancies page

For details on general use of the dashboard please see our Implementation Review Introduction in our documentation &#x20;

#### To access your Implementation Review:

1. [**Sign in**](https://chartbeat.zendesk.com/hc/en-us/articles/360017718093) to your Chartbeat account.&#x20;
2. Navigate to Settings from the menu top right-head&#x20;
3. Implementation Review pages on the left side menu
4. Navigate to the Platform Audit and Traffic Audit Tabs

{% hint style="warning" %}
**Tip:** If you cannot access the Implementation Review, verify with your organization if your account has the required Admin role. &#x20;
{% endhint %}

Once you're able to access the dashboard, here are some useful QA steps to ensure proper tracking:

#### **Platform Audit**

1. Navigate to the Platform Audit Tab to view the Implementation Summary Table
2. Locate the "AMP" column  and corresponding cell for the site and platform of interest

If there is a green checkmark in the corresponding cell, that means Chartbeat is receiving significant web traffic from the site at the time snapshot was taken.

#### **Traffic Audit**

1. Select a site and verify the timezone and snapshot date. &#x20;
2. Summary table: reference Web pageviews breakdown to validate snapshot counts
3. Device breakdown by platform: section selects "AMP" as an option in the dropdown to see data on visitors viewing your site across unique device types.&#x20;
4. Top Articles by pageviews: View the top 20 [canonical paths](/cbp/tracking/standard-websites/configuration-variables#page-ids-canonical-urls-vs-custom-paths) and corresponding pageviews across all platforms

If data in the Implementation Review snapshots are as expected, your site is implemented correctly for AMP. If more QA or troubleshooting is needed please continue with the additional QA steps below to assess your implementation.

## Check your Real-Time Dashboard

Upon a successful Chartbeat integration with your AMP pages, a new AMP filter will appear in the left-hand side of your Real-Time Dashboard (screenshot below).&#x20;

![AMP filter in Real-Time Dashboard](/files/-LqlexuUCw79opzoYMe6)

Select the AMP filter in your Real-Time Dashboard to perform the following data QA checks:

#### Under 'Top Pages':

* Page titles are populating as expected
* Hover over page titles in this list to view the canonical paths received from these pages. Alternatively, click into the article view and right-click the title to copy the link address
* Check for duplicate page entries in this list, and identify the discrepant paths being received from these pages to determine the fix in your configuration settings
* Open the section & author drop-down filters to ensure section & author data is populating as expected

{% hint style="success" %}
**Tip:** Once again, the page property metadata (sections, authors, path, title, and content type) configured to send from your AMP pages should match exactly the data we receive from those same pages on your non-AMP website via our [**standard JavaScript tag**](/cbp/tracking/standard-websites/configuration-variables), and stay consistent across all of your other Chartbeat tracked platforms. Check to ensure the page data being sent from your AMP pages to Chartbeat aligns precisely with the data we receive from those same pages across your other Chartbeat-tracked platforms.
{% endhint %}

## Use DevTools to monitor pings

You can use your browser's site inspector tools to monitor network activity from your site, filtering on "chartbeat" to view all chartbeat related scripts loading on the page, or on "ping?" to find requests to URLs beginning with **ping.chartbeat.net**.

Clicking into one of these ping requests to ping.chartbeat.net in DevTools will display a list of the included query string parameters in this call which contain data determined by the configuration variables in your code snippet. Below is a ping key that describes some of these QSP fields that are important to check for in your review.

| QSP/Key | Description                                                                                                                                                                             |
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| h       | The site, or "host" where your site's traffic data will appear in your Chartbeat account. This string is pulled from the value assigned to the `domain` variable in our AMP code block. |
| p       | The page's path. This string should match your page's canonical URL path, or whatever you're passing into the `canonicalPath` variable in your AMP code block.                          |
| d       | With this key, we collect the actual domain of your page, which may be different from the h-key host/site name in your Chartbeat account.                                               |
| g       | Account ID, pulled from the value assigned to the `uid` variable our AMP snippet.                                                                                                       |
| g0      | A comma-delimited list of sections received from the page via the `sections` variable in our snippet.                                                                                   |
| g1      | A comma-delimited list of authors received from the page via the `authors` variable in our AMP snippet.                                                                                 |
| i       | Page title. Only appears in the very first ping from each unique page session on your site.                                                                                             |
| r       | Will carry the referring URL of the current pageview if the referrer value is an external site.                                                                                         |
| t       | Unique identifier for the page-session. Each unique t-key value represents a separate pageview in your Chartbeat data.                                                                  |

## Limitations

Because Google restricts the use of JavaScript in its supported AMP partners' analytics scripts, we are currently unable to measure Visitor Frequency for your AMP page visitors. All AMP page visitors will be tracked as "Unclassified" users under the Visitor Frequency metric in your Chartbeat tools. The restricted use of JavaScript also means that AMP pages using infinite scroll are not compatible with Chartbeat's AMP code at this time.

Chartbeat video tracking for AMP pages is also unsupported at this time, as well as user subscriber status tracking.

## Up next

#### Choose your next integration.

* [Standard Website Tracking](/cbp/tracking/standard-websites)
* ~~**Google AMP Tracking**~~ ✅
* [Native App SDKs](/cbp/tracking/mobile-app-sdks)
* [Headline and Image Testing](/cbp/feature-integrations/testing)
* [Video Tracking ](/cbp/feature-integrations/video-engagement)&#x20;


# Mobile App SDKs

A guide to integrating Chartbeat tracking for your native mobile applications for iOS and Android smartphones and tablets.

## Articles included in this guide:

1. [Intro to Mobile App Tracking](/cbp/tracking/mobile-app-sdks/mobile-sdks-intro)
2. [Android SDK](/cbp/tracking/mobile-app-sdks/android-sdk)
3. [iOS SDK](/cbp/tracking/mobile-app-sdks/ios-sdk)
4. [Integration QA Steps: Mobile Apps](/cbp/tracking/mobile-app-sdks/qa-mobile-sdk-integration)

{% hint style="warning" %}
Chartbeat tracking for mobile apps is included with the [Essentials plan](https://help.chartbeat.com/hc/en-us/articles/45757319248155-Plans-products-features-2025-and-later) or any higher plan. If you’re interested in adding this feature to your Chartbeat software subscription, please reach out to your Customer Success Manager or to the Technical Solutions team at <support@chartbeat.com>.
{% endhint %}


# Intro to Mobile App Tracking

Review the information in this article before getting started with our integration for native mobile applications. Note that this is an add-on feature, not included in all Chartbeat service plans.

## Overview

Our native app SDKs are designed to track user engagement with publishers' content in native mobile applications. Our iOS SDK works for apps built in Objective-C and Swift. Our Android SDK works for apps built in Java and Kotlin. Separately, we also support tracking webview-based mobile apps using our standard JavaScript tracker, as well as hybrid applications with a blend of native and webview-based screens (more information below).

## How it works

Use our iOS and Android Mobile SDKs to pull native app traffic data into your team's Real-Time Dashboards and see what’s resonating with your app users on a second-to-second basis — which articles, site sections and authors they’re engaging with, what they’re sharing, and more. With our SDKs integrated in your mobile apps, your Chartbeat customer success rep can enable new app traffic filters in the Real-Time Dashboard for your website, enabling your team to view audience data as a whole (app traffic with website traffic together) or drill down into device-specific app audience data with a one-click breakdown of traffic across iOS and Android apps.

![Mobile App Filters in Real-Time Dashboard](/files/-M9-mXcuDlJECMOfSGhT)

## Know before you start

Before your app developer team begins integrating our SDKs (instructions included in the following articles of this guide), check out the sections below to understand (A) importance of page metadata consistency across your Chartbeat tracked platforms, (B) how to test your integration in a sandbox dashboard on your Chartbeat with real-time data, and (C) how to track webview-based pages of your app differently from native views.

### Page metadata consistency across platforms

The page property metadata configured to send from pages in your mobile app (page path, title, sections, and authors) should match *exactly* the data we receive from those same pages on your standard website via our [**JavaScript tag**](/cbp/tracking/standard-websites/configuration-variables) if you intend to include app audience data within the same Chartbeat Site as your website audience data. Before you start adding our code to your apps, we recommend your team reviews and documents how your website is configured to send these data points to Chartbeat, and ensure that your CMS makes this same data available from each corresponding page of your mobile app.&#x20;

If your app does not contain the same page metadata sent from your website to Chartbeat, for example canonical web URLs like `https://www.mysite.com/news/article-12345.html`, your team should consider sending app audience data to a separate Site in your Chartbeat  account to avoid creating discrepancies within the dashboard for your website traffic data. Consider creating a separate site in your Chartbeat account with a site ID like `app.mysite.com` or `mysite.app` to view your app data separately from your website data.

### Test your app integration in a sandbox dashboard

We recommend first adding our code in a test app, and using a corresponding test dashboard in your Chartbeat account to view the data before pushing your app to production with your prod site ID. For example, if your production site ID is `mysite.com`, your test site ID could be `test.mysite.app`. In the following instruction articles for our Android and iOS SDK, remember when adding our code to your test app to use test site ID and update it to your production site ID only when your team is finished QA'ing the integration for data accuracy.&#x20;

{% hint style="info" %}
**Note**: The test Site will need to be added to your organization's Chartbeat account to see your data in this sandbox dashboard. If you run into any issues adding this sandbox site to your account, contact your Chartbeat support representatives and we will add it for you.
{% endhint %}

### Native vs web-view or hybrid apps

Our native SDK trackers should not be used to track web-view pages of a mobile app in most cases. Rather, our standard JavaScript code should be used to track these pages by including our JS snippet directly within the requested HTML documents to your website.

To ensure that these pageviews are also registered as app views in your Chartbeat data, use our mobileApp setting to track all web-view pageviews in your app:

```javascript
_sf_async_config.mobileApp = true;
```

In a hybrid app where a user can navigate between native and web-view screens, be sure to stop our SDK tracker whenever a user moves from a native page to a web-view page, and resume the SDK tracker as the user navigates back to a native screen.

## Next steps

Instructions to integrate our Android SDK is up next, or skip to [iOS SDK integration steps](/cbp/tracking/mobile-app-sdks/ios-sdk) or [React Native SDK steps](/cbp/tracking/mobile-app-sdks/react-native-sdk).


# Android SDK

Instructions to integrate Chartbeat's Android SDK to your native app.

### Overview

The Chartbeat Android SDK is a Java library that sends real-time engagement beacons ("pings") to Chartbeat so Android apps can be measured in the same dashboards as the publisher's web content. The entire public surface is exposed as static methods on the Tracker class.

* **Package:** `com.chartbeat.androidsdk`
* **Main class:** `Tracker` (all public methods are `static`)
* **Distribution:** JitPack — `<https://jitpack.io/#chartbeat/android_sdk>`
* **Maven coordinates (release publication):** `com.github.chartbeat:android_sdk:<tag>`

***

### Installation

#### 1. Add the JitPack repository

**Gradle (Groovy) — root** `build.gradle`:

```java
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}
```

**Gradle (Kotlin DSL) —** `settings.gradle.kts`:

```java
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
    }
}
```

#### 2. Add the dependency

Use a version tag from [jitpack.io/#chartbeat/android\_sdk](https://jitpack.io/#chartbeat/android_sdk):

```java
dependencies {
    implementation 'com.github.chartbeat:android_sdk:1.8.0'
}
```

### Getting Started

#### 1. Initialize the tracker

Call `Tracker.setupTracker(...)` exactly once — typically in `Application.onCreate()` when the app is in the foreground. Subsequent calls are ignored.

**Java**:

```java
@Override
public void onCreate() {
  String chartbeatAccountId = "12345";
  String chartbeatSiteId = "yoursite.com";
    
  super.onCreate();
  Tracker.setupTracker(chartbeatAccountId, chartbeatSiteId, this);
}
```

For example, `this` is the application-level Context.

**Kotlin:**

```java
public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Tracker.setupTracker(
            "12345",               // accountId
            "yoursite.com",        // domain
            this,                  // Application context
            false                  // usePong — true to use the pong endpoint for IP truncation
        );
    }
}
```

> **Context rule:** `setupTracker` requires an **Application** context. Passing an Activity throws `IllegalArgumentException`.

#### 2. Track a view

Call `Tracker.trackView` in every screen's `onCreate()` or  `onResume()`:

```java
@Override
protected void onResume() {
    super.onResume();
    Tracker.trackView(this, "/articles/hello-world", "Hello World");
}
```

#### 3. Report that the user left the view

Call the `userLeftView` function (e.g. from your activities’ `onPause` functions) whenever the app leaves the foreground to stop the tracking for the current session.

```java
@Override
protected void onPause() {
    super.onPause();
    Tracker.userLeftView("/articles/hello-world");
}
```

#### 4. Track user interaction

Call the `userInteracted` function whenever the user interacts with your view to accurately measure user engagement with your content. Our recommendation is to call this from within the `onUserInteraction` function of your activity. Register writing engagement by calling the `userTyped` function whenever the user starts typing something.

```java
@Override
public void onUserInteraction() {
    super.onUserInteraction();
    Tracker.userInteracted();
}
```

### Usage Guide

#### Author/Section&#x20;

Set metadata **after** trackView(), because view changes purge metadata.

```java
Tracker.setAuthors(Arrays.asList("Jane Doe", "John Smith"));
Tracker.setSections(Arrays.asList("technology", "news"));
```

#### Push referrers

```java
Tracker.setPushReferrer("push-campaign-42");        // URL-encoded, wrapped as push/?id=...
```

#### Scroll position

For scrollable views you aren't passing through trackView, call setPosition as the user scrolls. Negative values are not sent to the server.

```java
Tracker.setPosition(scrollPositionTop, scrollWindowHeight,
                    totalContentHeight, fullyRenderedDocWidth);
```

#### User subscriber status

Set the [Subscriber Status](https://docs.chartbeat.com/cbp/tracking/standard-websites/subscriber-engagement) of the user.

```java
Tracker.setUserPaid(); // for Subscriber
Tracker.setUserLoggedIn(); // for Registered
Tracker.setUserAnonymous(); // for Guest
```

### IP Truncation

For clients in the EU, follow these steps to point traffic to our proxy layer that de-identifies IP addresses within the EU.When [initializing the tracker](https://app.gitbook.com/o/-LYD2YiRqOYWSR79uOpv/s/-LYD2YiVXfNNRnSgJXb4/~/diff/~/changes/484/tracking/mobile-app-sdks/android-sdk#initialize-the-tracker), add `true` as the last variable in the `Tracker.setupTracker()` function:

**Java:**

{% code overflow="wrap" %}

```java
@Override
public void onCreate() {
    String chartbeatAccountId = "12345";
    String chartbeatSiteId = "mysite.com";
    
    super.onCreate();
    Tracker.setupTracker(chartbeatAccountId, chartbeatSiteId, this, true);
```

{% endcode %}

***

### ProGuard and R8

If you enable ProGuard or R8 in your release build, ensure the following rules are added to your `proguard-rules.pro` file to prevent issues with Retrofit and RxJava dependencies:

```java
-dontwarn rx.**
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keep class rx.** { *; }
-keepattributes Signature, InnerClasses, EnclosingMethod
```

## Next steps

Next up is our iOS SDK implementation steps, or skip to the [**integration QA article**](/cbp/tracking/mobile-app-sdks/qa-mobile-sdk-integration) if your team will not implement tracking for an iOS app.


# API Reference

All methods are public static on com.chartbeat.androidsdk.Tracker.

#### Lifecycle

| Method                                                                             | Description                            |
| ---------------------------------------------------------------------------------- | -------------------------------------- |
| setupTracker(String accountId, String domain, Context appContext)                  | Initialize (defaults to ping endpoint) |
| setupTracker(String accountId, String domain, Context appContext, boolean usePong) | Initialize, optionally using pong      |
| stopTracker()                                                                      | Stop the tracker                       |
| pauseTracker()                                                                     | Pause while idle                       |
| backgroundTracker()                                                                | Pause when app is backgrounded         |
| restartPingService(Context)                                                        | Restart the ping scheduler             |

#### View tracking

| Method                                                                                                                                                | Description                                         |
| ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| trackView(Context, String viewId, String viewTitle)                                                                                                   | Track a view without scroll data                    |
| trackView(Context, String viewId, String viewTitle, int scrollPositionTop, int scrollWindowHeight, int totalContentHeight, int fullyRenderedDocWidth) | Track a view with scroll data                       |
| userLeftView(String viewId)                                                                                                                           | Report the user left a view (usually onPause)       |
| userInteracted()                                                                                                                                      | Report user interaction (usually onUserInteraction) |
| userTyped()                                                                                                                                           | Report typing/writing engagement                    |

#### Metadata

| Method                                                                  | Description                       |
| ----------------------------------------------------------------------- | --------------------------------- |
| setAuthors(String commaDelimited) / setAuthors(Collection\<String>)     | Article authors                   |
| setSections(String commaDelimited) / setSections(Collection\<String>)   | Article sections                  |
| setZones(String commaDelimited) / setZones(Collection\<String>)         | Content zones                     |
| setCustomData(HashMap\<String,String>)                                  | Arbitrary key/value metadata      |
| setDomain(String) / setSubdomain(String)                                | Override dashboard routing        |
| setAppReferrer(String) / setPushReferrer(String)                        | Referrers (call before trackView) |
| setViewLoadTime(float seconds)                                          | Record page-load time             |
| setPosition(int top, int windowHeight, int contentHeight, int docWidth) | Scroll position                   |

#### User state

| Method             | Description             |
| ------------------ | ----------------------- |
| setUserPaid()      | Mark as paid subscriber |
| setUserLoggedIn()  | Mark as logged in       |
| setUserAnonymous() | Mark as anonymous       |

#### Video

| Method                                                                                              | Description                  |
| --------------------------------------------------------------------------------------------------- | ---------------------------- |
| trackVideo(viewId, viewTitle, thumbnail, engagedTime, duration, playState, contentTime, playerTime) | Start tracking a video       |
| updateVideoPlayState(playState, duration, engagedTime, contentTime, playerTime)                     | Update in-flight video state |

#### Endpoint

| Method              | Description                                              |
| ------------------- | -------------------------------------------------------- |
| setUsePong(boolean) | Switch between ping.chartbeat.net and pong.chartbeat.net |
| getUsePong()        | true if using pong                                       |
| getEndpoint()       | Full endpoint URL                                        |
| getHost()           | Host portion only                                        |
|                     |                                                          |


# Conversion Tracking

Additional details about Conversion Tracking [can be found here](https://docs.chartbeat.com/cbp/feature-integrations/conversion/adding-subscriptions.js-to-your-site).&#x20;

Chartbeat's Conversion Tracking supports the following events:

* Subscriptions (Paywall)
* Registrations
* Newsletter Signups
* Unsubscribes
* Offers
* Surveys
* Account Creations
* Downloads
* Donations
* Payments
* Searches
* Saves

Every category has two overloads — short form (type only) and long form with [optional parameters](https://docs.chartbeat.com/cbp/feature-integrations/conversion/conversion-events#event-parameters) (`pathOverride`, `eventLabel`, `eventValue`).

{% hint style="warning" %}
Optional event parameters are not currently surfaced in any Chartbeat product.
{% endhint %}

```java
import com.chartbeat.androidsdk.ConversionEvent;

Tracker.trackConversionPaywall(ConversionEvent.PaywallType.SHOWN);

Tracker.trackConversionPaywall(
    ConversionEvent.PaywallType.COMPLETE,
    "/paywall/monthly",   // pathOverride
    "monthly-sub",        // eventLabel
    "9.99"                // eventValue
);
```

The different conversion types (e.g. PaywallType, RegistrationType) are called with the relevant event state (e.g. Shown, Complete - see [full details below](https://app.gitbook.com/o/-LYD2YiRqOYWSR79uOpv/s/-LYD2YiVXfNNRnSgJXb4/~/edit/~/changes/490/tracking/mobile-app-sdks/android-sdk/conversion-tracking#four-state-types-shown-start-complete-cancel)) when an event occurs (e.g. a paywall appears, a newsletter signup form is submitted). &#x20;

| **Method**                            | **Enum**                              |
| ------------------------------------- | ------------------------------------- |
| `trackConversionPaywall(...)`         | `ConversionEvent.PaywallType`         |
| `trackConversionRegistration(...)`    | `ConversionEvent.RegistrationType`    |
| `trackConversionNewsletter(...)`      | `ConversionEvent.NewsletterType`      |
| `trackConversionUnsubscribe(...)`     | `ConversionEvent.UnsubscribeType`     |
| `trackConversionOffer(...)`           | `ConversionEvent.OfferType`           |
| `trackConversionSurvey(...)`          | `ConversionEvent.SurveyType`          |
| `trackConversionAccountCreation(...)` | `ConversionEvent.AccountCreationType` |
| `trackConversionDownload(...)`        | `ConversionEvent.DownloadsType`       |
| `trackConversionDonation(...)`        | `ConversionEvent.DonationsType`       |
| `trackConversionPayment(...)`         | `ConversionEvent.PaymentsType`        |
| `trackConversionSearch(...)`          | `ConversionEvent.SearchType`          |
| `trackConversionSave(...)`            | `ConversionEvent.SavesType`           |

## Supported events

Below is a list of the event methods you can pass to the `trackConversion...()` function. You should fire a Chartbeat conversion event from the same category on every page in a single conversion flow.&#x20;

### Subscriptions

Track paywall activity and subscription transactions on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="268.2890625">Event</th><th width="241">Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.PaywallType.SHOWN</code></td><td>A user was shown a paywall</td><td>Required</td></tr><tr><td><code>ConversionEvent.PaywallType.START</code></td><td>A user started a subscription process</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.PaywallType.COMPLETE</code></td><td>A user completed a subscription transaction</td><td>Required</td></tr><tr><td><code>ConversionEvent.PaywallType.CANCEL</code></td><td>A user canceled a subscription process</td><td><em>Optional</em></td></tr></tbody></table>

### Registrations

Track registration activity (i.e. users creating an account) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="304.7578125">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.RegistrationType.SHOWN</code></td><td>A user viewed a registration screen</td><td>Required</td></tr><tr><td><code>ConversionEvent.RegistrationType.START</code></td><td>A user started the registration process</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.RegistrationType.COMPLETE</code></td><td>A user completed the registration process</td><td>Required</td></tr><tr><td><code>ConversionEvent.RegistrationType.CANCEL</code></td><td>A user canceled the registration process</td><td><em>Optional</em></td></tr></tbody></table>

### **Newsletter Signups**

Track newsletter signup activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="336">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.NewsletterType.SHOWN</code></td><td>A user was shown a newsletter signup option</td><td>Required</td></tr><tr><td><code>ConversionEvent.NewsletterType.START</code></td><td>A user started a signup process</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.NewsletterType.COMPLETE</code></td><td>A user completed a signup process</td><td>Required</td></tr><tr><td><code>ConversionEvent.NewsletterType.CANCEL</code></td><td>A user canceled a signup process</td><td><em>Optional</em></td></tr></tbody></table>

### Unsubscribes

Track unsubscribe activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="298.5">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.UnsubscribeType.SHOWN</code></td><td>A user visited an unsubscribe page</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.UnsubscribeType.START</code></td><td>A user started an unsubscribe flow</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.UnsubscribeType.COMPLETE</code></td><td>A user unsubscribed</td><td>Required</td></tr><tr><td><code>ConversionEvent.UnsubscribeType.CANCEL</code></td><td>A user dismissed an unsubscribe flow</td><td><em>Optional</em></td></tr></tbody></table>

### Offers

Track offer activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="241.5">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.OfferType.SHOWN</code></td><td>A user was shown an offer</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.OfferType.START</code></td><td>A user started an offer</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.OfferType.COMPLETE</code></td><td>A user completed an offer</td><td>Required</td></tr><tr><td><code>ConversionEvent.OfferType.CANCEL</code></td><td>A user dismissed an offer</td><td><em>Optional</em></td></tr></tbody></table>

### Surveys

Track survey activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="246.33333333333331">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.SurveyType.SHOWN</code></td><td>A user was shown a survey</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.SurveyType.START</code></td><td>A user started a survey</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.SurveyType.COMPLETE</code></td><td>A user completed a survey</td><td>Required</td></tr><tr><td><code>ConversionEvent.SurveyType.CANCEL</code></td><td>A user dropped off a survey</td><td><em>Optional</em></td></tr></tbody></table>

### Account Creations

Track user profile activity (e.g. filling out text fields) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="287.3333333333333">Event</th><th width="321">Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.AccountCreationType.SHOWN</code></td><td>A user viewed their profile</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.AccountCreationType.START</code></td><td>A user started editing their profile</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.AccountCreationType.COMPLETE</code></td><td>A user completed editing to their profile</td><td>Required</td></tr><tr><td><code>ConversionEvent.AccountCreationType.CANCEL</code></td><td>A user exited profile-editing mode</td><td><em>Optional</em></td></tr></tbody></table>

### Downloads

Track download activity (e.g. pdf case studies, apps) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="258.3333333333333">Event</th><th width="297">Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.Downloads.START</code></td><td>A user started downloading data</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.Downloads.COMPLETE</code></td><td>A download has completed</td><td>Required</td></tr><tr><td><code>ConversionEvent.Downloads.CANCEL</code></td><td>A download was canceled </td><td><em>Optional</em></td></tr></tbody></table>

### Donations

Track donation activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="264.3333333333333">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.Donations.SHOWN</code></td><td>An option to donate was shown</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.Donations.START</code></td><td>A user started the donation process</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.Donations.COMPLETE</code></td><td>A user completed the donation process</td><td>Required</td></tr><tr><td><code>ConversionEvent.Donations.CANCEL</code></td><td>A user canceled the donation process</td><td><em>Optional</em></td></tr></tbody></table>

### Payments

Track payment activity on your site (other than payment for a subscription).

<table data-header-hidden data-full-width="true"><thead><tr><th width="248.33333333333331">Event</th><th width="325">Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.Payments.START</code></td><td>A user started a payment process</td><td><em>Optional</em></td></tr><tr><td><code>ConversionEvent.Payments.COMPLETE</code></td><td>A user completed a payment process</td><td>Required</td></tr><tr><td><code>ConversionEvent.Payments.CANCEL</code></td><td>A user canceled a payment process</td><td><em>Optional</em></td></tr></tbody></table>

### Searches

Track completed search queries on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th>Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.Search.COMPLETE</code></td><td>A user performed a search</td><td>Required</td></tr></tbody></table>

### Saves

Track save activity (e.g. a user added an item to their "Favorites" collection) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th>Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>ConversionEvent.Saves.COMPLETE</code></td><td>A user performed a search</td><td>Required</td></tr></tbody></table>


# Video Tracking

```java
Tracker.trackVideo(
    "/video/123",       // viewId
    "My Video",         // viewTitle
    "https://...jpg",   // thumbnail (may be null)
    0L,                 // engagedTime (seconds)
    300_000L,           // duration (milliseconds)
    "s2",               // playState — see below
    0L,                 // contentTime (seconds)
    0L                  // playerTime (milliseconds)
);

Tracker.updateVideoPlayState("s2", 300_000L, 12L, 45L, 45_000L);
```

**Play states**

| Value | Meaning          |
| ----- | ---------------- |
| s1    | Unplayed         |
| s2    | Played           |
| s3    | Paused / stopped |
| s4    | Finished         |

#### Example URL from Video Tracking:

`https://ping.chartbeat.net/ping?h=video@androidsdktest.chartbeat.com&p=/korean-peace-talks.html&u=ZVEmdQVqqa00J71RvO&d=androidsdktest.chartbeat.com&g=1&_acct=anon&n=0&f=F0001&c=4.75&j=30&R=1&W=0&I=0&E=0&e=0&v=/&t=iDg9zmv4Loy0zmVOLd7-heiOfhvyE5&V=2013&D=ssShUwV_bK51dFzrQFHtCGtOyCMLbL&i=Sec. Schwartz Attends Korean Peace Talks&tz=-240&S=426&Z=1&sr=push/?id%3D123456a&sd=2&sv=fbgnceXLZMJwM2wgCUftDJopJb8D&_vs=s1&_vd=62315&_vpt=0&_vce=0&_vt=at&_vp=/korean-peace-talks.html&_vi=Sec. Schwartz Attends Korean Peace Talks&_`

**Breakdown:**

```
https://ping.chartbeat.net/ping?
h=video@androidsdktest.chartbeat.com
p=/korean-peace-talks.html
u=ZVEmdQVqqa00J71RvO
d=androidsdktest.chartbeat.com
g=1
_acct=anon
n=0
f=F0001
c=4.75
j=30
R=1
W=0
I=0
E=0
e=0
v=/
t=iDg9zmv4Loy0zmVOLd7-heiOfhvyE5
V=2013
D=ssShUwV_bK51dFzrQFHtCGtOyCMLbL
i=Sec.%20Schwartz%20Attends%20Korean%20Peace%20Talks
tz=-240
S=426
Z=1
sr=push/?id%3D123456a
sd=2
sv=fbgnceXLZMJwM2wgCUftDJopJb8D
_vs=s1
_vd=62315
_vpt=0
_vce=0
_vt=at
_vp=/korean-peace-talks.html
_vi=Sec.%20Schwartz%20Attends%20Korean%20Peace%20Talks
_
```


# iOS SDK

Instructions to integrate Chartbeat's iOS SDK to your native app.

## Add the SDK to your app

The Chartbeat iOS SDK is a framework written in Swift and Objective-C providing Chartbeat tracking functionality to native iOS apps. Like any other framework added to your project, the Chartbeat SDK provides a programming interface usable from your app code.

### Overview

The Chartbeat iOS SDK is a native Objective-C library that sends real-time engagement beacons ("pings") to Chartbeat so iOS apps can be measured in the same dashboards as the publisher's web content. The SDK exposes a single `CBTracker` singleton that you start once at app launch and feed view/engagement/conversion events throughout the app's lifetime.

* **SDK name:** `Chartbeat`
* **Language:** Objective-C (Swift-interoperable via `import Chartbeat`)
* **Public interface header:** `CBTracker.h`

***

### Requirements

| Item                  | Minimum                                                                                 |
| --------------------- | --------------------------------------------------------------------------------------- |
| iOS deployment target | 13.0 (per podspec of the RN bridge that consumes it; project itself supports older iOS) |
| Xcode                 | Project is maintained against current Xcode releases (As of today, Xcode 26 version)    |

**Swift support:** `import Chartbeat` works from any Swift file once the framework is integrated.

***

### Installation

There are three supported installation paths.

#### 1. CocoaPods (recommended)

We recommend adding the SDK to your iOS project using [**CocoaPods**](https://cocoapods.org/pods/Chartbeat). To start using the Chartbeat iOS SDK in your XCode iOS project, follow these steps:

1. Add a line referencing the SDK to your project's Podfile: `pod ‘Chartbeat’, '~> 1.5.0'`
2. Replace the version specifier in this Podfile line with the version of the latest release from [**this listing**](https://github.com/chartbeat-labs/chartbeat-ios-sdk/releases)
3. Run `pod install` from your Xcode project directory to install the SDK and its dependencies, creating a new Xcode workspace.

#### 2. Swift Package Manager

Beginning with Chartbeat iOS SDK 1.5.0, the Chartbeat iOS SDK supports an installation via Swift Package Manager&#x20;

1. Select "Add package" in the Package Dependencies menu of Project settings.&#x20;
2. In the Search bar, search for the Chartbeat SDK Github URL:&#x20;

   <pre><code><strong>https://github.com/chartbeat-labs/chartbeat-ios-sdk
   </strong></code></pre>
3. Select `chartbeat-ios-sdk`.&#x20;

#### 3. Manual

Outside of using CocoaPods and Swift Package Manager, you can also download the [latest version of the iOS SDK from Github](https://github.com/chartbeat-labs/chartbeat-ios-sdk) and copy it into your project.&#x20;

{% hint style="success" %}
**Tip:** Make sure that the framework is added with the option `"Embed and sign"` in the menu "General" of "Target Settings"
{% endhint %}

## Using the SDK

Chartbeat tracks each app visit by calling corresponding functions as the user interacts with your app. Implement the following steps for integrating the Chartbeat Mobile SDK for iOS.

### Initialize the tracker

At app startup, import the `CBTracker.h` file in your app’s delegate.

#### Objective-C:

```objectivec
#import <Chartbeat/CBTracker.h>
```

#### Swift:

```swift
import Chartbeat
```

Initialize the tracker by calling the method on the tracker singleton obtained via `[CBTracker sharedTracker]`. Call this method directly in the `applicationDidBecomeActive` method of your app delegate.&#x20;

{% hint style="success" %}
**Tip:** Remember to replace placeholder account ID and domain value (`1234` and `mysite.com`) with your organization's account ID and site ID.
{% endhint %}

#### Objective-C:

```objectivec
[[CBTracker sharedTracker] setupTrackerWithAccountId:1234 domain:@"mysite.com"];
```

#### Swift:

```swift
CBTracker.shared().setupTracker(withAccountId: 1234, domain: "mysite.com")
```

#### Swift 2.x:

```swift
CBTracker.sharedTracker().setupTracker(withAccountId: 1234, domain: "mysite.com")
```

#### Swift 3.x:

```swift
CBTracker.shared().setupTracker(withAccountId: 1234, domain: "mysite.com")
```

{% hint style="success" %}
**Tip:** When integrating with a Swift Framework, don't import `<Chartbeat/CBTracker.h>`in your framework header file. Doing so while importing Chartbeat in swift files will cause compile errors.
{% endhint %}

***

### Track screen views

Add the `trackView` call (see below) to any view you want Chartbeat to track.

**Page Metadata**

If you have views in your app that do not correspond to a page on your website, you can track user interactions on that screen by setting a `viewId` that does not conflict with other page paths from your site. Traffic for this view will then be tracked as a separate page in your dashboard. For example, you can track users in your app’s table of contents by setting the `viewId` to something like `https://mysite.com/nativeapp/main-toc`.

Pass section and author data for every view you track to ensure consistency between different views. Call `setSections` and `setAuthors` with an array of strings for each **before** calling `trackView`. Any sections and authors you set will stay set until you set a different set of values. If the user is navigating from a view with sections or authors to another view with no applicable sections or authors, you should set the sections or authors to an empty array.

{% hint style="warning" %}
**Tip:** Chartbeat uses the "|" character for a splitting process when organizing section and author values, so it is recommend to avoid this character when defining these variables.
{% endhint %}

#### Track each view

In each view controller's `viewDidAppear:`:

1. Set metadata (`setAuthors`, `setSections`, etc.) — pass empty arrays when a view has no section/author metadata.
2. Call `trackView:viewId:title:`

**Objective-C**

```objective-c
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [[CBTracker sharedTracker] setAuthors:@[@"Jane Doe", @"John Smith"]];
    [[CBTracker sharedTracker] setSections:@[@"tech", @"news"]];
    [[CBTracker sharedTracker] trackView:self.view
                                  viewId:@"/article/driverless-cars"
                                   title:@"Driverless cars will overpower humanity"];
}
```

**Swift**

```swift
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    CBTracker.shared().setAuthors(["Jane Doe", "John Smith"])
    CBTracker.shared().setSections(["tech", "news"])
    CBTracker.shared().trackView(view,
                                 viewId: "/article/driverless-cars",
                                 title: "Driverless cars will overpower humanity")
}
```

**SwiftUI**:

```swift
.onAppear {
    if let rootView = UIApplication.shared.windows.first?.rootViewController?.view {
        CBTracker.shared().trackView(rootView, viewId: "https://blog.chartbeat.com/2025/01/01/product-release-announcement", title: "Product Release Announcement")
    }
}
```

### Track user subscriber status

Beginning with Chartbeat iOS SDK 1.4.0, the Chartbeat Mobile SDK supports tracking subscriber data similar to our [JavaScript subscriber engagement integration](/cbp/tracking/standard-websites/subscriber-engagement) for standard websites. These functions are optional and can be skipped if your corresponding website does not utilize our JavaScript subscriber status integration linked above.

For proper tracking, it is important to call these functions **before** the first call to `trackView()`:

Call `setUserPaid()` to specify a user as a paid subscriber.

Call `setUserLoggedIn()` to specify a user as a registered user.

Call `setUserAnonymous()` to specify a user as a guest.

No additional information needs to be passed to these methods or functions. If at any time the user’s subscription status changes, simply call one of the above functions to ensure proper tracking.

### Stop the tracker

Call the `stopTracker` function if you want to pause tracking on certain views. Restart the tracker when the user navigates to a view you do want to track, by calling `trackView` again with the appropriate new arguments.

The tracker should automatically stop tracking when the app is backgrounded by a user. If tracking does not stop automatically, consider calling `stopTracker` *before* the app is backgrounded.&#x20;

The same applies for when the app is foregrounded. The tracker should automatically resume, but if this does not happen, you can restart the tracker by calling `trackView` again with the appropriate new arguments.&#x20;

```objective-c
[[CBTracker sharedTracker] stopTracker];
```

{% hint style="success" %}
**Tip:** When you run the tracker in [**debug mode**](/cbp/tracking/mobile-app-sdks/qa-mobile-sdk-integration#using-debug-mode), you can see log statements in the console to confirm the activity of the tracker.
{% endhint %}

### IP Truncation

When [initializing the tracker](#initialize-the-tracker), add the following `usePong` method to point traffic to our proxy layer that de-identifies IP addresses within the EU:

**Swift 3.0:**

```swift
CBTracker.shared().setupTracker(withAccountId: accountId, domain: domain)
CBTracker.shared().usePong = true
```

***

## Next steps

Review your integration for data accuracy using our integration QA steps for mobile apps, up next.


# Conversion Tracking

Additional details about Conversion Tracking [can be found here](https://docs.chartbeat.com/cbp/feature-integrations/conversion/adding-subscriptions.js-to-your-site).

Chartbeat's Conversion Tracking supports the following events:

* Subscriptions (Paywall)
* Registrations
* Newsletter Signups
* Unsubscribes
* Offers
* Surveys
* Account Creations
* Downloads
* Donations
* Payments
* Searches
* Saves

Each conversion type has two overloads — a short form and a long form that adds [optional](https://docs.chartbeat.com/cbp/feature-integrations/conversion/conversion-events#event-parameters) `pathOverride`, `eventLabel`, and `eventValue` parameters.

{% hint style="warning" %}
Optional event parameters are not currently surfaced in any Chartbeat product.
{% endhint %}

Call the relevant `trackConversion...`  method with the relevant event state (e.g. shown, complete) to track conversion events when they occur. For example, when a newsletter signup form appears call `trackConversionNewsletterWithType:EventTypeNewsletterSignupComplete`.

**Examples**

```objective-c
// paywall complete
[[CBTracker sharedTracker] trackConversionPaywallWithType:EventTypePaywallComplete];

// paywall complete with optional params
[[CBTracker sharedTracker] trackConversionPaywallWithType:EventTypePaywallComplete
                                             pathOverride:@"/paywall/monthly"
                                               eventLabel:@"monthly-sub"
                                               eventValue:@"9.99"];
```

| Short                                  | Long                                                                 |
| -------------------------------------- | -------------------------------------------------------------------- |
| `trackConversionPaywallWithType:`      | `trackConversionPaywallWithType:pathOverride:eventLabel:eventValue:` |
| `trackConversionRegistrationWithType:` | `…:pathOverride:eventLabel:eventValue:`                              |
| `trackConversionNewsletterWithType:`   | `…:pathOverride:eventLabel:eventValue:`                              |
| `trackConversionUnsubscribeWithType:`  | `…:pathOverride:eventLabel:eventValue:`                              |
| `trackConversionOffersWithType:`       | `…:pathOverride:eventLabel:eventValue:`                              |
| `trackConversionSurveysWithType:`      | `…:pathOverride:eventLabel:eventValue:`                              |
| `trackConversionAccountCreationType:`  | `…:pathOverride:eventLabel:eventValue:`                              |
| `trackConversionDownloadsType:`        | `…:pathOverride:eventLabel:eventValue:`                              |
| `trackConversionDonationsType:`        | `…:pathOverride:eventLabel:eventValue:`                              |
| `trackConversionPaymentsType:`         | `…:pathOverride:eventLabel:eventValue:`                              |
| `trackConversionSearchType:`           | `…:pathOverride:eventLabel:eventValue:`                              |
| `trackConversionSavesType:`            | `…:pathOverride:eventLabel:eventValue:`                              |

## Supported events

Below is a list of the event methods you can pass to the `trackConversion...` function. You should fire a Chartbeat conversion event from the same category on every page in a single conversion flow.&#x20;

### Subscriptions

Track paywall activity and subscription transactions on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="251">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypePaywallShown</code></td><td>A user was shown a paywall</td><td>Required</td></tr><tr><td><code>EventTypePaywallStart</code></td><td>A user started a subscription process</td><td><em>Optional</em></td></tr><tr><td><code>EventTypePaywallComplete</code></td><td>A user completed a subscription transaction</td><td>Required</td></tr><tr><td><code>EventTypePaywallCancel</code></td><td>A user canceled a subscription process</td><td><em>Optional</em></td></tr></tbody></table>

### Registrations

Track registration activity (i.e. users creating an account) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="298">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypeRegistrationShown</code></td><td>A user viewed a registration screen</td><td>Required</td></tr><tr><td><code>EventTypeRegistrationStart</code></td><td>A user started the registration process</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeRegistrationComplete</code></td><td>A user completed the registration process</td><td>Required</td></tr><tr><td><code>EventTypeRegistrationCancel</code></td><td>A user canceled the registration process</td><td><em>Optional</em></td></tr></tbody></table>

### **Newsletter Signups**

Track newsletter signup activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="336">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypeNewsletterSignupShown</code></td><td>A user was shown a newsletter signup option</td><td>Required</td></tr><tr><td><code>EventTypeNewsletterSignupStart</code></td><td>A user started a signup process</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeNewsletterSignupComplete</code></td><td>A user completed a signup process</td><td>Required</td></tr><tr><td><code>EventTypeNewsletterSignupCancel</code></td><td>A user canceled a signup process</td><td><em>Optional</em></td></tr></tbody></table>

### Unsubscribes

Track unsubscribe activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="298.5">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypeUnsubscribeShown</code></td><td>A user visited an unsubscribe page</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeUnsubscribeStart</code></td><td>A user started an unsubscribe flow</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeUnsubscribeComplete</code></td><td>A user unsubscribed</td><td>Required</td></tr><tr><td><code>EventTypeUnsubscribeCancel</code></td><td>A user dismissed an unsubscribe flow</td><td><em>Optional</em></td></tr></tbody></table>

### Offers

Track offer activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="241.5">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypeOfferShown</code></td><td>A user was shown an offer</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeOfferStart</code></td><td>A user started an offer</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeOfferComplete</code></td><td>A user completed an offer</td><td>Required</td></tr><tr><td><code>EventTypeOfferCancel</code></td><td>A user dismissed an offer</td><td><em>Optional</em></td></tr></tbody></table>

### Surveys

Track survey activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="246.33333333333331">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypeSurveyShown</code></td><td>A user was shown a survey</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeSurveyStart</code></td><td>A user started a survey</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeSurveyComplete</code></td><td>A user completed a survey</td><td>Required</td></tr><tr><td><code>EventTypeSurveyCancel</code></td><td>A user dropped off a survey</td><td><em>Optional</em></td></tr></tbody></table>

### Account Creations

Track user profile activity (e.g. filling out text fields) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="287.3333333333333">Event</th><th width="321">Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypeUserProfileShown</code></td><td>A user viewed their profile</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeUserProfileStart</code></td><td>A user started editing their profile</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeUserProfileComplete</code></td><td>A user completed editing to their profile</td><td>Required</td></tr><tr><td><code>EventTypeUserProfileCancel</code></td><td>A user exited profile-editing mode</td><td><em>Optional</em></td></tr></tbody></table>

### Downloads

Track download activity (e.g. pdf case studies, apps) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="258.3333333333333">Event</th><th width="297">Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypeDownloadStart</code></td><td>A user started downloading data</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeDownloadComplete</code></td><td>A download has completed</td><td>Required</td></tr><tr><td><code>EventTypeDownloadCancel</code></td><td>A download was canceled </td><td><em>Optional</em></td></tr></tbody></table>

### Donations

Track donation activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="264.3333333333333">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypeDonationShown</code></td><td>An option to donate was shown</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeDonationStart</code></td><td>A user started the donation process</td><td><em>Optional</em></td></tr><tr><td><code>EventTypeDonationComplete</code></td><td>A user completed the donation process</td><td>Required</td></tr><tr><td><code>EventTypeDonationCancel</code></td><td>A user canceled the donation process</td><td><em>Optional</em></td></tr></tbody></table>

### Payments

Track payment activity on your site (other than payment for a subscription).

<table data-header-hidden data-full-width="true"><thead><tr><th width="248.33333333333331">Event</th><th width="325">Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypePaymentStart</code></td><td>A user started a payment process</td><td><em>Optional</em></td></tr><tr><td><code>EventTypePaymentComplete</code></td><td>A user completed a payment process</td><td>Required</td></tr><tr><td><code>EventTypePaymentCancel</code></td><td>A user canceled a payment process</td><td><em>Optional</em></td></tr></tbody></table>

### Searches

Track completed search queries on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th>Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypeSearchComplete</code></td><td>A user performed a search</td><td>Required</td></tr></tbody></table>

### Saves

Track save activity (e.g. a user added an item to their "Favorites" collection) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th>Event</th><th>Description</th><th>Text</th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>EventTypeSaveComplete</code></td><td>A user saved an item for future retrieval</td><td>Required</td></tr></tbody></table>


# Video Tracking

| Method                                                                                                     | Description                       |
| ---------------------------------------------------------------------------------------------------------- | --------------------------------- |
| <ul><li>trackVideo:viewId:title:thumbnail:engagedTime:duration:playState:contentTime:playerTime:</li></ul> | Start tracking a video view       |
| <ul><li>updateVideoPlayState:duration:engagedTime:contentTime:playerTime:</li></ul>                        | Update an in-flight video's state |

**Play states**

| Value | Meaning          |
| ----- | ---------------- |
| s1    | Unplayed         |
| s2    | Played           |
| s3    | Paused / stopped |
| s4    | Finished         |


# API Reference

### API Reference

All methods live on \[CBTracker sharedTracker] unless noted otherwise.

#### Lifecycle

| Method                                              | Description                                                                    |
| --------------------------------------------------- | ------------------------------------------------------------------------------ |
| + sharedTracker                                     | Singleton accessor                                                             |
| <ul><li>setupTrackerWithAccountId:domain:</li></ul> | Recommended entry point — initialize account + domain                          |
| <ul><li>stopTracker</li></ul>                       | Pause tracking; resume by calling trackView again                              |
| <ul><li>active:</li></ul>                           | Manually mark the user inactive/active (default: YES)                          |
| <ul><li>setInterval:</li></ul>                      | Minimum seconds between beacons (default: 15) — takes effect on next trackView |

#### View tracking

| Method                                    | Description                                                                                |
| ----------------------------------------- | ------------------------------------------------------------------------------------------ |
| <ul><li>trackView:viewId:title:</li></ul> | Track a view (call in viewDidAppear:). viewId should look like a URL path starting with /. |

#### Metadata

| Method                                      | Description                                    |
| ------------------------------------------- | ---------------------------------------------- |
| <ul><li>setAuthors:</li></ul>               | Array of author-name strings                   |
| <ul><li>setSections:</li></ul>              | Array of section names                         |
| <ul><li>setZones:</li></ul>                 | Array of zone names                            |
| <ul><li>setDomain: (via property)</li></ul> | Main domain to report to                       |
| <ul><li>setSubDomain:</li></ul>             | Report-to sub-domain (usually equal to domain) |
| <ul><li>setAppReferrer:</li></ul>           | App referrer identifier                        |
| <ul><li>setPushReferrer:</li></ul>          | Push-notification referrer identifier          |
| <ul><li>setLocation:</li></ul>              | CLLocation for geo metadata                    |

#### User state

| Method                             | Description                                                                  |
| ---------------------------------- | ---------------------------------------------------------------------------- |
| <ul><li>userEngaged:</li></ul>     | YES = writing, NO = reading/interacting                                      |
| <ul><li>setUserPaid</li></ul>      | Mark user as paid subscriber                                                 |
| <ul><li>setUserLoggedIn</li></ul>  | Mark user as logged in                                                       |
| <ul><li>setUserAnonymous</li></ul> | Mark user as anonymous                                                       |
| <ul><li>setIdSync:</li></ul>       | NSDictionary\<NSString\*, NSString\*> for cross-product ID sync (e.g. GA ID) |
|                                    |                                                                              |

####


# React Native SDK

### Overview

`react-native-chartbeat` is a React Native wrapper around the native Chartbeat iOS and Android SDKs. It exposes a single JavaScript/TypeScript surface (`Chartbeat`) that routes calls to the appropriate native module at runtime.

* **Package name:** `react-native-chartbeat`
* **Source layout:**
  * `src/index.ts` — TypeScript API surface
  * `ios/RNChartbeat.{h,m}` — iOS native bridge
  * `android/src/**` — Android native bridge
  * `react-native-chartbeat.podspec` — iOS pod spec
  * `android/build.gradle` — Android module build

***

**Native dependencies (resolved automatically):**

* iOS: `Chartbeat` CocoaPod — pinned to **1.5.13**
* Android: `com.github.chartbeat:android_sdk` via JitPack — pinned to **1.8.0**

***

### Installation

#### 1. Install the package

```typescript
npm install react-native-chartbeat
# or
yarn add react-native-chartbeat
```

#### 2. iOS — install the pod

```typescript
cd ios && pod install
```

No manual linking required; autolinking picks up the module. The Chartbeat CocoaPod is declared in the podspec, so pod install pulls it automatically.

#### 3. Android — JitPack repository

The SDK's own `android/build.gradle` already declares `maven { url '<https://jitpack.io'> }`, so autolinking should work. If your app-level Gradle overrides repositories and your build fails with "could not find com.github.chartbeat:android\_sdk", add JitPack to your **root** `android/build.gradle` (or `settings.gradle` in newer RN projects):

```typescript
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}
```

#### 4. Expo

Custom native modules do **not** run in **Expo Go**. All Chartbeat calls will silently no-op (with a single console.warn on first call). Instead, use:

* **Expo development build:** `npx expo run:ios` / `npx expo run:android`
* **Bare React Native:** `npx react-native run-ios` / `run-android`
* **EAS Build:** `eas build` to produce a custom dev client or production build

***

### Quick Start

```typescript
import Chartbeat from 'react-native-chartbeat';

// 1. Initialize once at app startup with your account ID and dashboard ID
Chartbeat.setupTracker('12345', 'yoursite.com');

// 2. On each screen/article view, send viewID and title
Chartbeat.trackView('/articles/hello-world', 'Hello World');

// 3. Signal user engagement
Chartbeat.userInteracted();  // tap, scroll, etc.
Chartbeat.userTyped();        // text input

// 4. Stop the tracker when you're done (e.g. app teardown)
Chartbeat.stopTracker();
```

***

### Usage Guide

#### Initializing the tracker

Call `setupTracker` exactly once, before any other API call. Your account ID is passed as a string on both platforms.

```typescript
Chartbeat.setupTracker('12345', 'yoursite.com');
```

Replace the placeholder accountID `12345` with your organization’s specific Account ID and the placeholder dashboard ID `yoursite.com` with the dashboard ID of your site.

#### Tracking views

Call `trackView` each time the user navigates to a new screen or article.

```typescript
Chartbeat.trackView('/articles/hello-world', 'Hello World');
```

Include the `viewId` (e.g. `'/articles/hello-world'`) every time you call `trackView` .

To identify your pages and views in your dashboard, you should also include your view’s `title` (e.g. 'Hello World').

To accurately match pages and views across platforms, the `viewId` in your app **must** be set to the same string path (`p` key) that’s reported from your website. You can find more details about path settings by reviewing the documentation for [Canonical Links](http://support.chartbeat.com/docs/#canonical) and [Custom Path Variable](http://support.chartbeat.com/docs/#path).

#### Metadata

Set article metadata after `setupTracker` and before `trackView` (or between views, as the article changes).

```typescript
Chartbeat.setAuthors(['Jane Doe', 'John Smith']);
Chartbeat.setSections(['Technology', 'News']);
Chartbeat.setPushReferrer('push-campaign-42');
```

#### User Subscriber State

Set the [user's subscriber state](https://docs.chartbeat.com/cbp/tracking/standard-websites/subscriber-engagement) as Guest, Registered, or Subscriber.

```typescript
Chartbeat.setUserAnonymous(); // for Guest
Chartbeat.setUserLoggedIn(); // for Registered
Chartbeat.setUserPaid(); // for Subscriber
```

#### ID Sync

For iOS only, send ID Sync values if your organization is using [Datastream](https://docs.chartbeat.com/datastream/implement-tracking/id-sync).

```typescript
Chartbeat.setIdSync({ partnerA: 'abc', partnerB: 'def' }); 
```

#### Engagement Signals

| `userInteracted()`     | <p>iOS: <code>userEngaged(writing: false)</code></p><p>Android: <code>userInteracted()</code></p> |
| ---------------------- | ------------------------------------------------------------------------------------------------- |
| `userTyped()`          | <p>iOS: <code>userEngaged(writing: true)</code></p><p>Android: <code>userTyped()</code></p>       |
| `userLeftView(viewId)` | **Android only**; no-op on iOS                                                                    |

***

### Dependencies

* iOS: `Chartbeat` CocoaPod v1.5.13
* Android: `com.github.chartbeat:android_sdk` v1.8.0 via JitPack


# Conversion Tracking

Additional details about Conversion Tracking [can be found here](https://docs.chartbeat.com/cbp/feature-integrations/conversion/adding-subscriptions.js-to-your-site).&#x20;

Chartbeat's Conversion Tracking supports the following events:

* Subscriptions
* Registrations
* Newsletter Signups
* Unsubscribes
* Offers
* Surveys
* Account Creations
* Downloads
* Donations
* Payments
* Searches
* Saves

Every conversion method takes a typed enum value plus an [optional](https://docs.chartbeat.com/cbp/feature-integrations/conversion/conversion-events#event-parameters) ConversionOptions bag (`pathOverride`, `eventLabel`, `eventValue`):

```javascript
import Chartbeat, { PaywallType } from 'react-native-chartbeat';

Chartbeat.trackConversionPaywall(PaywallType.Shown);

Chartbeat.trackConversionPaywall(PaywallType.Complete, {
  pathOverride: '/paywall/monthly',
  eventLabel: 'monthly-sub',
  eventValue: '9.99',
});
```

All four-state conversion enums use the same values:

| Enum                  | `Shown` | `Start` | `Complete` | `Cancel` |
| --------------------- | ------- | ------- | ---------- | -------- |
| `PaywallType`         | 0       | 1       | 2          | 3        |
| `RegistrationType`    | 0       | 1       | 2          | 3        |
| `NewsletterType`      | 0       | 1       | 2          | 3        |
| `UnsubscribeType`     | 0       | 1       | 2          | 3        |
| `OfferType`           | 0       | 1       | 2          | 3        |
| `SurveyType`          | 0       | 1       | 2          | 3        |
| `AccountCreationType` | 0       | 1       | 2          | 3        |
| `DonationType`        | 0       | 1       | 2          | 3        |

Three-state enums (no `Shown`):

| Enum           | `Start` | `Complete` | `Cancel` |
| -------------- | ------- | ---------- | -------- |
| `DownloadType` | 0       | 1          | 2        |
| `PaymentType`  | 0       | 1          | 2        |

One-state enums:

| Enum         | `Complete` |
| ------------ | ---------- |
| `SearchType` | 0          |
| `SaveType`   | 0          |


# Video Tracking

```
Chartbeat.trackVideo({
  viewId: '/video/123',
  title: 'My Video',
  thumbnail: 'https://cdn.example.com/thumb.jpg',
  duration: 300,
  playState: 'playing',
  engagedTime: 0,
  contentTime: 0,
  playerTime: 0,
});
```

<table><thead><tr><th width="186.7109375">Name</th><th>Description</th></tr></thead><tbody><tr><td>viewId</td><td>The path of your video. This is a unique identifier that should be used consistently on all pages/platforms where the video is loaded.</td></tr><tr><td>title</td><td>The title of the video.</td></tr><tr><td>thumbnail</td><td>The thumbnail of the video.</td></tr><tr><td>duration</td><td>The duration of the video (in seconds).</td></tr><tr><td>playState</td><td>The play state, 'playing', 'paused', 'complete', 'unplayed'</td></tr><tr><td>engagedTime</td><td>The total time (in seconds) the user has actively engaged with the video up to this point.</td></tr><tr><td>contentTime</td><td>The total time (in seconds) the video content has been consumed.</td></tr><tr><td>playerTime</td><td>The current position of the video playback (in seconds).</td></tr></tbody></table>

{% code overflow="wrap" %}

```
// Periodically update as playback progresses
Chartbeat.updateVideoPlayState(
  'playing',  // playState
  300,        // duration (s)
  12,         // engagedTime (s)
  45,         // contentTime (s)
  45          // playerTime (s)
);
```

{% endcode %}


# Integration QA Steps: Mobile Apps

Recommended steps to test your mobile app integration for accuracy.

## Check your Implementation Review

Our Implementation Review lets Admin users view data snapshots from the latest full calendar day we have on hand.

Please note that a 5-10% difference between platforms is generally expected. If data is not coming through as expected, check out  common issues in troubleshooting pageviews and unique discrepancies page

For details on general use of the dashboard please see our Implementation Review Introduction in our documentation &#x20;

#### To access your Implementation Review:

1. [**Sign in**](https://chartbeat.zendesk.com/hc/en-us/articles/360017718093) to your Chartbeat account.&#x20;
2. Navigate to Settings from the menu top right-head&#x20;
3. Implementation Review pages on the left side menu
4. Navigate to the Platform Audit and Traffic Audit Tabs

{% hint style="warning" %}
**Tip:** If you cannot access the Implementation Review, verify with your organization if your account has the required Admin role. &#x20;
{% endhint %}

Once you're able to access the dashboard, here are some useful QA steps to ensure proper tracking:

#### **Platform Audit**

1. Navigate to the Platform Audit Tab to view the Implementation Summary Table
2. Locate the "Mobile Apps" column  and corresponding cell for the site and platform of interest

If there is a green checkmark in the corresponding cell, that means Chartbeat is receiving significant web traffic from the site at the time snapshot was taken.

#### **Traffic Audit**

1. Select a site and verify the timezone and snapshot date. &#x20;
2. Summary table: reference Web pageviews breakdown to validate snapshot counts
3. Top Articles by pageviews: View the top 20 [canonical paths](/cbp/tracking/standard-websites/configuration-variables#page-ids-canonical-urls-vs-custom-paths) and corresponding pageviews across all platforms

If data in the Implementation Review snapshots are as expected, your site is implemented correctly for this Mobile Apps. If more QA or troubleshooting is needed please continue with the additional QA steps below to assess your implementation.&#x20;

## Using debug mode

When debug mode is enabled, exceptions will be thrown when the tracker is not started properly.&#x20;

{% hint style="info" %}
**Note:** When you are ready to submit the app for review, remember to disable debug mode.
{% endhint %}

### Enable debug mode for iOS

For iOS, you can switch between logging tiers to see output from the tracker to help you with integration. There are four logging tiers:

* **verbose:** output everything
* **debug:** output debug related info, warning and error
* **warning:** output warnings and errors
* **error:** output errors (see property `loglevel`)

All log output has the format `[CB]: xxxx`

#### **Objective-C**

```objectivec
[CBTracker sharedTracker].debugMode = true;
[CBTracker sharedTracker].logLevel = CBTrackerLogLevelVerbose;i
```

#### **Swift**

```objectivec
CBTracker.shared().debugMode = true
CBTracker.shared().logLevel = .verbose
```

### Enable debug mode for Android

Set `com.chartbeat.androidsdk.DEBUG` to true before your first call to `setupTracker`.

### Enable debug mode for React Native

#### Turn on verbose native logs

```
Chartbeat.setDebugMode(true);
```

Call this immediately after setupTracker.

#### iOS — view native logs

Open **Console.app**, filter by the iOS Simulator process, and search for Chartbeat-related messages, or stream from the terminal:

```
xcrun simctl spawn booted log stream --level debug --style compact \
  --predicate 'eventMessage CONTAINS "[CB]:Pinging"'
```

#### Android — view native logs

Open **Logcat** in Android Studio, or stream from the terminal:

```
adb logcat -T 1 | grep -i pingclient
```

## Test your app integration in a sandbox dashboard

We recommend first adding our code in a test app, and using a corresponding test dashboard in your Chartbeat account to view the data before pushing your app to production with your prod site ID. For example, if your production site ID is `mysite.com`, your test site ID could be `test.mysite.app`. In the following instruction articles for our Android and iOS SDK, remember when adding our code to your test app to use test site ID and update it to your production site ID only when your team is finished QA'ing the integration for data accuracy.&#x20;

{% hint style="info" %}
**Note**: The test Site will need to be added to your organization's Chartbeat account to see your data in this sandbox dashboard. If you run into any issues adding this sandbox site to your account, contact your Chartbeat support representatives and we will add it for you.
{% endhint %}

### Check your Real-Time Dashboard

Once you've added our code to your test app and added the test Site to your Chartbeat account, you can now watch real-time traffic data populate your test dashboard as your team interacts with different pages of your test app.&#x20;

To view your test dashboard:

1. [**Sign in**](https://chartbeat.zendesk.com/hc/en-us/articles/360017718093) to your Chartbeat account.
2. From the [**Sites page**](https://chartbeat.com/publishing/sites/), find your test Site and hover over the site entry to reveal the 'Real-Time' button.
3. Click the 'Real-Time' button.

{% hint style="success" %}
**Tip:** If you do not have a Chartbeat user account, request access from one of your account admins.
{% endhint %}

## App integration QA checklist

* Requests to ping.chartbeat.net are made from all pages of the app where our SDK is implemented and the `trackView` function is called
* Unique `viewId` (page path) values are sent from every tracked view within your app
  * `viewId` values should be consistent for each unique screen across your iOS and Android apps, and you can check the value being sent to our servers in the "p=" parameter included in the outbound heartbeat requests to ping.chartbeat.net
  * `viewId` values should also match the canonical web URLs we receive from corresponding pages on your website if you intend to view app and web traffic data in the same dashboard
  * For views unique to the app without corresponding pages on your website, we recommend passing placeholder web URLs such as `https://www.mysite.com/app-homepage`
* Each in-app page with a corresponding page on your website should also pass section, author, and title values which match exactly the sections, authors and titles we receive from the webpage
* For the Android SDK, `userInteracted` calls should be made each time a visitor touches the screen&#x20;

{% hint style="success" %}
**Tip:** For Android Oreo (8.0) and above, if you use Proguard for code optimization, a file has been added to our SDK library (version [**1.6.6**](https://jitpack.io/#chartbeat/android_sdk/) and above) that should make Proguard builds including our library work automatically. If Proguard builds that include the Chartbeat SDK Library fail, the Proguard file can be added directly to your application’s Proguard rules.
{% endhint %}

In the Chartbeat requests to ping.chartbeat.net, the following query string parameters can be reviewed to see how data is being passed from each page via your team's code implementation:

| QSP/Key | Description                                                           |
| ------- | --------------------------------------------------------------------- |
| h       | The domain (site ID) value set in your SDK implementation             |
| p       | The viewId (page path) value your app is sending for the current view |
| g0      | The section value(s) for the current view                             |
| g1      | The author value(s) for the current view                              |
| i       | Page title for the current view                                       |
| E       | Total engaged seconds so far for each page session                    |

The p, g0, g1 & i parameters should update with new values as you navigate to new views within the app, containing the unique page path, section, author, and title for each tracked view.

## Up next

#### Choose your next integration.

* [Standard Website Tracking](/cbp/tracking/standard-websites)
* [Google AMP Tracking](/cbp/tracking/google-amp)
* ~~**Mobile App SDKs**~~ ✅
* [Headline and Image Testing](/cbp/feature-integrations/testing)
* [Video Tracking ](/cbp/feature-integrations/video-engagement)&#x20;


# Troubleshooting

### iOS SDK

| Symptom                                             | Likely cause                                                       | Fix                                                                                                                         |
| --------------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| No data on dashboard                                | Tracker never started, wrong account ID, or trackView never called | Enable debugMode = YES; verify setupTrackerWithAccountId:domain: runs at launch and each view calls trackView:viewId:title: |
| Pings too frequent / infrequent (for your use case) | Default interval not tuned                                         | Call setInterval: (seconds) before next trackView                                                                           |
| CocoaPods: unable to find spec "Chartbeat"          | CocoaPods cache stale                                              | pod repo update then pod install                                                                                            |

##

### React Native SDK

| Single warning react-native-chartbeat: Native module not available… | Running under Expo Go, or native module not autolinked            | Use an Expo development build / bare RN / EAS Build; for bare RN, run pod install (iOS) and clean-rebuild Android                                                                                   |
| ------------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Gradle error: Could not find com.github.chartbeat:android\_sdk      | App-level Gradle overrides repositories and drops JitPack         | Add maven { url '<[https://jitpack.io'>](https://docs.chartbeat.com/cbp/tracking/mobile-app-sdks/qa-mobile-sdk-integration/https:/jitpack.io'>) } to root android/build.gradle (or settings.gradle) |
| iOS build error: No such module 'Chartbeat'                         | Pod not installed after package install                           | cd ios && pod install                                                                                                                                                                               |
| No data in Chartbeat dashboard                                      | setupTracker not called before trackView, or account/domain wrong | Verify setupTracker('\<accountId>', '\<domain>') runs at app start, and enable setDebugMode(true) to confirm pings are firing                                                                       |


# Debug

## Using debug mode

When debug mode is enabled, exceptions will be thrown when the tracker is not started properly.&#x20;

{% hint style="info" %}
**Note:** When you are ready to submit the app for review, remember to disable debug mode.
{% endhint %}

### Enable debug mode for iOS

For iOS, you can switch between logging tiers to see output from the tracker to help you with integration. There are four logging tiers:

* **verbose:** output everything
* **debug:** output debug related info, warning and error
* **warning:** output warnings and errors
* **error:** output errors (see property `loglevel`)

All log output has the format `[CB]: xxxx`

#### **Objective-C**

```objectivec
[CBTracker sharedTracker].debugMode = true;
[CBTracker sharedTracker].logLevel = CBTrackerLogLevelVerbose;i
```

#### **Swift**

```objectivec
CBTracker.shared().debugMode = true
CBTracker.shared().logLevel = .verbose
```

### Enable debug mode for Android

Set `com.chartbeat.androidsdk.DEBUG` to true before your first call to `setupTracker`.

### Enable debug mode for React Native

#### Turn on verbose native logs

```
Chartbeat.setDebugMode(true);
```

Call this immediately after setupTracker.

#### iOS — view native logs

Open **Console.app**, filter by the iOS Simulator process, and search for Chartbeat-related messages, or stream from the terminal:

```
xcrun simctl spawn booted log stream --level debug --style compact \
  --predicate 'eventMessage CONTAINS "[CB]:Pinging"'
```

#### Android — view native logs

Open **Logcat** in Android Studio, or stream from the terminal:

```
adb logcat -T 1 | grep -i pingclient
```

## Example Pings

#### Android

Example URL from View Tracking:

`https://ping.chartbeat.net/ping?h=androidsdktest.chartbeat.com&p=/korean-peace-talks.html&u=ZVEmdQVqqa00J71RvO&d=androidsdktest.chartbeat.com&g=1&_acct=anon&n=0&f=F0001&c=0.00&j=30&R=1&W=0&I=0&E=1&e=1&v=/&t=iDg9zmv4Loy0zmVOLd7-heiOfhvyE5&V=2013&D=ssShUwV_bK51dFzrQFHtCGtOyCMLbL&i=Sec. Schwartz Attends Korean Peace Talks&tz=-240&S=426&Z=1&sr=push/?id%3D123456a&sd=2&sv=fbgnceXLZMJwM2wgCUftDJopJb8D&_`

**Breakdown:**

```
https://pong.chartbeat.net/ping?
h=androidsdktest.chartbeat.com
p=/korean-peace-talks.html
u=ZVEmdQVqqa00J71RvO
d=androidsdktest.chartbeat.com
g=1
_acct=anon
n=0
f=F0001
c=0.00
j=30
R=1
W=0
I=0
E=1
e=1
v=/
t=iDg9zmv4Loy0zmVOLd7-heiOfhvyE5
V=2013
D=ssShUwV_bK51dFzrQFHtCGtOyCMLbL
i=Sec.%20Schwartz%20Attends%20Korean%20Peace%20Talks
tz=-240
S=426
Z=1
sr=push/?id%3D123456a
sd=2
sv=fbgnceXLZMJwM2wgCUftDJopJb8D
_
```

#### iOS

Example URL from View Tracking

`https://ping.chartbeat.net/ping?p=nonVideo/sharks-win-big.html&c=0.25&V=1014&I=0&sv=eS1kOVVKRUVTX2FpU0ptdm1jR0dCZ19f&e=11&W=1&f=a0601&t=NW5wTGJDbk9SR3lrdGR6Qk1SVHBvZ19f&g=54876&u=VkhrMzkwVFFSdVdFTH&Z=1&h=iphonesdktest.chartbeat.com&i=Sharks Win Big. Mets%2C Knicks Lose Again&j=30&x=0&y=2622&sr=&m=0&R=0&E=11&o=1206&sd=8&_`

**Breakdown:**

```
https://ping.chartbeat.net/ping?
p=nonVideo/sharks-win-big.html
c=0.25
V=1014
I=0
sv=eS1kOVVKRUVTX2FpU0ptdm1jR0dCZ19f
e=11
W=1
f=a0601
t=NW5wTGJDbk9SR3lrdGR6Qk1SVHBvZ19f
g=54876
u=VkhrMzkwVFFSdVdFTH
Z=1
h=iphonesdktest.chartbeat.com
i=Sharks%20Win%20Big.%20Mets%2C%20Knicks%20Lose%20Again
j=30
x=0
y=2622
sr=
m=0
R=0
E=11
o=1206
sd=8
_
```


# Headline and Image Testing

A guide to integrating Chartbeat Headline Testing for your homepage, with or without our Image Testing add-on.

## Articles included in this guide:

1. [Adding chartbeat\_mab.js to Your Site](/cbp/feature-integrations/testing/homepage-testing-code)
2. [Image Compatibility](/cbp/feature-integrations/testing/image-compatibility)
3. [Flicker & Flicker Control](/cbp/feature-integrations/testing/flicker-and-flicker-control)
4. [mab.js Specifications](/cbp/feature-integrations/testing/mabjs-specifications)
5. [Integration QA Steps: Headline and Image Testing](/cbp/feature-integrations/testing/qa-homepage-testing-integration)

{% hint style="warning" %}
Chartbeat **Headline Testing** is a Premium feature that is not included within all customer plans. **Image Testing** is an add-on feature not included within all customers plans. If your team would like to add these features to your Chartbeat software subscription, please reach out to your Customer Success Manager or to the Technical Solutions team at [**support@chartbeat.com**](mailto:support@chartbeat.com).
{% endhint %}


# Adding chartbeat\_mab.js to Your Site

An introduction to our code snippet for testing headlines and images on your site. Note that our homepage testing tools are not included in all Chartbeat service plans.

## Implementation requirements

Headline and Image Testing are only supported for websites using our tracking library (chartbeat.js or chartbeat\_video.js), covered in the [**website tracking guide**](/cbp/tracking/standard-websites/our-javascript) for adding Chartbeat tracking to your site.

Our chartbeat\_mab.js library is responsible for Chartbeat's headline and image testing functionality on your sites. Our tool makes it easy to get started testing elements on your homepage; simply implement the required JavaScript outlined over the next several pages of this guide and our code takes care of the rest. Your sites' editors can begin using the tool from within our [**Heads Up Display interface**](https://chartbeat.zendesk.com/hc/en-us/articles/360023929453-Guide-to-Headline-Testing#h_2398e757-d24d-44f1-8374-fc8a55f34474).&#x20;

There are a few primary requirements for loading mab.js on your site to ensure optimal functionality...

#### First requirement:

Our testing script, ​chartbeat\_mab.js​​, should be loaded in the ​`<head>` on any page where tests will be conducted. Our default code snippet utilizes the ​async attribute to load this script asynchronously:&#x20;

```markup
<script async src="//static.chartbeat.com/js/chartbeat_mab.js"></script>
```

#### Second requirement:

Prior to chartbeat\_mab.js loading, the following configuration variables should already be initialized in your Chartbeat code snippet: \
• `domain` \
• `uid`\
• `flickerControl`\
• `useCanonical` & `useCanonicalDomain` OR `path`

If your chosen Chartbeat configuration includes the `useCanonical` and `useCanonicalDomain` variables to pull your page paths from the canonical URL element in your source code, these variables must be initialized *after* the `rel=canonical` element loads in your site's header, and followed by the script tag which loads mab.js. Similarly, our code should load after your page `<title>` tags near the top of the `<head>`.

Ideally, mab.js should load as early in the page load sequence as possible, so best practice is to ensure the above elements are loaded near the top of the `<head>` in this order:&#x20;

1. `rel=canonical` link element loads
2. Chartbeat snippet loads, initializing the required variables listed above
3. mab.js is loaded with the `async` attribute

#### Third requirement:

Headline and Image Testing rely on a first-party cookie set by our JavaScript, so it is necessary to have first-party cookies enabled for your site. If your site uses our optional [**`noCookies` configuration variable**](/cbp/tracking/standard-websites/configuration-variables#no-cookies) set to `true` to disable Chartbeat's first-party cookies, Headline and Image Testing will not work. See our [**alternative website integrations page**](/cbp/tracking/standard-websites/alternative-integrations-web#gdpr-and-ccpa-compliance) for instructions on how to customize your Chartbeat tracking code to use our noCookies variable in a way that is compatible with homepage testing.

## Configuration options

Our JavasScript code can be added to your page templates as one snippet or two separate snippets, both options ensuring that our chartbeat\_mab.js library is loaded in the page header (before the closing `<head>` tag).

### One snippet solution

Below is our standard JavaScript code for web tracking and homepage testing functionality, including our recommended [**configuration variables**](/cbp/tracking/standard-websites/configuration-variables). The below script tags should be loaded in your site's global header (before the closing `<head>` tag), *after* the `rel="canonical"` link element in your HTML.

```markup
<script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.uid = #####; //CHANGE THIS
        _sf_async_config.domain = 'domain.com'; //CHANGE THIS
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        _sf_async_config.flickerControl = false;
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
     })();
</script>
<script async src="//static.chartbeat.com/js/chartbeat_mab.js"></script>
```

### Two snippet solution

If your site loads our tracking code in the `<body>`, either by necessity of your tag manager platform or because your data layer is only available to initialize the section/author or subscriber variables later in the page load, you can load our testing code separately in the `<head>` following the instructions below.

We recommend splitting our code into the following two snippets with the first snippet being implemented directly in your source code, after canonical URLs are defined in your header HTML.

#### Header snippet

```markup
<script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.uid = #####; //CHANGE THIS
        _sf_async_config.domain = 'domain.com'; //CHANGE THIS
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.flickerControl = false;
        /** CONFIGURATION END **/
     })();
</script>
<script async src="//static.chartbeat.com/js/chartbeat_mab.js"></script>
```

The above code needs to load early in the `<head>` after canonical URLs are loaded because it is responsible for altering the headlines and images in your homepage template to display test variants to your visitors, so this should happen before your CMS headlines and images become visible to the user to prevent "flicker" (more on flicker and how to prevent it in the next article of this guide).

The below code includes the rest of our tracking snippet, and can be loaded near the top of the `<body>` , or whenever your data layer is available with the necessary [**section and author**](/cbp/tracking/standard-websites/configuration-variables#page-sections-and-authors) or [**user subscriber status**](/cbp/tracking/standard-websites/subscriber-engagement) data.

#### Body snippet

```markup
<script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
     })();
</script>
```

## Configuration variables

### Headline flicker control on/off

Set this variable to `false` if your site does not utilize our headline flicker control JavaScript detailed in a [subsequent page of this guide](https://docs.chartbeat.com/cbp/feature-integrations/testing/flicker-and-flicker-control#option-2-headline-flicker-control-enabled). Set this variable to `true` if your site *does* utilize our **headline** testing flicker control JavaScript. **Note**: If using our **image** testing flicker control settings, set `flickerControl` to false.

| Field Name       | Value Type | Default Value | Max Length | Example Value |
| ---------------- | ---------- | ------------- | ---------- | ------------- |
| `flickerControl` | boolean    | `true`        | *None*     | `false`       |

Example usage:

```javascript
_sf_async_config.flickerControl = true;
```

### Top storage domain

Use this variable **only** if your homepage links out to subdomain pages of your site. This variable must be set to the top-level domain of your website in order to run tests on headlines or images that link to articles pages under unique subdomains. For example, if I want to test a headline on the mydomain.com/ homepage, for an article that lives under mysub.domain.com, this variable should be set to `mydomain.com` in the mysub.domain.com article page Chartbeat code.

| Field Name         | Value Type | Default Value | Max Length | Example Value |
| ------------------ | ---------- | ------------- | ---------- | ------------- |
| `topStorageDomain` | text       | *None*        | *None*     | `mysite.com`  |

Example usage:

```javascript
_sf_async_config.topStorageDomain = 'mysite.com';
```

### Lazy load image attribute (Image Testing only)

**For Chartbeat Image Testing customers:** If your site uses custom lazy load attribute names in its image markup outside of `data-src` and `data-srcset` (for example, `data-img-sm`, `data-img-lg`), you will need to set this variable ​to list the names of these lazy load attributes so our code can detect and replace those image urls. This variable accepts multiple comma separated string values as shown in the example below.

| Field Name                | Value Type | Default Value | Max Length | Example Value |
| ------------------------- | ---------- | ------------- | ---------- | ------------- |
| `lazyLoadImageAttributes` | text       | *None*        | *None*     | `data-img-sm` |

Example usage:

```javascript
_sf_async_config.lazyLoadImageAttributes = 'data-img-sm,data-img-md,data-img-lg';
```

### Article block selector (Image Testing only)

**For Chartbeat Image Testing customers:** This variable must be set if your homepages use custom HTML elements to encapsulate each headline/byline/image link combination in your homepage source code. By default, this variable will be set to `article`, so if your site uses only `<article>` tags to encapsulate your headline/image `<a>` tags, you do not need to include this variable in your Chartbeat code snippet. However, if your site uses any additional tags for different image/headline link positions on your homepage, each of those elements needs to be assigned to this configuration variable separated by commas, as shown in the example below.

| Field Name             | Value Type | Default Value | Max Length | Example Value  |
| ---------------------- | ---------- | ------------- | ---------- | -------------- |
| `articleBlockSelector` | text       | `article`     | *None*     | `section.item` |

Example usage:

```javascript
_sf_async_config.articleBlockSelector = 'article,section.item,div.top-story';
```

## Next steps

**For Image and Headline Testing customers:** Read through the next article in this guide to determine Image Testing compatibility with your homepage's image markup formatting:

{% content-ref url="/pages/-MIF86XIrs8cOpT9cQKA" %}
[Image Compatibility](/cbp/feature-integrations/testing/image-compatibility)
{% endcontent-ref %}

**For Headline Testing&#x20;*****only*****&#x20;customers:** Skip ahead to learn about headline flicker, and if necessary, how to prevent it with our optional implementation to prevent flicker, called "flicker control":

{% content-ref url="/pages/-LYJL0SBqNcDyFC0c3An" %}
[Flicker & Flicker Control](/cbp/feature-integrations/testing/flicker-and-flicker-control)
{% endcontent-ref %}


# Image Compatibility

Customers planning to use our Image Testing add-on should review the below information to determine whether or not Chartbeat Image Testing is compatible with your homepage image markup formats.

{% hint style="success" %}
**Tip:** This article is only relevant for customers evaluating our Image Testing add-on. For customers using only our Headline Testing feature, skip ahead to the [**next page**](/cbp/feature-integrations/testing/flicker-and-flicker-control) of this guide.
{% endhint %}

We support current standards and best practices of displaying images in modern web browsers. Although we will always be working to expand the methods of image display we support, the sheer breadth of variables prevents us from being compatible with every method.&#x20;

## **Supported image formats**

✅ **We support testing** on responsive `<picture>` elements with `<source>` and `<img>` children with `srcset` attributes.

Example:

```markup
<picture>
    <source srcset="https://chartbeat.com/large.jpg, https://chartbeat.com/large-retina.jpg 2x" media="(min-width: 1200px)">
    <source srcset="https://chartbeat.com/medium.jpg, https://chartbeat.com/medium-retina.jpg 2x" media="(min-width: 976px)">
    <source srcset="https://chartbeat.com/small.jpg, https://chartbeat.com/small-retina.jpg 2x" media="(min-width: 576px)">
    <img srcset="https://chartbeat.com/mobile.jpg" alt="A picture">
</picture>
```

✅ **We support testing** on responsive `<img>` elements with `src` and `srcset` attributes.

Example:

```markup
<img src="https://chartbeat.com/image.jpg" srcset="https://chartbeat.com/image1x.jpg 1x, https://chartbeat.com/image2x.jpg 2x">
```

✅ **We support testing** on non-responsive `<img>` elements with a single `src` attribute defined on page load.

Example:

```markup
<img src="https://chartbeat.com/image.jpg">
```

✅ **We support testing** on images with lazy load source or source-set attributes for any of the above formats (i.e. `data-src` or any other attribute names that your website uses for this purpose).

Example:

```markup
<img data-src="https://chartbeat.com/image.jpg">
<img data-img-sm="https://chartbeat.com/image1.jpg" data-img-lg="https://chartbeat.com/image2.jpg">
```

## **Unsupported image formats**

❌ **We do not support testing** for `<img>` elements that are not descendants of an `<a>` tag (anchor element). Tests can only be initiated on `<img>` elements that are descendants of an anchor tag linking to the target article.

Example:

```markup
<!-- Unsupported -->
<article> 
  <img src="image.jpg">
  <a href="test.com/article1">
    <h3>Headline Text</h3>
  <a>
</article>

<!-- Supported -->
<article> 
  <a href="test.com/article1">
    <img src="image.jpg">
  </a>
  <a href="test.com/article1">
    <h3>Headline Text</h3>
  <a>
</article>

<!-- Also supported -->
<article> 
  <a href="test.com/article1">
    <img src="image.jpg">
    <h3>Headline Text</h3>
  <a>
</article>
```

❌ **We do not support testing** for `<picture>` tags that do not properly have an `<img>` element as a child.

Example:

```markup
<picture><div class="background-css-image-here"></div></picture>
```

❌ **We do not support testing** images that use the `background-image` CSS property.

Example:

```markup
<div style="background-image: url('image.jpg');"></div>
```

❌ **We do not support testing** on responsive `<img>` elements that have their `src` attributes dynamically altered by JavaScript.

* This can usually be determined by inspecting an `<img>` element at mobile and desktop breakpoints: if the `srcs` differ at each breakpoint, JavaScript is dynamically altering the `src`.
* Chartbeat code does not have a way to determine whether this is or is not the case as the markup is indistinguishable from supported elements. In the rare case that your site uses this as well as supported formats, use caution when making a test.

❌ **We do not support testing** on `<picture>` or `<img>` elements that are injected dynamically into the page by JavaScript dependent on user action.

## Partial homepage compatibility

If your site uses both supported and unsupported formats for different positions on your homepage, you will only be able to successfully run image tests on supported formats. You will still be able to run headline tests on positions with unsupported image formats. Inspect your page layouts to confirm if you can run image tests in each section.

{% hint style="warning" %}
**Important:** The above list of supported and unsupported image formats does not cover all possible image markup formats. Given the variety of methods used to display images across the web, we expect to uncover new image markup formats as we onboard new clients to this feature. As we discover more compatible and incompatible image markups, we will update this document accordingly.
{% endhint %}

## Next steps

Read through the next article to learn about headline and image flicker, and if necessary, how to prevent it with our optional implementation to prevent flicker, called "flicker control".


# Flicker & Flicker Control

"Flicker" explained — why it occurs, and how to prevent it on sites that are optimized for fast first paint.

## What is flicker?

"Flicker" is how we refer to the visitor experience of witnessing the very brief appearance of your site's above-the-fold CMS' headline and/or image before it quickly changes to a different headline or image from your active Chartbeat headline/image test. This can happen for users accessing your site with slow connection speeds where our JavaScript does not finish swapping out the default headline or image prior to those elements being visible on the page. It can can happen more often if our headline testing code is not placed near the top of your page's source code after the opening `<head>` tag (adding our code near the top of your site's `<head>` places it first in line to be downloaded by client browsers, guaranteeing a faster download completion speed).

Customers using our Image Testing add-on to run image tests are more likely to require a solution to flicker ("flicker control") to prevent flash-of-original-image for their end users because our JavaScript requires that your images have finished loading prior to replacing the image. This does not apply for headline tests where the occurrence of flicker will be much less, and headline flicker is less visible than image flicker when it does occur. For this reason, we have different recommendations for our customers to deal with potential flicker based on whether or not your sites will use Headline Testing *only* or Headline *and* Image Testing, both outlined below.

## Flicker control for Headline Testing *only* customers

### Option 1: Headline flicker control disabled

Our default implementation detailed on the [**first page**](/cbp/feature-integrations/testing/homepage-testing-code) of this guide include initializing the `flickerControl` variable to `false` and subsequently calling mab.js with the `async` attribute in your site's header. This setting *disables* our "flicker control" code contained within chartbeat\_mab.js, which would *otherwise* automatically hide all elements contained within the `<body>` tags of your website until our code has completed changing headline text in your HTML (in order to prevent the possibility of your visitors experiencing headline flicker).

We recommend this implementation (`flickerControl` set to `false`) because headline flicker is unlikely to occur or be detectable by the naked eye for most websites running only headline tests with Chartbeat as long as mab.js is loaded near the top of your site's `<head>`. This way, our script *does not* briefly hide elements of your page ("flicker control") to prevent flicker.&#x20;

The below example demonstrates a homepage with Chartbeat tracking and Headline Testing enabled, flicker control *disabled* (line 21)*.*

```markup
<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">

  <!-- Page title -->
  <title>My Site's Homepage</title>
  <meta name="description" content="My Site's Homepage">

  <!-- Set canonical URL -->
  <link rel="canonical" href="https://mysite.com/article-xyz.html" />
  
  <!-- Load Chartbeat tracker and tester -->
  <script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.uid = #####; //CHANGE THIS
        _sf_async_config.domain = 'mysite.com'; //CHANGE THIS
        _sf_async_config.flickerControl = false;
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
  </script>
  <script async src="//static.chartbeat.com/js/chartbeat_mab.js"></script>

</head>

<body>

</body>
</html>
```

### Option 2: Headline flicker control enabled

{% hint style="success" %}
**Tip:** We recommend this implementation for websites that are optimized for very fast first paint, making flicker more likely to occur. We’ve found that for most sites using our [**default implementation**](/cbp/feature-integrations/testing/homepage-testing-code#one-snippet-solution) (above), headlines are replaced fast enough that there is no noticeable impact to the visitor experience.
{% endhint %}

If the `flickerControl` variable is not set to `false` when ​chartbeat\_mab.js loads, we will look for a style tag on the page with id ​`#chartbeat-flicker-control-style` and if it doesn't exist, we will immediately add a style tag to the page with that id, with one rule that sets the `<body>` visibility to hidden. Once we’ve loaded our strategy from the CDN and replaced headlines in the document, we remove any style tag on the page with id ​`#chartbeat-flicker-control-style`​​.This causes a *perceived* increase to your page's load time since the `<body>` is hidden temporarily, which may not be ideal.&#x20;

For this reason, we recommend adding the below code to your Chartbeat `<head>` snippet only if you need to implement flicker control for headlines. It allows you to adjust the ​`#chartbeat-flicker-control-style` style tag to target something other than the entire body — for instance, you could target ​`#main_content` if you have a content container that contains all headlines that would be tested, or target something like ​`h1` or ​`.headline` to only make the actual headline text itself invisible until the text is updated by chartbeat\_mab.js​​. The details will depend on how your HTML and CSS are structured, but fine-tuning ​`#chartbeat-flicker-control-style` may allow you to avoid flash-of-original-headline while allowing most of the page to render even if headlines haven’t been replaced yet.&#x20;

If implementing flicker control on your site, the `flickerControl` configuration variable should instead be set to `true`, and the following code should be added to your site's `<head>` snippet prior to the chartbeat\_mab.js script tag:

```markup
<script type='text/javascript'>
    (function() {

        // Change below from "body" to a CSS selector that selects only your headlines
        var css = 'body { visibility: hidden; }'; 
        
        var head = document.head || document.querySelector('head');
        var style = document.createElement('style');
        style.id = 'chartbeat-flicker-control-style';
        head.appendChild(style);
        style.type = 'text/css';
        if (style.styleSheet) {
            // This is required for IE8 and below.
            style.styleSheet.cssText = css;
        } else {
            style.appendChild(document.createTextNode(css));
        }

        window.setTimeout(function () {
            var hider = document.getElementById('chartbeat-flicker-control-style');
            if (hider) {
              hider.parentNode.removeChild(hider);
            }
        }, 1000);
    })();
  </script>
```

The `setTimeout` function in this script script above is just a failsafe; in case ​chartbeat\_mab.js doesn’t load for any reason, it will make sure the body does not remain hidden. The expected behavior is that ​chartbeat\_mab.js is either already in the visitor’s browser cache or loads from our CDN very quickly, updates any headlines under test, and removes the `#chartbeat-flicker-control-style​​` tag much much faster than the 1-second timeout included here (line 24 above).

Here's what that looks like altogether, included directly below our default tracking code snippet and prior to the mab.js script tag in our example homepage source code:

```markup
<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">

  <!-- Page title -->
  <title>My Site's Homepage</title>
  <meta name="description" content="My Site's Homepage">

  <!-- Set canonical URL -->
  <link rel="canonical" href="https://mysite.com/article-xyz.html" />
  
  <!-- Load Chartbeat tracker and tester -->
  <script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.uid = #####; //CHANGE THIS
        _sf_async_config.domain = 'mysite.com'; //CHANGE THIS
        _sf_async_config.flickerControl = true;
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
  </script>
  <script type='text/javascript'>
    (function() {

        // Change below from "body" to a CSS selector that selects only your headlines
        var css = 'body { visibility: hidden; }'; 
        
        var head = document.head || document.querySelector('head');
        var style = document.createElement('style');
        style.id = 'chartbeat-flicker-control-style';
        head.appendChild(style);
        style.type = 'text/css';
        if (style.styleSheet) {
            // This is required for IE8 and below.
            style.styleSheet.cssText = css;
        } else {
            style.appendChild(document.createTextNode(css));
        }

        window.setTimeout(function () {
            var hider = document.getElementById('chartbeat-flicker-control-style');
            if (hider) {
              hider.parentNode.removeChild(hider);
            }
        }, 1000);
    })();
  </script>
  <script async src="//static.chartbeat.com/js/chartbeat_mab.js"></script>

</head>

<body>

</body>
</html>
```

{% hint style="success" %}
**Tip:** Remember to change`body`in your flicker control code (line 42 above) to a CSS selector that targets only your page headlines in order to avoid temporarily hiding all elements in your site body. Also, feel free to adjust the 1000 millisecond timeout (line 61 above) to allow more time for our JS to finish loading (only relevant for users of your site with slow network connections).
{% endhint %}

## Flicker control for Image and Headline Testing customers

### Option 1: Image and headline flicker control enabled

The noticeability of image flicker varies from site to site and can go unnoticed entirely by your visitors if you are testing images below the fold where images have time to be replaced before they are viewed. If your homepage team will be image testing above the fold, we highly recommend that you implement our flicker control solution to avoid this issue and ensure that readers have the most seamless experience possible.

In summary, our flicker control code for sites with image testing will auto-hide images and headlines on page load, and only unhides these elements when (A) mab.js finishes loading or (B) a 1-second default timeout elapses. For end users of your site viewing from devices with standard speed network connections, and with mab.js loading near the top of your page's `<head>` code, mab.js will successfully replace images and headlines before the 1-second timeout and reveal all hidden elements as soon as possible, eliminating user wait and flicker.

If implementing flicker control on your site, the code below should be added to your site's `<head>` code as one of the first scripts loaded in your site's `<head>` tags. It requires two customizations:

* It allows you to set the ​`headlineSelector` variable to target specific headline elements used in your homepage markup so we can hide all of your homepage headlines very briefly until the headline text is replaced by chartbeat\_mab.js​​ for positions with active tests.
* Similarly, if your site is not using `<article>` tags to encapsulate the headline, byline, and image, and instead uses custom tag(s) for this, you must set the `articleBlockSelector` variable to include all possible story selectors. Otherwise, this can be set to an empty string or commented out.&#x20;

The details will depend on how your HTML and CSS are structured, but fine-tuning ​these variables allows you to avoid headline and image flicker while allowing most of the page to render while our code replaces elements where there are active tests.

```markup
<style>
  .cb-it-hide {
    visibility: hidden;
  }
</style>

<script type="text/javascript">
  !function(){
    // Add customization below
    var headlineSelector = "h3, h2, h1";
    var articleBlockSelector = "article, section.item";
    var timeout = 1000;

    if(!("srcset"in document.createElement("img")))return;const e=window.chartbeatFlicker=window.chartbeatFlicker||{};function t(e,t,c,o){const n=document.createElement("style"),i=function(e,t){return e.split(",").reduce((e,c)=>e+c+` ${t} { visibility: hidden; }\n`,"")}(c,o);n.id=t,e.appendChild(n),n.type="text/css",n.appendChild(document.createTextNode(i))}window._sf_async_config=window._sf_async_config||{},e.timeout=timeout||1e3,e.headlineSelector=headlineSelector||"h3, h2, h1",window._sf_async_config.articleBlockSelector=articleBlockSelector||"article, section";const c=window._sf_async_config.articleBlockSelector;e.timeoutFlag=!1,e.strategyFetched=e.strategyFetched||!1;const o=document.head||document.querySelector("head");t(o,"chartbeat-flicker-control-style-ht",c,e.headlineSelector),t(o,"chartbeat-flicker-control-style-it",c,"img"),document.addEventListener("DOMContentLoaded",()=>{if(e.timeoutFlag){const e=document.getElementById("chartbeat-flicker-control-style-it");return void(e&&e.parentNode.removeChild(e))}document.querySelectorAll(c).forEach(function(e){Array.prototype.slice.call(e.querySelectorAll("img")).forEach(function(e){e.getAttribute("pinger-seen")||e.classList.add("cb-it-hide")})});const t=document.getElementById("chartbeat-flicker-control-style-it");t&&t.parentNode.removeChild(t)}),window.setTimeout(function(){if(!e.strategyFetched){e.timeoutFlag=!0;const t=document.getElementById("chartbeat-flicker-control-style-ht");t&&t.parentNode.removeChild(t)}document.querySelectorAll(c).forEach(function(e){Array.prototype.slice.call(e.querySelectorAll("img")).forEach(function(e){"true"!==e.getAttribute("keep-hiding")&&e.classList.remove("cb-it-hide")})});const t=document.getElementById("chartbeat-flicker-control-style-it");t&&t.parentNode.removeChild(t)},e.timeout)
  }();
</script>
```

{% hint style="success" %}
**Tip:** Remember to change`headlineSelector`and `articleBlockSelector`in the code above (lines 10 and 11) to CSS selectors that target only your page headlines and article block tags. Also, feel free to adjust the 1000 millisecond timeout (line 12) to allow more time for our JS to finish loading (only relevant for users of your site with slow network connections).
{% endhint %}

Here's what this looks like altogether, included as the first script after the opening \<head> tag, before the Chartbeat tracking snippet and call to load chartbeat\_mab.js (which should only be called *after* the page title and canonical URL elements):

```markup
<!doctype html>

<html lang="en">
<head>
  <style>
    .cb-it-hide {
		  visibility: hidden;
	  }
  </style>

  <script type="text/javascript">
	  !function () {
	  // Add customization below
    var headlineSelector = "h3, h2, h1";
    var articleBlockSelector = "article, section.item";
    var timeout = 1000;

    if (!("srcset" in document.createElement("img"))) return; const e = window.chartbeatFlicker = window.chartbeatFlicker || {}; function t(e, t, c, o) { const n = document.createElement("style"), i = function (e, t) { return e.split(",").reduce((e, c) => e + c + ` ${t} { visibility: hidden; }\n`, "") }(c, o); n.id = t, e.appendChild(n), n.type = "text/css", n.appendChild(document.createTextNode(i)) } window._sf_async_config = window._sf_async_config || {}, e.timeout = timeout, e.headlineSelector = headlineSelector, window._sf_async_config.articleBlockSelector = articleBlockSelector; const c = window._sf_async_config.articleBlockSelector; e.timeoutFlag = !1, e.strategyFetched = e.strategyFetched || !1; const o = document.head || document.querySelector("head"); t(o, "chartbeat-flicker-control-style-ht", c, e.headlineSelector), t(o, "chartbeat-flicker-control-style-it", c, "img"), document.addEventListener("DOMContentLoaded", () => { if (e.timeoutFlag) { const e = document.getElementById("chartbeat-flicker-control-style-it"); return void (e && e.parentNode.removeChild(e)) } const t = document.querySelectorAll(c); let o = []; t.forEach(function (e) { const t = Array.prototype.slice.call(e.querySelectorAll("img")); t.forEach(function (e) { e.getAttribute("pinger-seen") || e.classList.add("cb-it-hide") }), o = o.concat(t) }); const n = document.getElementById("chartbeat-flicker-control-style-it"); n && n.parentNode.removeChild(n) }), window.setTimeout(function () { if (!e.strategyFetched) { e.timeoutFlag = !0; const t = document.getElementById("chartbeat-flicker-control-style-ht"); t && t.parentNode.removeChild(t) } document.querySelectorAll(c).forEach(function (e) { Array.prototype.slice.call(e.querySelectorAll("img")).forEach(function (e) { "true" !== e.getAttribute("keep-hiding") && e.classList.remove("cb-it-hide") }) }) }, e.timeout)
  }();
  </script>
  <meta charset="utf-8">

  <!-- Page title -->
  <title>My Site's Homepage</title>
  <meta name="description" content="My Site's Homepage">

  <!-- Set canonical URL -->
  <link rel="canonical" href="https://mysite.com/article-xyz.html" />
  
  <!-- Load Chartbeat tracker and tester -->
  <script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.uid = #####; //CHANGE THIS
        _sf_async_config.domain = 'mysite.com'; //CHANGE THIS
        _sf_async_config.flickerControl = false;
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
  </script>
  <script async src="//static.chartbeat.com/js/chartbeat_mab.js"></script>

</head>

<body>

</body>
</html>
```

{% hint style="info" %}
**Note:** The headline flicker control configuration variable, `_sf_async_config.flickerControl`, should be set to `false` when using this implementation. The Image Testing flicker control code snippet handles all of the flicker control in the above code example.
{% endhint %}

### Option 2: Image and headline flicker control disabled

To implement image and headline testing without flicker control, simply use the default code snippet detailed in the first page of this guide with the `flickerControl` variable set to `false`. Here's what this looks like in our example homepage source code:

```markup
<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">

  <!-- Page title -->
  <title>My Site's Homepage</title>
  <meta name="description" content="My Site's Homepage">

  <!-- Set canonical URL -->
  <link rel="canonical" href="https://mysite.com/article-xyz.html" />
  
  <!-- Load Chartbeat tracker and tester -->
  <script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.uid = #####; //CHANGE THIS
        _sf_async_config.domain = 'mysite.com'; //CHANGE THIS
        _sf_async_config.flickerControl = false;
        _sf_async_config.articleBlockSelector = 'article'; //CHANGE THIS IF NEEDED
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
  </script>
  <script async src="//static.chartbeat.com/js/chartbeat_mab.js"></script>

</head>

<body>

</body>
</html>
```

## Next steps

The next article in this guide details resources requested by mab.js and the (very minimal) effect this script has on page load time if implemented as our documentation suggests with the `async` attribute in your site's header.&#x20;

For those ready to implement our code as detailed in the [**first page of this guide**](/cbp/feature-integrations/testing/homepage-testing-code), feel free to skip ahead to our [**integration QA instructions**](/cbp/feature-integrations/testing/qa-homepage-testing-integration) article to review your work.


# mab.js Specifications

Resources requested by mab.js and its effect on page load time.

## Resources requested by chartbeat\_mab.js

chartbeat\_mab.js requests the testing “strategy” for the page the visitor is on. The strategy contains the specs of the tests that are currently active — where the tests are (which article links), what the variant headlines are, and what the current play percentages are for each variant. chartbeat\_mab.js will request the strategy via asynchronous ​`XMLHttpRequest` as soon as it loads. The strategy itself is not user cacheable, because the play percentages for each variant in each test are frequently changing as we receive data and converge on a winner for each test.

We host our strategy files on the fastly CDN ([**click here**](https://www.fastly.com/network-map) for details on edge locations), and we actively push new changes to the strategy files on the CDN as tests update, so user requests should effectively never have a cache miss at the CDN edge. The size of the strategy depends on how many tests are running on the page, and the details of those tests — when no tests are running the strategy is about 150 bytes; each test will add about 500-1000 bytes.

The strategy file is requested from a URL with the following structure:&#x20;

`https://mab.chartbeat.com/mab_strategy/headline_testing/get_strategy/?host=example.com&domain=example.com&path=%2F`

For each test that is described in the strategy, there will be a single ping request (technically, a GET request for a 1x1 gif image) that is sent to our servers to indicate that the test was found (or not) and which variant was picked. These requests are not sent to our servers until the main ​chartbeat.js script loads and runs.

## Effect on page load time

We work to ensure the file is as small as possible but also fully self-contained without any additional dependencies. It is currently about 13Kb. It is served through our CDN network and is sent as a gzipped file. This ensures that the customer download time is as speedy as possible by reducing network latency and overhead.

We generally have a cache-header of between 2 to 24 hours on the response. We keep this low enough so that we can push out changes without having to do any complicated cache-busting when we need to make a scheduled change, but high enough so that most users would not be downloading the file more than once per visit to your site. As well, since all customers across the entire Chartbeat network access the same version of that file, it is quite possible that your website users will already have a cached version of the file.

## Image Testing specs

### Image Storage and Requests

Test images are stored on the fastly CDN, which is optimized to cache and serve test images in a low latency configuration ([**click here**](https://www.fastly.com/network-map) for details on edge locations).

### Image Testing flicker control code size

Our flicker control code is about 1.7Kb; however, because it is directly inserted into your page's HTML, it will not result in another network request. There is close to zero impact to page load time.

## Next steps

Review our [**integration QA instructions**](/cbp/feature-integrations/testing/qa-homepage-testing-integration) article for best practice advice to test your integration.


# Integration QA Steps: Headline and Image Testing

Integration QA guide for Headline and Image Testing.

## Testing your integration in staging or production

Your first headline and/or image tests can be initiated either on your staging site or production homepage(s). To test your integration in a QA/staging environment, the first step is to configure a staging Site on your Chartbeat account and set the `domain` variable in your stage-site Chartbeat code to the test Site ID. For example:

```javascript
_sf_async_config.domain = 'test.mysite.com';
```

Follow these instructions from our website tracking integration guide to [**configure a QA Site in Chartbeat**](https://docs.chartbeat.com/cbp/tracking/standard-websites/qa-web-integration#configure-qa-staging-sites-in-chartbeat) if you prefer to test your integration in staging and there is not already a test Site enabled for your account.

### Tips for running your first tests

#### How to generate HUD pins on your staging site homepage:

We require click-through data on homepage links in order to generate Heads Up Display pins needed to initiate a headline test, so this click-through needs to be simulated on staging sites that have zero organic traffic and clicks. If you click a headline link a few times to open a few new tabs to that article in your browser, this should generate enough click data to create a pin on your staging site. The target article page must also have our code installed with the domain variable assigned to the same test Site ID as the homepage. The Heads Up Display will not assign pins for links to other landing pages or to external domains, so begin your test on a link to an article page under your staging domain.

#### How to verify image/headline replacement:

Whether testing on a staging site or your production homepage, you will need to use an incognito window to simulate visiting your site as a user in order to be served a variant. You will need to continue to close and re-open your incognito window to your homepage until a variant is shown. If a variant is not shown after five or so new homepage loads in fresh incognito windows, there may be an error in your integration.

#### Advice for testing on your production website:

If your team chooses to initiate your first tests on your production site homepage, you should always use legitimate headlines/image variants that your team is comfortable having displayed for a subset of your homepage audience while testing the integration.

If there are errors in your integration, or you initiate a test in an incompatible position of your homepage, the effect on your visitor experience should be minimal if at all noticeable. In most cases, a non-working headline/image test results in your visitors seeing the original headline/image from your CMS as usual, so the failed test will be unnoticeable.

## Headline Testing *only* QA checklist: for sites that do not use our Image Testing add-on

* [ ] The following variables are initialized prior to chartbeat\_mab.js loading in the `<head>`: `domain`, `uid`, `flickerControl`, `useCanonical` and `useCanonicalDomain` OR `path`.
* [ ] `_sf_async_config.flickerControl` is set to `false` if your site does not make use of our Headline Testing flicker control code ([**option 1**](https://docs.chartbeat.com/cbp/feature-integrations/testing/flicker-and-flicker-control#option-1-headline-flicker-control-disabled)), or it is set to `true` if your site does make use of our Headline Testing flicker control code ([**option 2**](https://docs.chartbeat.com/cbp/feature-integrations/testing/flicker-and-flicker-control#option-2-headline-flicker-control-enabled)).

Initiate a few headline tests in different positions to verify that headlines are being replaced successfully by the Chartbeat code on your staging or production website. If this is your first time initiating a Chartbeat headline/image test, learn how in this [**Help Center article**](https://chartbeat.zendesk.com/hc/en-us/articles/360023929453-Guide-to-Headline-Testing#h_3a79d67b-fbe2-4287-b2aa-3ea49b06e478). Before getting started with your first test, review our tips for QA'ing your integration at the top of this page.

## Image Testing QA checklist: for sites that use Headline *and* Image Testing

### Reviewing your code

The Chartbeat code on your site should include the following variables:

* [ ] `_sf_async_config.articleBlockSelector`: Must be set in the Chartbeat snippet only if your site is not using \<article> tags to encapsulate all headline and image \<a> tag(s).
* [ ] `_sf_async_config.lazyLoadImageAttributes`: Must be set in the Chartbeat snippet only if your site makes use of custom lazy load attributes for your images outside of `data-src` and `data-srcset` which our code will detect automatically.
* [ ] `_sf_async_config.domain`: For testing on a staging site, this value should be set to the Site ID the staging site dashboard that has been created on your account. This is typically the root domain of the staging site.
* [ ] `_sf_async_config.flickerControl`: If you are implementing flicker control for headline and image testing, this variable must set to false and must be loaded in the code snippet prior to the chartbeat\_mab.js script.

If you are planning to test above the fold, flicker control code must be inserted in the `<head>`:

* [ ] For optimal performance, the flicker control code should be loaded early in the page's head code.
* [ ] There are two variables that must be set in the flicker control code: `headlineSelector` and `articleBlockSelector`.&#x20;
* [ ] A third variable, `timeout`, can be modified to allow more time for the Chartbeat Javascript to finish loading.

### Running headline and image tests

Once your test environment is setup with Chartbeat Image Testing code and the corresponding configuration variables, we highly recommend starting image tests on multiple areas of your homepage to determine compatibility with your page markup. Tests should be started in multiple homepage areas above and below the fold, and should include both a **variant image and headline**. With tests running, you will need to load the test homepage in a Chrome Incognito window until the variant image and headline is served to verify successful replacement. Repeat for all areas where you anticipate your homepage team running headline and image tests.

If this is your first time initiating a Chartbeat headline/image test, learn how in this [**Help Center article**](https://chartbeat.zendesk.com/hc/en-us/articles/360023929453-Guide-to-Headline-Testing#h_3a79d67b-fbe2-4287-b2aa-3ea49b06e478). Before getting started with your first test, review our tips for QA'ing your integration at the top of this page.

* [ ] Tests should be initiated in different positions on your homepage to determine image test compatibility with each position's unique HTML markup format.
* [ ] Tests should be started with a single test variant (variant B): a different headline *and* a different image than the original (variant A). The headline and image should be easily distinguishable.
* [ ] Once served a variant, the browser should be updated to [**simulate a mobile device**](https://developers.google.com/web/tools/chrome-devtools/device-mode). After simulating a mobile device and refreshing the page, you should confirm that the variant image and headline are still being shown.
* [ ] In all tests, the variant headline should always be shown with its corresponding variant image. You should never see a scenario where the variant headline is shown with the original image.

A headline and image test should be started above the fold to evaluate the flicker control implementation:

* Images and headlines should be hidden until either chartbeat\_mab.js loads or the timeout, 1-second by default, elapses.&#x20;
* If the timeout elapses, the original headline and image should appear to the user and a trial should not be recorded.
* If the timeout does not elapse and chartbeat\_mab.js loads, the user will be served either the original headline and image or a variant. In either case, a trial should be recorded.&#x20;
* When images and headlines are eventually shown, users should not see any flicker effect.

A headline and image test should be started below the fold to evaluate the lazy load implementation:

* The variant headline should always be shown with its corresponding variant image. You should never see a scenario where a variant headline is shown with the original image.

## Use DevTools to review Chartbeat requests from your site

You can use your browser's site inspector tools to monitor network activity from your site, filtering on "chartbeat" to view all Chartbeat related requests made from your page.

Filtered on "chartbeat" in the Network tab of DevTools, here's the expected order of requests from a standard implementation of our snippet with mab.js included:

1. chartbeat\_mab.js
2. chartbeat.js (or chartbeat\_video.js)
3. mab.chartbeat.com/mab\_strategy/headline\_testing/get\_strategy/
4. mabping.chartbeat.net/ping/mab
5. ping.chartbeat.net/ping (repeated every 15 seconds)

## Up next

#### Choose your next integration.

* [Standard Website Tracking](/cbp/tracking/standard-websites)
* [Google AMP Tracking](/cbp/tracking/google-amp)
* [Mobile App SDKs](/cbp/tracking/mobile-app-sdks)
* ~~**Headline and Image Testing**~~**&#x20;✅**
* [Video Tracking ](/cbp/feature-integrations/video-engagement)&#x20;


# Video Engagement

A guide to integrating Chartbeat Video engagement tracking for your website.

## Articles included in this guide:

1. [Adding chartbeat\_video.js to Your Site](/cbp/feature-integrations/video-engagement/adding-videojs-to-your-site)
2. [Supported OVP Integrations](/cbp/feature-integrations/video-engagement/supported-ovp-integrations)
3. [Custom Player Integration SDK](/cbp/feature-integrations/video-engagement/custom-player-integration-sdk)
4. [Configure Video Tracking Settings](/cbp/feature-integrations/video-engagement/video-configuration-variables)
5. [Integration QA: Video Tracking](/cbp/feature-integrations/video-engagement/integration-qa-steps-video-tracking)

{% hint style="warning" %}
Chartbeat's Video suite is an add-on feature that is not included within all customer plans. If your team would like to add this feature to your Chartbeat software subscription, please reach out to your Customer Success Manager or to the Technical Solutions team at [**support@chartbeat.com**](mailto:support@chartbeat.com).
{% endhint %}


# Adding chartbeat\_video.js to Your Site

An introduction to our video engagement tracking code for real-time video analytics for your website. Note that this is an add-on feature, not included in all Chartbeat service plans.

## Implementation requirements: Video

Video Engagement tracking is an add-on feature, designed to supplement our website engagement analytics with video analytics to help your teams understand who’s watching your videos, which ones are keeping audience attention, and what you can do to pair articles and videos to boost engagement and consistently grow your viewership.

So the first requirement is to integrate our standard webpage engagement tracking for your site *in addition* to integrating Chartbeat with the main video player on your site because it's not possible to use our Video Dashboard as a standalone product. If you haven't already, see our [**integration guide for standard websites**](/cbp/tracking/standard-websites) to understand our basic tracking code first, then make adjustments to that code based on the instructions found in this guide to integrate with your chosen web player.

The second primary requirement is to load our tracker with support for video analytics, chartbeat\_video.js, instead of our standard tracker, chartbeat.js. chartbeat\_video.js contains all of the same functionality as our standard web analytics script, chartbeat.js, but it also adds support for video tracking. So in most cases, chartbeat\_video.js should *replace* chartbeat.js in the code snippet detailed in our standard website integration guide instead of loading both scripts on your site. Only if your video player is loaded in an iframe will both trackers will need to be used in your integration, detailed later in this guide.

Here's what our standard web tracking snippet looks like with chartbeat.js replaced with chartbeat\_video.js to add support for video engagement tracking (see line 18):

```markup
<script type='text/javascript'>
    (function() {
        /** CONFIGURATION START **/
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        _sf_async_config.uid = #####; //CHANGE THIS TO YOUR ACCOUNT ID
        _sf_async_config.domain = 'domain.com'; //CHANGE THIS TO YOUR SITE ID
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //SET PAGE SECTION(S)
        _sf_async_config.authors = ''; //SET PAGE AUTHOR(S)
        _sf_async_config.autoDetect = false;
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat_video.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
     })();
</script>
```

{% hint style="info" %}
A third requirement is to use our additional iframe configuration, [**found here**](https://docs.chartbeat.com/cbp/feature-integrations/video-engagement/video-configuration-variables#track-videos-in-an-iframe), for videos embedded within iframes.
{% endhint %}

## Supported OVPs vs custom player integration

The next step to integrate Chartbeat tracking for your player is to determine whether your website player platform for proprietary content is a third-party OVP (Online Video Platform) that our team has already created a plug-and-play integration for, or whether your player will require its own custom integration by your video developer team using our custom video SDK. Our list of supported players is as follows:

* Brightcove
* Flowplayer
* JW Player
* Kaltura
* Ooyala
* YouTube

Note that for some of these third-party web player platforms, compatibility of our integration is only guaranteed for certain player versions, detailed in the next page of this guide where you will find integration instructions for each player platform above.

## Limitations

Currently, Chartbeat Video tracking is only supported for our standard website tracking integration and native apps. Though we offer unique visitor tracking integrations for other platforms like [**AMP**](https://docs.chartbeat.com/cbp/tracking/google-amp) , we cannot provide video engagement analytics for those separate platform integrations at this time. If you are a current customer and this functionality is important to your team, we'd like to hear from you! Shoot us a note at [**feedback@chartbeat.com**](mailto:feedback@chartbeat.com).&#x20;

Video tracking for our [Android](/cbp/tracking/mobile-app-sdks/android-sdk/video-tracking), [iOS](/cbp/tracking/mobile-app-sdks/ios-sdk/video-tracking) and [React Native](/cbp/tracking/mobile-app-sdks/react-native-sdk/video-tracking) SDKs is documented in the corresponding [Mobile App SDK](/cbp/tracking/mobile-app-sdks) guides.

## Next steps

If your organization uses one of the player platforms listed above to host owned video content on your website, find additional integration instructions for the player (as well as player version compatibility details) on the next article of this guide.&#x20;

If your organization uses a player not included in our list of supported OVPs above, skip ahead to learn about our [**custom player integration SDK**](/cbp/feature-integrations/video-engagement/custom-player-integration-sdk).


# Supported OVP Integrations

Third-party video player platforms with plug-and-play integrations currently supported by Chartbeat.

The below instructions refer to Chartbeat code already installed on your website (see our [**tracking for standard websites guide**](/cbp/tracking/standard-websites)) because our Video engagement tracking add-on integration is frequently made by existing customers who are upgrading their Chartbeat service. If your team has not yet implemented our standard tracking snippet code for websites and instead plans to enable website and video player tracking simultaneously, simply apply any code *adjustment* recommendations found below to the new Chartbeat code template for your site.

## **Brightcove**

{% hint style="info" %}
**Note:** These instructions are for the New Brightcove Player available for the [**Video Cloud**](https://support.brightcove.com/en/video-cloud) or [**Perform**](http://support.brightcove.com/en/perform) products.
{% endhint %}

To start tracking Brightcove Player videos, you’ll need to add the Chartbeat Video Pinger in your Brightcove Console.

#### Implementation Instructions

1. Across all pages on your site, change the `chartbeat.js` script currently being run to `chartbeat_video.js`
2. In the Brightcove VideoCloud console for the player you're looking to track, navigate to the **Plugins** module.
3. In the **JavaScript** section, add the Chartbeat plugin URL:

   ```
   https://static.chartbeat.com/js/chartbeat_brightcove_plugin.js
   ```
4. In the **Name,Options(JSON)** section, enter the name **"chartbeat"** (all lower-case) for the **Name**.
5. Save and republish the player.
6. Repeat the above steps for all players you want to track.

## Flowplayer

{% hint style="info" %}
**Note**: Support is for Flowplayer v.5.4.3+ only. Flowplayer Flash is unsupported. Please see instructions for setting up a custom integration for Flowplayer Flash compatibility.
{% endhint %}

If you're using Flowplayer as your video platform, you'll need to include the "title" and "poster" attributes, and change the Chartbeat Pinger to the Chartbeat Video Pinger.

#### Implementation Instructions

1. **Include the "title" and "poster" attributes in the HTML/DOM**

   These attributes populate the title and thumbnail images in the Chartbeat Video Dashboard. The "title" attribute must be added to the parent \<div>, while the "poster" attribute can be in either the \<video> tag or the parent \<div>. So a sample video tag might look like the following:

   ```
   <div data-swf='//releases.flowplayer.org/5.4.3/flowplayer.swf' class='flowplayer no-toggle' data-ratio='0.416' data-engine='flash' title='Video Title'>
       <video poster='http://yoursite.com/thumbnail.jpg'>
           <source src='http://yoursite.com/videos/movie.mp4' type='video/ mp4'>
       </video>
   </div>
   ```

   The other option looks like the following:

   ```
   <div data-swf='//releases.flowplayer.org/5.4.3/flowplayer.swf' class='flowplayer no-toggle' data-ratio='0.416' data-engine='flash' title='Video Title' poster='http://yoursite.com/thumbnail.jpg'>
       <video>
           <source src='http://yoursite.com/videos/video.mp4' type='video/mp4'>
       </video>
   </div>
   ```
2. **Change `chartbeat.js` to `chartbeat_video.js`**

   In order to use the Video Pinger, you must modify the Chartbeat code already on your site. You'll want to switch the Chartbeat code to load `chartbeat_video.js` instead of `chartbeat.js`. The `chartbeat_video.js` file includes both Publishing and Video tracking capabilities.

   ```
   e.setAttribute('src', '//static.chartbeat.com/js/chartbeat_video.js');
   ```

#### Checking your Work

Your complete Chartbeat code will look something like this:

```markup
<script type='text/javascript'>
    (function() {
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        /** CONFIGURATION START **/
        _sf_async_config.uid = YOUR_ACCOUNT_ID;
        _sf_async_config.domain = 'yoursite.com';
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat_video.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
</script>
```

## HTML5

If you use the HTML5 video element to embed your videos, you'll need to add "title" and "poster" attributes to the HTML/DOM, and change the Chartbeat Pinger to the Chartbeat Video Pinger.

#### Implementation Instructions

1. **Include the "title" and "poster" attributes in the HTML/DOM**

   These attributes populate the title and thumbnail images in the Chartbeat Video Dashboard. You'll need to add the "title" and "poster" attributes to the \<video> tag. For example:

   ```
   <video src='yourmovie.mp4' title='Video Title' poster='http://yoursite.com/thumbnail.jpg'>
   ```
2. **Change `chartbeat.js` to `chartbeat_video.js`**

   In order to use the Video Pinger, you must modify the Chartbeat code already on your site. You'll want to switch the Chartbeat code to load `chartbeat_video.js` instead of `chartbeat.js`. The `chartbeat_video.js` file includes both Publishing and Video tracking capabilities.

   ```
   e.setAttribute('src', '//static.chartbeat.com/js/chartbeat_video.js');
   ```

#### Checking your Work

Your complete Chartbeat code will look something like this:

```markup
<script type='text/javascript'>
    (function() {
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        /** CONFIGURATION START **/
        _sf_async_config.uid = YOUR_ACCOUNT_ID;
        _sf_async_config.domain = 'yoursite.com';
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat_video.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
</script>
```

## JW Player

{% hint style="info" %}
**Note:** Support is for v5, v6, v7, and v8 only.
{% endhint %}

If you're using JW Player as your video platform, you'll need to change the Chartbeat Pinger to the Chartbeat Video Pinger and include Title and Thumbnail attributes in your code.

#### Implementation Instructions

1. **Include the JW Player attributes "title" and "image" in each video**

   This will populate the titles and thumbnails in your Video Dashboard. Your code should look similar to:

   ```
   jwplayer("myElement").setup({
       file: "http://www.yoursite.com/video/movie.mp4",
       title: "JW Video Example"
       image: "http://www.yoursite.com/image/thumbnail.jpg"
   });
   ```
2. **Change `chartbeat.js` to `chartbeat_video.js`**

   In order to use the Video Pinger, you must modify the Chartbeat code already on your site. You'll want to switch the Chartbeat code to load `chartbeat_video.js` instead of `chartbeat.js`. The `chartbeat_video.js` file includes both Publishing and Video tracking capabilities.

   ```
   e.setAttribute('src', '//static.chartbeat.com/js/chartbeat_video.js');
   ```

**Additional Notes**

If you load your videos after the page has loaded (e.g. video only loads when someone presses the play button), you'll need to manually push the video object to Chartbeat to start tracking. Just call the lines below immediately after your video loads:

```javascript
window._cbv = window._cbv || [];
window._cbv.push(jwplayer);
```

#### Checking your Work

Your complete Chartbeat code will look something like this:

```markup
<script type='text/javascript'>
    (function() {
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        /** CONFIGURATION START **/
        _sf_async_config.uid = YOUR_ACCOUNT_ID;
        _sf_async_config.domain = 'yoursite.com';
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat_video.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
</script>
```

## Kaltura

{% hint style="info" %}
**Note:** Support is for Player v2+.
{% endhint %}

#### Implementation Instructions

1. **Change `chartbeat.js` to `chartbeat_video.js`**

   In order to use the Video Pinger, you must modify the Chartbeat code already on your site. You'll want to switch the Chartbeat code to load `chartbeat_video.js` instead of `chartbeat.js`. The `chartbeat_video.js` file includes both Publishing and Video tracking capabilities.

   ```
   e.setAttribute('src', '//static.chartbeat.com/js/chartbeat_video.js');
   ```

#### Checking your Work

Your complete Chartbeat code will look something like this:

```markup
<script type='text/javascript'>
    (function() {
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        /** CONFIGURATION START **/
        _sf_async_config.uid = YOUR_ACCOUNT_ID;
        _sf_async_config.domain = 'yoursite.com';
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat_video.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
</script>
```

## Ooyala

{% hint style="info" %}
**Note**: Support is for v3+ and v2+.
{% endhint %}

If you're using Ooyala as your video platform, you'll just need to modify the Ooyala embed code and the existing Chartbeat code.

#### Implementation Instructions

1. **Modify the Ooyala embed code**

   The standard embed code for Ooyala does not expose the player to the Chartbeat pinger, so in order to start tracking video performance, you just need two lines of code inside the 'onCreate' event provided by Ooyala. Find the following code:

   ```
   OO.ready(function() {
       OO.Player.create('element_id', 'account_id', {
           onCreate: function(player) {
               var _cbv = window._cbv || (window._cbv = []);
               _cbv.push(player);
           }
       });
   });
   ```
2. **Modify the Chartbeat code**

   Within the current Chartbeat code on the page, you’ll need to make two adjustments.

   **Part A:** Add the video type line to the configuration section

   ```
   _sf_async_config.videoType = 'ooyala';
   ```

   **Part B:** Change `chartbeat.js` to `chartbeat_video.js`

   In order to use the Video Pinger, you must modify the Chartbeat code already on your site. You'll want to switch the Chartbeat code to load `chartbeat_video.js` instead of `chartbeat.js`. The `chartbeat_video.js` file includes both Publishing and Video tracking capabilities.

   ```
   e.setAttribute('src', '//static.chartbeat.com/js/chartbeat_video.js');
   ```

   **Part C:** Add `_sf_async_config.autoDetect=false;` to your `_sf_async_config` object setup.

   ```
   _sf_async_config.autoDetect = false;
   ```

## YouTube

To start tracking YouTube Videos (using the new \<iframe> embed style that supports both Flash and HTML5 videos), you'll need to use the YouTube API and modify the current Chartbeat code.

#### Implementation Instructions

1. **Link to the YouTube JavaScript API**

   Add this script, which grants access to the YouTube API, to the \<head> section for any page with video. This must be inserted before the chartbeat\_video.js is included.

   ```
   <script type='text/javascript' src='//www.youtube.com/iframe_api'></script>
   ```
2. **Enable JavaScript API access to your YouTube embeds**

   Chartbeat’s code needs to communicate with the YouTube videos to track them, so you’ll need to enable JavaScript access. This is easily done by adding a single query parameter (“enablejsapi=1”) to your YouTube embed URL. For example:

   ```
   <iframe width="420" height="315" src=“//www.youtube.com/embed/_OBlgSz8sSM?enablejsapi=1" frameborder="0" allowfullscreen></iframe>
   ```

   Note that in the above code uses a single query parameter. If your URL already has other options set, you can add the enablejsapi parameter after the others by using an ampersand ("&") as a separator. For example:

   ```
   <iframe width="420" height="315" src=“//www.youtube.com/embed/_OBlgSz8sSM?autohide=1&enablejsapi=1" frameborder="0" allowfullscreen></iframe>
   ```
3. Push the YouTube Video to Chartbeat and Update the Pinger

   Within the current Chartbeat code on the page, you’ll need to make two adjustments.

   **Part A:** Change `chartbeat.js` to `chartbeat_video.js`

   In order to use the Video Pinger, you must modify the Chartbeat code already on your site. You’ll want to switch the Chartbeat code to load `chartbeat_video.js` instead of `chartbeat.js`. The `chartbeat_video.js` file includes both Publishing and Video tracking capabilities.

   ```
   e.setAttribute('src', '//static.chartbeat.com/js/chartbeat_video.js');
   ```

   **Part B:** Add these lines to tell Chartbeat to autodetect the YouTube iframes

   ```
   var _cbv = window._cbv || (window._cbv = []);_cbv.push(['autoDetectYouTubeIframes']);
   ```

#### Checking your Work

Your complete Chartbeat code will look something like this:

```markup
<script type='text/javascript'>
  (function() {
      var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
      /** CONFIGURATION START **/
      _sf_async_config.uid = CHARTBEAT_ACCOUNT_ID;
      _sf_async_config.domain = 'yoursite.com';
      _sf_async_config.useCanonical = true;
      _sf_async_config.useCanonicalDomain = true;
      _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
      _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
      /** CONFIGURATION END **/
      function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat_video.js';
            n.parentNode.insertBefore(e, n);
          }
          /** TELL CHARTBEAT TO AUTODETECT YOUTUBE VIDEOS **/
      var _cbv = window._cbv || (window._cbv = []);
      _cbv.push(['autoDetectYouTubeIframes']);
      loadChartbeat();
  })();
</script>
```

#### Special Instructions

* If the YouTube video embed is not present on the page load or is loaded later on a later event, you need to push the video object (event.target, in this case) to \_cbv once a reference to the iframe is available:.

  ```
  // Once we have a reference to the YouTube video's iframe element...
          new window.YT.Player(iframe, {
            'events': {'onReady': function(event) {
            window._cbv.push(event.target);
            }}
          });
  ```
* If you are declaring custom events in event listeners on your YouTube Player video objects, you can preserve your events in those listeners by performing the \_cbv.push in the onPlayerReady function of your video object constructor, along with other code you want to fire on that listener:

  ```
  function onPlayerReady(event) {
          var window._cbv = window._cbv || (window._cbv = []);
          _cbv.push(event.target);
          event.target.playVideo();
        }
  ```

## Next steps

If your team uses a web player not listed in our plug-and-play integration list above, the next article in this guide details instructions for using our custom player SDK to build your own player integration. If you've already found the install steps for your supported player platform in the list above, skip ahead to [**configure video tracking settings**](/cbp/feature-integrations/video-engagement/video-configuration-variables)**.**


# Custom Player Integration SDK

Manually integrate your video player with our custom player SDK.

If your video player is not listed among our [**Supported OVPs**](/cbp/feature-integrations/video-engagement/supported-ovp-integrations), you may use our Video SDK to create a custom integration. The primary task is to define a JavaScript Class - referred to as a **strategy** - which interfaces with your native video API to return values measured by the Chartbeat Publishing Video platform. These include values such as video state (playing, stopped, completed, etc.), video duration, video thumbnail, playhead position, and so on.

{% file src="/files/-LnO-Mamj-BN4nwNTZkp" %}
Download the Chartbeat Video SDK (JavaScript)
{% endfile %}

## Included files in the SDK

* **simplified\_strategy.js** is a template for creating a strategy class with no external dependencies. The comments in the code explains what each of the methods in the strategy class need to do.
* **strategy\_interface.js** is a [**Google Closure**](https://developers.google.com/closure/) implementation of the strategy interface. If you’re using Google Closure, it defines all the required methods for you. Using Google Closure is not a requirement if you use the `simplified_strategy.js` as a basis for your object.
* **strategy\_html\_player.js** is an example file showing how Chartbeat implemented the video strategy constructor for HTML5 video players.

## Implementation Instructions

### **Step 1: Push your video strategy to \_cbv\_strategies**

On each page with embedded video, you will need to push your video strategy constructor—as customized in `strategy_interface.js` — so that Chartbeat's pinger can use it to watch for video events. For the constructor, “CUSTOM\_STRATEGY\_CONSTRUCTOR”, you would push the constructor like this:

```javascript
window['_cbv_strategies'] = window['_cbv_strategies'] || [];
window['_cbv_strategies'].push(CUSTOM_STRATEGY_CONSTRUCTOR_NAME);
```

{% hint style="info" %}
**Note**: Make sure to push your video strategy constructor to `_cbv_strategies` synchronously, before Chartbeat's pinger (`chartbeat_video.js`) loads.
{% endhint %}

### **Step 2: Push your video object to \_cbv**

Whenever a new video player loads on the page, push the video object to \_cbv. This should come after you’ve pushed your video strategy constructor, as in Step 1. For each individual video player, push the video object as below:

```javascript
var _cbv = window._cbv || (window._cbv = []); _cbv.push(player);
```

Make sure to do this for each player that loads. So if a single page has two separate video players, you’ll need to push twice. However, if the same video player switches to a new video, you do not need to make this push, as the strategy interface should take care of new video loads.

### **Step 3: Change chartbeat.js to chartbeat\_video.js**

In order to use the Video Pinger, you must modify the Chartbeat code already on your site. You'll want to switch the Chartbeat code to load `chartbeat_video.js` instead of `chartbeat.js`. The `chartbeat_video.js` file includes both Publishing and Video tracking capabilities.

## Checking your work

Your complete Chartbeat code will look something like this:

```markup
<script type='text/javascript'>
    (function() {
        window['_cbv_strategies'] = window['_cbv_strategies'] || [];
        window['_cbv_strategies'].push(MyStrategy);
        var _cbv = window._cbv || (window._cbv = []);
        _cbv.push(MyPlayer);
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        /** CONFIGURATION START **/
        _sf_async_config.uid = YOUR_ACCOUNT_ID;
        _sf_async_config.domain = 'yoursite.com ';
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = 'section1,section2 ';
        _sf_async_config.authors = 'author1,author2 ';
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat_video.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
</script>
```

## Next steps

Review our JavaScript configuration variables and add them to the Chartbeat code snippet for your website if they apply to your video player implementation.


# Configure Video Tracking Settings

Use our JavaScript configuration variables to fine-tune your video tracking settings.

Similar to the configuration variables listed in our [**integration guide for standard web tracking**](/cbp/tracking/standard-websites/configuration-variables), review these variables to determine if they are needed for your integration.

## Disable auto-tracking of all video players

This variable can be set to false to prevent our video tracker from automatically tracking all `<video>` elements on a page. We recommend this setting for all video tracking integrations where your [**player object is pushed to \_cbv**](/cbp/feature-integrations/video-engagement/custom-player-integration-sdk#step-2-push-your-video-object-to-_cbv) in order to prevent additional video elements (ad units for example) from being detected and tracked automatically by chartbeat\_video.js.

| Field Name   | Value Type | Default Value | Max Length | Example Value |
| ------------ | ---------- | ------------- | ---------- | ------------- |
| `autoDetect` | boolean    | `true`        | *N/A*      | `false`       |

Example usage:

```javascript
_sf_async_config.autoDetect = false;
```

## Set video type to Ooyala

Use this variable **only** if your implementation makes use of our plug-and-play [**Ooyala integration**](/cbp/feature-integrations/video-engagement/supported-ovp-integrations#ooyala).

| Field Name  | Value Type | Default Value | Max Length | Example Value |
| ----------- | ---------- | ------------- | ---------- | ------------- |
| `videoType` | text       | *None*        | *None*     | `ooyala`      |

Example usage:

```javascript
_sf_async_config.videoType = 'ooyala';
```

## Define video sections

Use this variable **only** if your team prefers to pass a page's section(s) data to the Video Dashboard as the section values associated for videos on that page. This variable is not recommended for most integrations.

| Field Name        | Value Type | Default Value | Max Length | Example Value |
| ----------------- | ---------- | ------------- | ---------- | ------------- |
| `videoPageGroups` | boolean    | `false`       | *N/A*      | `true`        |

Example usage:

```javascript
_sf_async_config.videoPageGroups = true;
```

## Track videos in an iframe

This variable is required for our video tracker to properly identify videos embedded within iframes. It should be assigned to the full domain name, including subdomain, of the site your iframe embedded video file is hosted on.

| Field Name     | Value Type | Default Value | Max Length | Example Value     |
| -------------- | ---------- | ------------- | ---------- | ----------------- |
| `playerdomain` | text       | *None*        | *None*     | `player.site.com` |

Example usage:

```javascript
_sf_async_config.playerdomain = 'player.site.com';
```

### Additional iframe instructions

If you deploy your video player in an iframe on your website pages, you will need to (A) load chartbeat\_video.js within the iframe, and (B) load chartbeat.js on the parent page with the playerdomain variable set to the full subdomain of your iframe embedded video content.

#### Step 1: Add chartbeat\_video.js in the iframe

In the iframe content page where the player loads, include the Chartbeat Video tracker like so:

```markup
<script type='text/javascript'>
    (function() {
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        /** CONFIGURATION START **/
        _sf_async_config.uid = YOUR_ACCOUNT_ID;
        _sf_async_config.domain = 'yoursite.com';
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat_video.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
</script>
```

#### Step 2: Set the playerdomain variable

On the main page—not within the iframe—add the following line into the existing Chartbeat code configuration section:

```javascript
_sf_async_config.playerdomain = '<player domain>';
```

As an example: Let's say you have a video link on your homepage, and that link opens an iframe with a video player in it. Also, let's say that video player is hosted on a subdomain, media.yoursite.com. Your Chartbeat Publishing code on the homepage must then include the following line:

```javascript
_sf_async_config.playerdomain = 'media.yoursite.com';
```

#### Checking your Work

Your Chartbeat code within the iframe should look like:

```markup
<script type='text/javascript'>
    (function() {
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        /** CONFIGURATION START **/
        _sf_async_config.uid = YOUR_ACCOUNT_ID;
        _sf_async_config.domain = 'yoursite.com';
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat_video.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
</script>
```

The Chartbeat code on the parent page (outside the iframe) should look like:

```markup
<script type='text/javascript'>
    (function() {
        var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
        /** CONFIGURATION START **/
        _sf_async_config.uid = YOUR_ACCOUNT_ID;
        _sf_async_config.domain = 'yoursite.com';
        _sf_async_config.useCanonical = true;
        _sf_async_config.useCanonicalDomain = true;
        _sf_async_config.sections = ''; //CHANGE THIS TO YOUR SECTION NAME(s)
        _sf_async_config.authors = ''; //CHANGE THIS TO YOUR AUTHOR NAME(s)
        _sf_async_config.playerdomain = 'media.yoursite.com'; //CHANGE THIS TO YOUR DOMAIN
        /** CONFIGURATION END **/
        function loadChartbeat() {
            var e = document.createElement('script');
            var n = document.getElementsByTagName('script')[0];
            e.type = 'text/javascript';
            e.async = true;
            e.src = '//static.chartbeat.com/js/chartbeat.js';
            n.parentNode.insertBefore(e, n);
        }
        loadChartbeat();
    })();
</script>
```

## Next steps

Review our [**integration QA instructions**](/cbp/feature-integrations/video-engagement/integration-qa-steps-video-tracking) article for best practice advice to test your integration.


# Integration QA: Video Tracking

Integration QA guide for Video Engagement tracking.

## Check your Video Dashboard

After publishing your integration, your site's Video Dashboard should begin to populate with engagement data as users interact with your tracked player. To check out your Video Dashboard:

1. [**Sign in**](https://chartbeat.zendesk.com/hc/en-us/articles/360017718093) to your Chartbeat account.
2. From the [**Sites page**](https://chartbeat.com/publishing/sites/), find the site you've recently integrated video tracking for and hover over the site entry to reveal the 'Video' button.

{% hint style="success" %}
**Tip:** If you do not have a Chartbeat user account, request access from one of your account admins.
{% endhint %}

Here are some useful QA steps to ensure proper player tracking:

* [ ] Video titles and thumbnail images are populating as expected
* [ ] No duplicate video content entries appear in your list of top videos (left side of the dashboard)
* [ ] Video duration appears accurately in minutes and seconds in your top videos list (appears in the bottom-right corner of each video thumbnail)
* [ ] Starts Today and Average Eng. Time metrics are populating with data shortly after your integration is published
* [ ] The 'Top Pages Without Video' list should only display pages of your site where Chartbeat is not registering zero player engagement activity (indicating that no video exists on those pages)

## Up next

#### Choose your next integration.

* [Standard Website Tracking](/cbp/tracking/standard-websites)
* [Google AMP Tracking](/cbp/tracking/google-amp)
* [Mobile App SDKs](/cbp/tracking/mobile-app-sdks)
* [Headline and Image Testing](/cbp/feature-integrations/testing)
* ~~**Video Tracking**~~**&#x20; ✅**


# Conversion

A guide to integrating Chartbeat Conversion to track conversion flows

## Articles included in this guide:

1. [Adding subscriptions.js to your Site](/cbp/feature-integrations/conversion/adding-subscriptions.js-to-your-site)
2. [Conversion Events](/cbp/feature-integrations/conversion/conversion-events)
3. [Integration QA Steps: Conversion](/cbp/feature-integrations/conversion/integration-qa-steps-conversion)
4. [Supported Conversion Flows](/cbp/feature-integrations/conversion/supported-conversion-flows)

{% hint style="warning" %}
Chartbeat's Conversion suite is an add-on feature that is not included within all customer plans. If your team would like to add this feature to your Chartbeat software subscription, please reach out to your Customer Success Manager or to the Technical Solutions team at [**support@chartbeat.com**](mailto:support@chartbeat.com).
{% endhint %}


# Adding subscriptions.js to Your Site

An introduction to our code snippet for tracking conversions on your site. Note that this is an add-on feature, not included in all Chartbeat service plans.

## Implementation requirements

Conversion is supported for websites using our tracking library (chartbeat.js or chartbeat\_video.js), covered in the [**website tracking guide**](/cbp/tracking/standard-websites/our-javascript). Customers planning to use our Conversion add-on should review our [Supported Conversion Flows](/cbp/feature-integrations/conversion/supported-conversion-flows) to determine whether or not Conversion is compatible with your conversion flow.&#x20;

Our subscriptions.js library is responsible for Chartbeat's main conversion tracking functionality on your sites. With subscriptions.js, you can track a user’s journey starting at a paywalled article and continuing through the moment they subscribe. Editorial, product, and subscription teams will be able to use the tool from our [Conversion Dashboard](https://help.chartbeat.com/hc/en-us/articles/15963582634267-Guide-to-Conversion) and see data in the [Real-Time Dashboard](https://help.chartbeat.com/hc/en-us/articles/15964458639131) and [Heads-Up Display](broken://spaces/Sp6UCjBseVB5tf81JqD7/pages/DMx8Hlp8ejQRv9Sa8t8Z). You can also access your top converting articles via the [Conversion API](/cbp/api/conversion-api).\
\
Once subscriptions.js is loading on your site, you'll need to fire [conversion events](/cbp/feature-integrations/conversion/conversion-events) through the global `chartbeat()` function to begin tracking your conversion flows and send key event properties to Chartbeat.&#x20;

There are two requirements for loading subscriptions.js on your site to begin tracking conversions.

#### First requirement

Our conversion snippet should be loaded in the ​`<head>` , after chartbeat.js, on all pages including articles and conversion flows.&#x20;

```markup
<!-- Chartbeat Conversion -->
<script type="text/javascript">
	(function(w,d,s,c,u){
		var qf=function(e,a){(w[c].q=w[c].q||[]).push([e,a]);};w[c]=w[c]||qf;
		var st=d.createElement(s);
		var fs=d.getElementsByTagName(s)[0];
		st.async=1;st.src=u;fs.parentNode.insertBefore(st,fs);
	})(window,document,'script','chartbeat','https://static.chartbeat.com/js/subscriptions.js');
</script>
<!-- End Chartbeat Conversion -->
```

{% hint style="info" %}
The conversion snippet must be loaded after chartbeat.js
{% endhint %}

#### Second requirement

Call the global `chartbeat()` function to track conversion events when they occur with optional parameters. Events fired after the subscriptions.js script loads will be tracked, and any fired before will not be tracked by Chartbeat.

See our [Conversion Events](/cbp/feature-integrations/conversion/conversion-events) to find the full list of the event methods to pass to the `chartbeat()` function.

```javascript
chartbeat('event_name', { // CHANGE THIS TO THE SUPPORTED EVENT YOU WANT TO TRACK
    pathOverride, // optional
    eventLabel, // optional
    eventValue, // optional
} );
```

{% hint style="info" %}
Only events fired after the subscriptions.js script loads will be tracked
{% endhint %}

## Complete example

Here's an example of our standard web tracking snippet with the conversion snippet included:

<pre class="language-markup"><code class="lang-markup"><strong>&#x3C;script type='text/javascript'>
</strong>  (function() {
      /** CONFIGURATION START **/
      var _sf_async_config = window._sf_async_config = (window._sf_async_config || {});
      _sf_async_config.uid = #####; //CHANGE THIS TO YOUR ACCOUNT ID
      _sf_async_config.domain = 'domain.com'; //CHANGE THIS TO YOUR SITE ID
      _sf_async_config.useCanonical = true;
      _sf_async_config.useCanonicalDomain = true;
      _sf_async_config.sections = ''; //SET PAGE SECTION(S)
      _sf_async_config.authors = ''; //SET PAGE AUTHOR(S)
      /** CONFIGURATION END **/
      function loadChartbeat() {
          var e = document.createElement('script');
          var n = document.getElementsByTagName('script')[0];
          e.type = 'text/javascript';
          e.async = true;
          e.src = '//static.chartbeat.com/js/chartbeat.js';
          n.parentNode.insertBefore(e, n);
      }
      loadChartbeat();
   })();
&#x3C;/script>
    
&#x3C;!-- Chartbeat Conversion -->
&#x3C;script type="text/javascript">
    (function(w,d,s,c,u){
	var qf=function(e,a){(w[c].q=w[c].q||[]).push([e,a]);};w[c]=w[c]||qf;
	var st=d.createElement(s);
	var fs=d.getElementsByTagName(s)[0];
	st.async=1;st.src=u;fs.parentNode.insertBefore(st,fs);
    })(window,document,'script','chartbeat','https://static.chartbeat.com/js/subscriptions.js');
&#x3C;/script>
&#x3C;!-- End Chartbeat Conversion -->
</code></pre>

## Limitations

Currently, tracking conversions is only supported for our [**standard website tracking integration**](https://help.chartbeat.com/hc/en-us/articles/360017557814). Though we offer unique *visitor tracking* integrations for native apps (along with AMP), we cannot provide conversion tracking for these separate platform integrations at this time.&#x20;

If you are a current customer and this functionality is important to your team, we'd like to hear from you! Shoot us a note at <feedback@chartbeat.com>.&#x20;

Sites that are using our [cookieless tracking](https://docs.chartbeat.com/cbp/tracking/standard-websites/alternative-integrations-web#implement-cookieless-tracking) or [cookieDomain variable](https://docs.chartbeat.com/cbp/tracking/standard-websites/configuration-variables#cookie-domain) are also not supported at this time.

## Next Steps

The next page of this guide covers the **full list of conversion events** available for your team to send to the Conversion tool.


# Conversion Events

Use our built-in events to track conversion flows and send key event properties to Chartbeat.

## Supported events

Below is a list of the event methods you can pass to the `chartbeat()` function, followed by a list of [optional parameters](#event-parameters) you can send with each method. You should fire a Chartbeat event from the same category on every page in a single conversion flow.&#x20;

### Subscriptions

Track paywall activity and subscription transactions on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="251">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackPaywallShown</code></td><td>A user was shown a paywall</td><td>Required</td></tr><tr><td><code>trackPaywallStart</code></td><td>A user started a subscription process</td><td><em>Optional</em></td></tr><tr><td><code>trackPaywallComplete</code></td><td>A user completed a subscription transaction</td><td>Required</td></tr><tr><td><code>trackPaywallCancel</code></td><td>A user canceled a subscription process</td><td><em>Optional</em></td></tr></tbody></table>

### Registrations

Track registration activity (i.e. users creating an account) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="298">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackRegistrationShown</code></td><td>A user viewed a registration screen</td><td>Required</td></tr><tr><td><code>trackRegistrationStart</code></td><td>A user started the registration process</td><td><em>Optional</em></td></tr><tr><td><code>trackRegistrationComplete</code></td><td>A user completed the registration process</td><td>Required</td></tr><tr><td><code>trackRegistrationCancel</code></td><td>A user canceled the registration process</td><td><em>Optional</em></td></tr></tbody></table>

### **Newsletter Signups**

Track newsletter signup activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="336">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackNewsletterSignupShown</code></td><td>A user was shown a newsletter signup option</td><td>Required</td></tr><tr><td><code>trackNewsletterSignupStart</code></td><td>A user started a signup process</td><td><em>Optional</em></td></tr><tr><td><code>trackNewsletterSignupComplete</code></td><td>A user completed a signup process</td><td>Required</td></tr><tr><td><code>trackNewsletterSignupCancel</code></td><td>A user canceled a signup process</td><td><em>Optional</em></td></tr></tbody></table>

### Unsubscribes

Track unsubscribe activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="298.5">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackUnsubscribeShown</code></td><td>A user visited an unsubscribe page</td><td><em>Optional</em></td></tr><tr><td><code>trackUnsubscribeStart</code></td><td>A user started an unsubscribe flow</td><td><em>Optional</em></td></tr><tr><td><code>trackUnsubscribeComplete</code></td><td>A user unsubscribed</td><td>Required</td></tr><tr><td><code>trackUnsubscribeCancel</code></td><td>A user dismissed an unsubscribe flow</td><td><em>Optional</em></td></tr></tbody></table>

### Offers

Track offer activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="241.5">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackOfferShown</code></td><td>A user was shown an offer</td><td><em>Optional</em></td></tr><tr><td><code>trackOfferStart</code></td><td>A user started an offer</td><td><em>Optional</em></td></tr><tr><td><code>trackOfferComplete</code></td><td>A user completed an offer</td><td>Required</td></tr><tr><td><code>trackOfferCancel</code></td><td>A user dismissed an offer</td><td><em>Optional</em></td></tr></tbody></table>

### Surveys

Track survey activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="246.33333333333331">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackSurveyShown</code></td><td>A user was shown a survey</td><td><em>Optional</em></td></tr><tr><td><code>trackSurveyStart</code></td><td>A user started a survey</td><td><em>Optional</em></td></tr><tr><td><code>trackSurveyComplete</code></td><td>A user completed a survey</td><td>Required</td></tr><tr><td><code>trackSurveyCancel</code></td><td>A user dropped off a survey</td><td><em>Optional</em></td></tr></tbody></table>

### Account Creations

Track user profile activity (e.g. filling out text fields) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="287.3333333333333">Event</th><th width="321">Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackUserProfileShown</code></td><td>A user viewed their profile</td><td><em>Optional</em></td></tr><tr><td><code>trackUserProfileStart</code></td><td>A user started editing their profile</td><td><em>Optional</em></td></tr><tr><td><code>trackUserProfileComplete</code></td><td>A user completed editing to their profile</td><td>Required</td></tr><tr><td><code>trackUserProfileCancel</code></td><td>A user exited profile-editing mode</td><td><em>Optional</em></td></tr></tbody></table>

### Downloads

Track download activity (e.g. pdf case studies, apps) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="258.3333333333333">Event</th><th width="297">Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackDownloadStart</code></td><td>A user started downloading data</td><td><em>Optional</em></td></tr><tr><td><code>trackDownloadComplete</code></td><td>A download has completed</td><td>Required</td></tr><tr><td><code>trackDownloadCancel</code></td><td>A download was canceled </td><td><em>Optional</em></td></tr></tbody></table>

### Donations

Track donation activity on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th width="264.3333333333333">Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackDonationShown</code></td><td>An option to donate was shown</td><td><em>Optional</em></td></tr><tr><td><code>trackDonationStart</code></td><td>A user started the donation process</td><td><em>Optional</em></td></tr><tr><td><code>trackDonationComplete</code></td><td>A user completed the donation process</td><td>Required</td></tr><tr><td><code>trackDonationCancel</code></td><td>A user canceled the donation process</td><td><em>Optional</em></td></tr></tbody></table>

### Payments

Track payment activity on your site (other than payment for a subscription).

<table data-header-hidden data-full-width="true"><thead><tr><th width="248.33333333333331">Event</th><th width="325">Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackPaymentStart</code></td><td>A user started a payment process</td><td><em>Optional</em></td></tr><tr><td><code>trackPaymentComplete</code></td><td>A user completed a payment process</td><td>Required</td></tr><tr><td><code>trackPaymentCancel</code></td><td>A user canceled a payment process</td><td><em>Optional</em></td></tr></tbody></table>

### Searches

Track completed search queries on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th>Event</th><th>Description</th><th></th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackSearchComplete</code></td><td>A user performed a search</td><td>Required</td></tr></tbody></table>

### Saves

Track save activity (e.g. a user added an item to their "Favorites" collection) on your site.

<table data-header-hidden data-full-width="true"><thead><tr><th>Event</th><th>Description</th><th>Text</th></tr></thead><tbody><tr><td><strong>Event Name</strong></td><td><strong>Description</strong></td><td><strong>Requirement</strong></td></tr><tr><td><code>trackSaveComplete</code></td><td>A user saved an item for future retrieval</td><td>Required</td></tr></tbody></table>

## Call t**he `chartbeat()` function**

Once you've [added the subscriptions.js snippet](broken://pages/sgUstYHXhbBSAwR63LCj) to your webpages, call the global `chartbeat()` function to track subscription events — with or without [optional parameters](#event-parameters).&#x20;

```javascript
chartbeat('event_name', { // CHANGE THIS TO THE SUPPORTED EVENT YOU WANT TO TRACK
    pathOverride, // optional
    eventLabel, // optional
    eventValue, // optional
} )
```

Track activity related to offers on your site.

## Event parameters

There are 3 *optional* parameters you can send with each event:

<table data-header-hidden data-full-width="false"><thead><tr><th>Optional Parameter</th><th width="144">Type</th><th width="112">Default</th><th>Description</th></tr></thead><tbody><tr><td>Optional Parameter</td><td>Type</td><td>Default</td><td>Description</td></tr><tr><td><code>pathOverride</code></td><td>string</td><td>null</td><td>Set a path different from the one sent by your standard configuration <strong>(not recommended)</strong></td></tr><tr><td><code>eventLabel</code></td><td>string</td><td>null</td><td>Set a label (e.g. <code>monthly</code> or <code>annual</code> subscription package)</td></tr><tr><td><code>eventValue</code></td><td>number</td><td>null</td><td>Set a value (e.g. the price of the subscription package)</td></tr></tbody></table>

{% hint style="warning" %}
Optional event parameters are not currently surfaced in any Chartbeat product.
{% endhint %}

## Examples

Here is a basic example that you would add to your paywall's "Complete" event handler:

```
// Add to your Paywall Complete event handler

chartbeat('trackPaywallComplete');
```

### Google Analytics Tag Manager

<figure><img src="/files/7RD5CFxYZVPO4YX4zjHU" alt=""><figcaption><p>An example of an implementation using a new Tag in Google Analytics Tag Manager linked to an existing Trigger</p></figcaption></figure>

### Piano Composer

Below is an example from [Piano Composer](https://piano.io/product/composer/):

```javascript
tp.push(["addHandler", "checkoutComplete", function(conversionData){
    chartbeat('trackPaywallComplete');
}]);
```

## Next Steps

Review our **integration QA instructions** article for best practices.&#x20;


# Integration QA Steps: Conversion

Steps to QA your integration of Chartbeat Conversion

## Conversion QA Checklist

### Reviewing your code

* [ ] [**Verify that subscriptions.js loads**](#verify-subscriptions.js-loads)**:** In order to begin tracking conversion events with Chartbeat, the subscriptions.js script must load on your pages.&#x20;
* [ ] [**Verify events are tracked**](#verify-events-are-being-tracked): Review the conversion event ping data being sent to Chartbeat by using your browser's developer tools.

### Conversion Event Guidelines

Review the guidelines below to ensure your conversion events are being fired according to our best practices:

* [ ] **Fire a Chartbeat event on every page in a single conversion flow.** \
  \
  Events with `Shown` or `Complete` in their name should only be fired **once** per conversion flow but those with `Start` or `Cancel` may be called multiple times if needed. \
  \
  Below is an example of a conversion flow that spans multiple pages:<br>

  <table><thead><tr><th>Page Path</th><th width="284">Chartbeat Event Name</th><th>Optional Parameter</th></tr></thead><tbody><tr><td>example.com/articleA</td><td><code>trackPaywallShown</code></td><td><code>eventLabel: 'Modal paywall'</code></td></tr><tr><td>example.com/signup1</td><td><code>trackPaywallStart</code></td><td><code>eventLabel: 'Enter customer info'</code></td></tr><tr><td>example.com/signup2</td><td><code>trackPaywallStart</code></td><td><code>eventLabel: 'Enter payment info'</code></td></tr><tr><td>example.com/signup-complete</td><td><code>trackPaywallComplete</code></td><td><code>eventValue: 100</code></td></tr></tbody></table>

  <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p>You can use one of our <a href="#event-parameters">optional parameters</a> – e.g. an <code>eventLabel</code> or <code>eventValue</code> – to distinguish events with the same name.</p></div>

* [ ] **Use the `trackPaywallShown` event on the&#x20;*****article*****&#x20;page associated with a paywall.**\
  \
  The path associated with the `trackPaywallShown` event is used to determine the content a user last viewed prior to a conversion event. <br>

* [ ] **Use events from the same category to track a single conversion flow.**\
  \
  Use the same category when tracking a single conversion. Do not start a conversion flow with `trackPaywallShown` and end with `trackRegistrationComplete`.<br>

* [ ] **Use `trackPaywallComplete` at a place in the code that runs after a successful conversion.** \
  \
  Call `Complete` events after a successful conversion – for all event tracking methods.<br>

* [ ] **Fire Chartbeat events only when you are sure they have occurred.**\
  \
  If you fire an event e.g. `trackPaywallComplete` on page load, make sure you account for edge cases. If users can visit that page directly, your logic should first check that the user is completing a paywall flow by, for example, checking the referrer or the user's subscriber status<br>

* [ ] **Fire Chartbeat events only on your site's domain.** \
  \
  Some conversion pages, like checkout pages, are hosted on separate domains. If a conversion event occurs on a separate domain, the corresponding Chartbeat `Complete` event should be called on your domain's article page, **after** the user is redirected from the checkout page. \
  \
  Below is an example of a conversion flow that includes pages hosted on a separate domain:<br>

  | Page Path                    |    Chartbeat Event Name    |
  | ---------------------------- | :------------------------: |
  | example.com/articleA         |     `trackPaywallShown`    |
  | example.com/signup1          |     `trackPaywallStart`    |
  | payment.com/example-checkout | :x: *DO NOT FIRE AN EVENT* |
  | example.com/articleA         |   `trackPaywallComplete`   |

### Verify subscriptions.js loads

In order to track events with Chartbeat, the subscriptions.js script must load first. Use your browser's developer tools to confirm that it is loading.

Select the **Network** tool and filter for `subscriptions.js`:

<figure><img src="/files/haT6gSFryp1hHARJjf0R" alt="" width="563"><figcaption></figcaption></figure>

### Verify events are being tracked

Conversion data is sent via a ping request to Chartbeat, you can review the data being sent by using using your browser's developer tools:\
\
Select **Network** and filter for `conversion_event` :

<figure><img src="/files/TFLIhLDPB9c4SGGtYPxT" alt="" width="563"><figcaption></figcaption></figure>

Click **Payload** and review the query string parameters. Verify that the values match what you intended to send.

| Parameter | Value                           |
| --------- | ------------------------------- |
| p         | path                            |
| h         | siteID or host                  |
| d         | actual domain                   |
| cec       | event category (e.g. `paywall`) |
| cet       | event type (e.g. `shown`)       |
| cel       | event label                     |
| cev       | event value                     |

{% hint style="info" %}
If you are using the optional `pathOverride` parameter, make sure to check that the `p`  parameter contains the `pathOverride` value. Otherwise `p` should match the article's page path that you're sending to Chartbeat in your [standard configuration](https://docs.chartbeat.com/cbp/tracking/standard-websites/our-javascript).
{% endhint %}

## Using Debug Mode

When debug mode is enabled for each event, the event details will display in the console.&#x20;

**Enable Debug Mode for an Event:** Add the `debug` parameter set to `true` for an event method to print the event configuration to the console. This will only print to the console if a ping was successfully sent to Chartbeat.&#x20;

```
chartbeat('trackPaywallStart', {
    debug: true
})
```

In your browsers's developer tools, select **Console** to find your event debug details:

<figure><img src="/files/Z458DbPlZ9Y1pQrm6M8P" alt="" width="563"><figcaption></figcaption></figure>

## Implementation Review

Our Implementation Review dashboard lets Admin users view data snapshots from the latest full calendar day we have on hand. The [Conversions Audit tab](https://chartbeat.com/publishing/settings/implementation-review/conversions) provides a way to verify correct Conversion implementation and to help self-diagnose data discrepancies or issues.

#### To access Implementation Review:

1. [**Sign in**](https://help.chartbeat.com/hc/en-us/articles/360017718093) to your Chartbeat account.
2. Navigate to Settings from the menu top right-head
3. Click the Implementation Review pages on the left side menu
4. Navigate to the Conversions Audit Tab

{% hint style="warning" %}
**Tip:** If you cannot access the Implementation Review, verify with your organization if your account has the required Admin role. &#x20;
{% endhint %}

## Next Steps

Read through the next article in this guide to review a list of **supported conversion flows**


# Supported Conversion Flows

Customers planning to use our Conversion add-on should review the below information to determine whether or not Conversion is compatible with your conversion flow

### Supported Conversion Flows

<div data-full-width="true"><figure><img src="/files/l4d7k77rg5tImRDnk9xV" alt=""><figcaption></figcaption></figure></div>

:white\_check\_mark: **We support** flows that start and end on the same root domain

:white\_check\_mark: **We support** flows that incorporate root and subdomains that report to the same Chartbeat dashboard. *Please note this is only possible if you have **not** included* Chartbeat's `cookieDomain` *on your subdomains.*&#x20;

:white\_check\_mark: **We support** flows that start and end on subdomains

### Unsupported Conversion Flows

❌ **We do not support** flows that use the `cookieDomain` configuration on subdomains. See our [cookieDomain](/cbp/tracking/standard-websites/configuration-variables#cookie-domain) documentation for more information

❌ **We do not support** flows that span multiple domains without redirect: *We cannot track the same user across different domains, see our* [*Partially Supported Conversion Flows*](#partially-supported-conversion-flows) *for more information.*

❌ **We do not support** sites using Chartbeat's [Cookieless mode](/cbp/tracking/standard-websites/alternative-integrations-web#implement-cookieless-tracking).

### Partially Supported Conversion Flows

If you have a cross-domain flow such as a third-party payment system ensure that you implement a redirect page back to the domain where the `Shown` event was fired, and call your `Complete` event there. Do not fire any events on the third-party pages.&#x20;


# Push Alerts

Chartbeat’s Mobile SDK integrates Chartbeat analytics with your native iOS or Android app. This page includes documentation for functionality of an updated SDK release candidate with support for push-alert referrers. For general SDK documentation, please reference [this page](http://support.chartbeat.com/docs/sdk.html).

Beginning with Chartbeat iOS SDK version [1.3.0](https://cocoapods.org/pods/Chartbeat), Android SDK version [1.4.0](https://bintray.com/chartbeat-labs/maven/com.chartbeat.androidsdk), and React Native SDK version [1.0.2](https://www.npmjs.com/package/chartbeat-react-native-bridge-sdk), the Chartbeat Native App Integration provides support for tracking push notifications.

<figure><img src="/files/CvPUyUkaiXMIKYM1Xxrw" alt="" width="283"><figcaption></figcaption></figure>

### setPushReferrer() Method <a href="#method" id="method"></a>

When your app responds to push notifications, use the `setPushReferrer()` method to set an identifier for the notification received. The identifier specified will be sent along with an indicator that the referrer is a push notification. These push notifications and their ids will be viewable in the real-time dashboard.

Below are sample snippets for how Chartbeat implemented this method in iOS, Android, and React Native apps.

### iOS <a href="#ios" id="ios"></a>

Call `setPushReferrer()` method with the ID of the push alert **before** the `trackView()` call.

#### Objective-C:

```
[[CBTracker sharedTracker] setPushReferrer:@"your_push_id"];
```

#### Swift:

```
CBTracker.shared().setPushReferrer("your_push_id")
```

### Android <a href="#android" id="android"></a>

Set metadata — including `setPushReferrer` — **after** `trackView()`, because view changes purge metadata.

#### Java

```
Tracker.setPushReferrer(“your_push_id”);
```

### React Native <a href="#android" id="android"></a>

Set article metadata **after** `setupTracker` and **before** `trackView` (or between views, as the article changes).

```
Chartbeat.setPushReferrer('your_push_id');
```

### Push Vendors <a href="#vendors" id="vendors"></a>

The push identifiers for each vendor will vary slightly. You can follow your vendor’s documentation for retrieving the push identifier to add to the Chartbeat SDK for each application.


# Real-Time API

A guide to retrieving your real-time engagement data via our API.

## Articles included in this guide:

1. [Getting Started with our Real-Time API](/cbp/api/real-time-apis/getting-started-with-our-real-time-api)
2. [Traffic Data](/cbp/api/real-time-apis/traffic-data)
3. [Video Engagement Data](/cbp/api/real-time-apis/video-engagement-data)

{% hint style="success" %}
Real-Time API access is a feature included in all customer plans.&#x20;
{% endhint %}

###


# Getting Started with our Real-Time API

Access real-time audience data with an HTTP GET request to our live API endpoints.

Our Real-Time API consists of data that is updated every three seconds, detailing what is happening on your site right now. There are two categories of real-time API endpoints detailed in the next two articles within this section: [**traffic data endpoints**](/cbp/api/real-time-apis/traffic-data), and [**video engagement data**](/cbp/api/real-time-apis/video-engagement-data) endpoints (available only to organizations that use our [**Video Engagement**](/cbp/feature-integrations/video-engagement) add-on).

## Rate limits

In order to prevent against API abuse we rate limit the amount of requests a user is allowed to submit. Our current rate limit is 200 requests per minute per host. Our suggested best practice is to call the endpoint within our rate limit, never directly embed your API calls into your production website code, and instead cache the results server-side to populate your custom widgets or dashboards. In addition, we cache queries for 3 seconds, so it's unnecessary to make requests more frequently than that.

{% hint style="info" %}
There's a header in every response that says how many requests are remaining on the rate limit (X-RateLimit-Limit and X-RateLimit-Remaining).
{% endhint %}

## Required parameters

**Host:** To be passed as a query parameter "host" in the URL. This is the Site ID associated with your Chartbeat account that you want to get data for, and the value of your `_sf_async_config.domain` variable in the Chartbeat tracking code for that site. If you are using one of our real-time video API endpoints, the host value is your site ID prepended with "video@". For example, `host=video@mysite.com`.

**API Key**: To be passed in the HTTP Header with "X-CB-AK". You can create and access your [**API key here**](https://chartbeat.com/publishing/settings/api-keys/) under your account settings. Make sure to select an API key that has access to all calls for your domain.&#x20;

### Example CURL request

`curl --header "X-CB-AK: <YOUR API KEY>" "https://api.chartbeat.com/live/toppages/v3/?all_platforms=1&host=<YOUR HOST>&limit=50&loyalty=1&metrics=account_type%2Cmedium%2Csource%2Ccampaign&now_on=1&types=1"`

## Next steps

The next page of this guide details all of our Real-Time API endpoints for retrieving [**traffic and page data**](/cbp/api/real-time-apis/traffic-data).

If you're seeking out our Real-Time API for retrieving video engagement data, skip ahead to [**page 3**](/cbp/api/real-time-apis/video-engagement-data).


# Traffic Data

The following endpoints return real-time traffic data about your webpages and live audience.

{% hint style="success" %}
Our Real-Time Traffic Data API endpoints are accessible to all customers regardless of your plan.
{% endhint %}

## Top Pages

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/live/toppages/v3/`

This call returns a list of top pages on your site ordered by default based on the number of concurrents.

#### Query Parameters

| Name                                     | Type    | Description                                                                                                                                                                                                          |
| ---------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apikey<mark style="color:red;">\*</mark> | string  | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/real-time-apis/getting-started-with-our-real-time-api#required-parameters) |
| host<mark style="color:red;">\*</mark>   | string  | The site ID of the site you are querying.                                                                                                                                                                            |
| metrics                                  | string  | Accepts custom metrics sets with a variety of outputs. Video customers can specify **\&metrics=video\_state** for video information in the top pages response                                                        |
| jsonp                                    | string  | The name of a function in which to wrap the return data                                                                                                                                                              |
| limit                                    | integer | Number of pages to return (Default returns 10)                                                                                                                                                                       |
| section                                  | string  | Return top pages within a section, or multiple sections (e.g. "us, news")                                                                                                                                            |
| author                                   | string  | Return top pages by author, or multiple authors (e.g. "kim, joe")                                                                                                                                                    |
| exclude\_people                          | integer | Set a minimum number of concurrents for returned pages. Useful when sorting by engaged time.                                                                                                                         |
| sort\_by                                 | string  | <p>Parameter by which to sort your Top Pages (Default is by most concurrents). <br><br>Options: engaged\_time, new, returning, social, links, internal, direct, search</p>                                           |
| now\_on                                  | integer | Set to 1 to show the recirculation list for article pages                                                                                                                                                            |
| all\_platforms                           | integer | Set to 1 to show the breakdown of desktop, mobile, tablet, and app                                                                                                                                                   |
| loyalty                                  | integer | Set to 1 to show the breakdown of new, returning, and loyal visitors                                                                                                                                                 |
| types                                    | integer | Set to 1 to show the type of page ("Article" or "Landing Page")                                                                                                                                                      |

{% tabs %}
{% tab title="200 Response" %}

```scheme
{
    pages: [
        {
            stats: {
                engaged_visit: {
                    //A histogram of number of concurrents 
                    //that are engaging with the site right now 
                    //that fall within the following time 
                    //on site buckets (in minutes):
                    //1.0,2.0,3.0,4.0,5.0,6.0,10.0,15.0,30.0+
                    breaks: [
                        //Histogram buckets
                    ],
                    num_values: 0,
                    median: 0,
                    hist: [
                        // Actual histogram values
                    ],
                    avg: 0.00,
                    sum: 0.00
                },
                engaged_time: {
                    //A histogram of number of concurrents  
                    //that have engaged times that fall within
                    //the following time buckets (in seconds):
                    //0,0-15,15-30,30-45,45-60,60-120,120-180,
                    //180-240,240-300,300-600,600-900,900+
                    breaks: [
                        //Histogram buckets
                    ],
                    num_values: 0,
                    median: 0,
                    hist: [
                        //Actual histogram values
                    ],
                    avg: 0,
                    sum: 0
                },
                writing_visit: {...},
                domload: {...},
                    //A histogram of number of concurrents  
                    //broken down by how long their DOM took to  
                    //load, within the following time buckets (seconds): 
                    //1-2,2-3,3-4,4-5,5-6,6-7,7-8,8-9,9-10,10+
                scroll: {...},
                    //A histogram of how far down the page 
                    //concurrent visitors have scrolled (in 10% intervals)
                visit: {...},
                people: 0, //Concurrents
                read: 0,
                visits: 0,
                performance_score: null,
                subscr: 0, 
                    //The number of people arriving from a 
                    //web based subscription server (e.g. Google Reader)
                platform: {
                    //Concurrents by platform type
                    a: 0,
                    e: 0,
                    d: 0,
                    g: 0,
                    f: 0,
                    i: 0,
                    m: 0,
                    r: 0,
                    t: 0
                },
                platform_engaged: {
                    //Concurrents currently engaging by platform type
                    a: 0,
                    e: 0,
                    d: 0,
                    g: 0,
                    f: 0,
                    i: 0,
                    m: 0,
                    r: 0,
                    t: 0
                },
                article: 0, 
                    //If an article page, will return the number  
                    //of concurrents on that page, else 0
                toprefs: [
                    //Top referrals for this page
                    {
                        visitors: 0,
                        domain: "Google Search"
                    },
                    {
                        visitors: 0,
                        domain: "Bing"
                    },
                    {
                        visitors: 0,
                        domain: "Your Domain"
                    },
                    {
                        visitors: 0,
                        domain: "Yahoo Search"
                    },
                    {
                        visitors: 0,
                        domain: "MSN"
                    }
                ],
                recirc: 0, //Recirculated concurrents 
                crowd: 0, 
                write: 0, //Concurrents on the page that are typing
                num_refs: 0, //Number of unique referrers
                idle: 0, //Concurrents that are idle
                direct: 0, //Concurrents from Direct traffic sources
                links: 0, //Concurrents from Links traffic sources
                search: 0, //Concurrents from Search traffic sources
                internal: 0, //Concurrents from Internal traffic sources
                social: 0, //Concurrents from Social traffic sources
                new: 0 //Concurrents that are classified as "New" visitors
            },
            title: "Breaking News - Top Story",
            host: "yourdomain.com",
            authors: [
                "Jim Carrey"
            ],
            path: "yourdomain.com/top-stories/",
            sections: [
                "breaking news",
                "domestic",
                "politics"
            ]
        },
{...}

// Note: All histograms return the same values (breaks, num_values, median, hist, avg, sum). For brevity, these have been condensed in the documentation.
```

{% endtab %}
{% endtabs %}

### Top Pages: Custom Metrics

{% hint style="warning" %}
**Important:** Custom Metrics are only supported by the Top Pages API endpoint, and they are not viewable in your other Chartbeat Dashboards and Reports. The purpose of this feature is to send an additional custom data point to Chartbeat that your team wants to surface in a custom widget powered by our Top Pages data. For example, passing image thumbnail URLs for article pages for use in a [**Chartbeat-powered recirculation module**](https://chartbeat.zendesk.com/hc/en-us/articles/360045337214-Guide-to-Chartbeat-APIs#h_3a0daa7c-ca8d-4db2-b54e-f495436525ed) on your live site.
{% endhint %}

#### Creating a Custom Metric

To create a Custom Metric you'll need to let us know the key, the type, and the label for your metric (details below). Note that custom metrics are only available via our real-time API and are not present in our Real-Time and Historical Dashboards. [**Contact our support team**](https://chartbeat.zendesk.com/hc/en-us/requests/new) **to** get started.

The explanations for these values are as follows:

* **Label**: A human-readable name for this metric, e.g. `user_account_type` or `product_name`. The label of a metric may contain letters (A-Z, a-z) and underscores.&#x20;
* **Key**: The key that will be reported to our servers. For example, if you are tracking user\_account\_type, you might use the key \_acct or \_utype. The shorter the better, but for a key to be reported it must begin with an underscore.
* **Type**: What kind of metric you are tracking and any metadata associated with the given type. The types are:
  * **Number**: A floating point, signed number. Use for reporting numerical stats, such as average, sum, min, max, etc.&#x20;
  * **String**: Any arbitrary string, e.g. "awesome" or "pelle". No interpretation of it is made. Reports are of number of visitors with a value, top 5 values, and number of unique values.
  * **Enum**: Data is interpreted as one of the given enum values. Multiple strings can match one enum value (eg: "female", "f", "woman", "lady", etc all get mapped to one index). The report is an un-labeled array of counts for each enum-index, eg: \[4, 0, 23, 3].

#### Reporting a Custom Metric

Once your Custom Metric has been created, you'll need to handle the implementation on your site. To report a Metric to Chartbeat, you'll need to add it to the `_cbq` object by providing the key you emailed Chartbeat about, and a value:

```javascript
var _cbq = window._cbq = (window._cbq || []);
_cbq.push(['_usr', 'pro']);
```

The above code will tell chartbeat.js to add `&_usr=pro` to all pings. You can update a Metric at any time during a user's interactions without waiting for them to reload the page or travel to a new page by merely calling `_cbq.push` again with the same key, and a new value. The change will be reported with the next ping.

## Summary

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/live/summary/v3/`

Returns either numeric or categorical summaries of event fields given a host and optional path. Numeric summaries include min, max, sum, nonzero observations, observations and sum of squares. Categorical summaries include field keys with associated counts. This call return real- time data.\
\
The **keys** parameter specifies what data to return from the call. Accepts one or a comma separated list of values (detailed in the table below).

#### Query Parameters

| Name   | Type   | Description                                                                                                                                                                                                          |
| ------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apikey | string | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/real-time-apis/getting-started-with-our-real-time-api#required-parameters) |
| host   | string | Required. The site ID of the Site you are querying.                                                                                                                                                                  |
| keys   | string | Required. Specifies what data to return from your call.  Choose from the list of key values below. Accepts a single value, or a comma separated list.                                                                |
| jsonp  | string | The name of a function in which to wrap the return data.                                                                                                                                                             |
| path   | string | Returns data for a specific path.                                                                                                                                                                                    |

{% tabs %}
{% tab title="200 Response " %}

```scheme
{
  path: {
    data: {
      support.chartbeat.com/edu/glossary.html: 1,
      support.chartbeat.com/docs/api.html: 1,
      support.chartbeat.com/docs/: 1,
      support.chartbeat.com/edu/archive/hud.html: 1,
      support.chartbeat.com/docs/rb.html: 1
    },
    type: "string"
  },
  domain: {
    data: {
      support.chartbeat.com: 5
    },
    type: "string"
  },
  title: {
    data: {
      Education: 2,
      Documentation: 3
    },
    type: "string"
  }
}
```

{% endtab %}
{% endtabs %}

| "Keys" values   | Description of returned data                                                         |
| --------------- | ------------------------------------------------------------------------------------ |
| `pagetimer`     | Time to finish loading the DOM.                                                      |
| `time_spent`    | Number of seconds on the page.                                                       |
| `domain`        | The domain name of the document (what's in the browser bar).                         |
| `uid`           | The Chartbeat account.                                                               |
| `host`          | The reported domain (the dashboard the data goes to).                                |
| `title`         | Page title.                                                                          |
| `new`           | First-time visitor for the site in the last 30 days.                                 |
| `path`          | Path of the page from location.pathname.                                             |
| `referrer`      | Referrer from document.referrer.                                                     |
| `token`         | Temporary uuid event's page session (regenerated when moving to another page).       |
| `user`          | User token.                                                                          |
| `window_height` | window\.innerHeight or document.body.offsetHeight.                                   |
| `scroll_top`    | window\.pageYOffset or document.body.scrollTop or document.documentElement.scrollTop |
| `page_height`   | document.body.scrollHeight.                                                          |
| `read`          | The number of people reading.                                                        |
| `write`         | The number of people writing.                                                        |
| `idle`          | The number of people idle.                                                           |

## Referrers

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/live/referrers/v3/`

Returns the list of top referrers for a specific domain.

#### Query Parameters

| Name     | Type    | Description                                                                                                                                                                                                          |
| -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apikey   | string  | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/real-time-apis/getting-started-with-our-real-time-api#required-parameters) |
| host     | string  | The site ID of the Site you are querying.                                                                                                                                                                            |
| jsonp    | string  | The name of a function in which to wrap the return data.                                                                                                                                                             |
| limit    | integer | The total number of referrers to return.                                                                                                                                                                             |
| path     | string  | Returns data for a specific path.                                                                                                                                                                                    |
| by\_type | integer | Set to 1 to show the breakdown of referrer information by traffic source.                                                                                                                                            |

{% tabs %}
{% tab title="200 Response" %}

```scheme
{
    referrers: {
        Google Search: 2,
        Facebook: 2,
        Twitter: 3
    }
}
```

{% endtab %}
{% endtabs %}

## Geo

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/live/top_geo/v1/`

Returns real-time geographic information by location segment, including regions, countries, cities, and metro codes.

#### Query Parameters

| Name   | Type    | Description                                                                                                                                                                                                          |
| ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apikey | string  | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/real-time-apis/getting-started-with-our-real-time-api#required-parameters) |
| host   | string  | The site ID of the Site you are querying.                                                                                                                                                                            |
| jsonp  | string  | The name of a function in which to wrap the return data.                                                                                                                                                             |
| limit  | integer | Number of segments to return.                                                                                                                                                                                        |

{% tabs %}
{% tab title="200 Response" %}

```scheme
{
  geo: {
    regions: {
      US-TX: 61,
      US-VA: 75,
      PE-15: 41,
      US-CT: 71,
      AR-07: 67,
      CO-34: 49,
      CA-QC: 99,
      CA-ON: 116,
      US-NJ: 60,
      BR-27: 84,
      US-CA: 173,
      US-MD: 41,
      US-DC: 79,
      ES-29: 188,
      US-NY: 288,
      US-GA: 94,
      GB-H9: 174,
      MX-09: 126,
      US-FL: 120,
      US-IL: 69
    },
    countries: {
      ES: 265,
      FR: 51,
      CO: 67,
      CA: 263,
      DE: 79,
      JP: 28,
      IT: 66,
      US: 1696,
      MX: 179,
      NZ: 38,
      AR: 118,
      AU: 67,
      IL: 27,
      BR: 134,
      IN: 28,
      FI: 38,
      IE: 26,
      PE: 47,
      SE: 42,
      GB: 310
    },
    total: 3900,
    cities: {
      Toronto: 83,
      Mexico City: 111,
      Sao Paulo: 62,
      Bristol: 42,
      Boucherville: 31,
      Helsinki: 28,
      Paris: 26,
      Washington: 81,
      Auckland: 31,
      Buenos Aires: 34,
      Bogota: 48,
      Brooklyn: 91,
      New York: 97,
      Chicago: 42,
      Los Angeles: 41,
      Madrid: 162,
      London: 175,
      Montréal: 52,
      Lima: 41,
      Atlanta: 57
    },
    metro_codes: {
      501: 334,
      504: 31,
      506: 42,
      511: 157,
      524: 93,
      528: 34,
      533: 65,
      534: 24,
      539: 28,
      544: 15,
      561: 18,
      602: 67,
      609: 19,
      613: 28,
      618: 24,
      751: 19,
      753: 14,
      803: 90,
      807: 58,
      819: 32
    }
  }

```

{% endtab %}
{% endtabs %}

## Quickstats

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/live/quickstats/v4/`

​Returns information about the most recent visitors for a specific domain.

#### Query Parameters

| Name           | Type    | Description                                                                                                                                                                                                          |
| -------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apikey         | string  | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/real-time-apis/getting-started-with-our-real-time-api#required-parameters) |
| host           | string  | The site ID of the site you are querying.                                                                                                                                                                            |
| jsonp          | string  | The name of a function to wrap the return data in.                                                                                                                                                                   |
| path           | string  | Returns data for a specific path.                                                                                                                                                                                    |
| now\_on        | integer | Set to 1 to show the recirculation list for article pages.                                                                                                                                                           |
| all\_platforms | integer | Set to 1 to show the breakdown of desktop, mobile, tablet and app.                                                                                                                                                   |
| loyalty        | integer | Set to 1 to show the breakdown of new, returning, and loyal visitors.                                                                                                                                                |
| types          | integer | Set to 1 to show the type of page ("Article" or "Landing Page").                                                                                                                                                     |
| section        | string  | Returns data only for visitors in the defined section.                                                                                                                                                               |

{% tabs %}
{% tab title="200 Response" %}

```scheme
{
   data:{
      metrics:{

      },
      host_metadata:{
         subdomain_handling: true
      },
      stats:{
         engaged_visit:{
            breaks:[
               1,
               2,
               3,
               4,
               5,
               6,
               10,
               15,
               30
            ],
            num_values: 42339,
            median: 3,
            hist:[
               16436,
               3907,
               2474,
               1586,
               1191,
               836,
               1903,
               1127,
               1360,
               11519
            ],
            avg: 32.725,
            sum: 1385559.66
         },
         engaged_time:{
            breaks:[
               0,
               15,
               30,
               45,
               60,
               120,
               180,
               240,
               300,
               600,
               900
            ],
            num_values: 180292,
            median: 30,
            hist:[
               42734,
               43671,
               20854,
               14671,
               10715,
               23746,
               10090,
               4977,
               2653,
               4117,
               1025,
               1039
            ],
            avg: 61.403,
            sum: 11070442
         },
         links: 7286,
         people: 180292,
         read: 41159,
         writing_visit:{
            breaks:[
               1,
               2,
               3,
               4,
               5,
               6,
               10,
               15,
               30
            ],
            num_values: 1180,
            median: 10,
            hist:[
               193,
               111,
               74,
               59,
               61,
               45,
               133,
               121,
               188,
               195
            ],
            avg: 16.006,
            sum: 18886.620000000003
         },
         toppages:[
            {
               path:"mysite.com/",
               visitors:43984
            },
            {
               path:"mysite.com/2020/06/11/news/my-article-1.html",
               visitors:6523
            },
            {
               path:"mysite.com/2020/06/11/sports/story-2.html",
               visitors:5569
            },
            {
               path:"mysite.com/interactive/2020/news/story-3.html",
               visitors:3835
            },
            {
               path:"mysite.com/2020/06/11/entertainment/story-4.html",
               visitors:2446
            },
            {
               path:"mysite.com/2020/06/11/business/story-5.html",
               visitors:605
            }
         ],
         direct: 17373,
         visits: 180292,
         recirc: 21402,
         subscr: 0,
         platform_engaged:{
            a: 0,
            e: 0,
            d: 19265,
            g: 0,
            f: 0,
            i: 0,
            m: 22642,
            r: 0,
            t: 0
         },
         article: 128057,
         pages: 4085,
         search: 36358,
         crowd: 0,
         domload:{
            breaks:[
               1000,
               2000,
               3000,
               4000,
               5000,
               6000,
               7000,
               8000,
               9000,
               10000,
               11000,
               12000,
               13000,
               14000,
               15000,
               16000,
               17000,
               18000,
               19000,
               20000,
               25000,
               30000
            ],
            num_values: 180292,
            median: 5000,
            hist:[
               15413,
               16473,
               26764,
               25579,
               19877,
               53191,
               7146,
               3251,
               1942,
               6604,
               0
            ],
            avg: 11311.049,
            sum: 2039291726
         },
         visit:{
            breaks:[
               1,
               2,
               3,
               4,
               5,
               6,
               10,
               15,
               30
            ],
            num_values: 180292,
            median: 30,
            hist:[
               22171,
               11354,
               7697,
               4708,
               5280,
               4713,
               10487,
               9603,
               20079,
               84200
            ],
            avg: 39.803,
            sum: 7176152.8100000005
         },
         write: 1180,
         platform:{
            a: 0,
            e: 0,
            d: 143512,
            g: 0,
            f: 0,
            i: 0,
            m: 34354,
            r: 0,
            t: 0
         },
         idle: 137953,
         internal: 84490,
         social: 34785,
         new: 27214,
         scroll:{
            breaks:[
               0,
               1,
               2,
               3,
               4,
               5,
               6,
               7,
               8
            ],
            num_values: 386281,
            median: 2,
            hist:[
               74550,
               67362,
               55869,
               43597,
               35226,
               30032,
               24988,
               21221,
               15737,
               17699
            ],
            avg: 3.067,
            sum: 1184617
         }
      }
   }
}
```

{% endtab %}
{% endtabs %}


# Video Engagement Data

Our Video REST APIs give you access to data that makes up the Chartbeat Video Dashboard. Make an HTTP GET request to the endpoints below to return data encoded in JSON.

{% hint style="warning" %}
Video Engagement Data API endpoints are usable only to customers with our Video add-on integrated.
{% endhint %}

## Top Video

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/live/video/videos/v1/`

This live API call returns the top videos on your site.

#### Query Parameters

| Name   | Type    | Description                                                                                                                                                                                                      |
| ------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apikey | string  | NOTE: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/real-time-apis/getting-started-with-our-real-time-api#required-parameters) |
| host   | string  | The site ID of the Site you are querying, prepended with video@ (e.g. <video@mysite.com>)                                                                                                                        |
| limit  | integer | 20 is the default limit of videos to return, but this can be increased or decreased.                                                                                                                             |

{% tabs %}
{% tab title="200 " %}

```scheme
{
    data: {
        items: [
            {
                path: "video_content_id_12345", //The path that is being passed to Chartbeat to identify the video.
                stats: {
                    visitors: 3063, //The number of people who have loaded the video.
                    watching: 2736 //The number of people who have loaded this video and are watching now
                },
                thumbnail: "https://www.mysite.com/thumbnail-11.jpg", //The URL being passed to Chartbeat for the thumbnail image.
                title: "My Great Video!" //The video title.
            },
    {
                path: "video_content_id_09876",
                stats: {
                    visitors: 1695,
                    watching: 1523
                },
                thumbnail: "https://www.mysite.com/thumbnail-22.jpg",
                title: "Must Watch: The President Said What?!"
                },
    {
                path: "video_content_id_45678",
                stats: {
                    visitors: 1588,
                    watching: 1412
                    },
                thumbnail: "https://www.mysite.com/thumbnail-33.jpg",
                title: "Our Top 10 Favorite Seinfeld Episodes"
            },
{...}            
```

{% endtab %}
{% endtabs %}

## Video Metrics

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/live/metrics/`

This call gives detailed information for a specific video. If no video path is specified, all metrics reflect the top results for the aggregate of all videos.

#### Query Parameters

| Name   | Type   | Description                                                                                                  |
| ------ | ------ | ------------------------------------------------------------------------------------------------------------ |
| apikey | string | Your API key.                                                                                                |
| host   | string | The site ID of the Site you are querying, prepended with video@ (e.g. <video@mysite.com>).                   |
| names  | string | Can be a comma-delineated list of any of the metrics in the table below.                                     |
| path   | string | The path that is being passed to Chartbeat to identify the video (Not the path of the page the video is on). |

{% tabs %}
{% tab title="200 Example response for call with names=video\_title,video\_duration applied as a query parameter with metrics." %}

```scheme
{
    metrics:{
        video_duration: {
                count: 13988,
                max: 11983253.3,
                sum: 2374541830.886638,
                min: 0,
                mean: 169755.6356081383
        },
        video_title:{
            count: 15242,
            unique_max: 2756,
            top:{
                My Great Movie: 463,
                Watch Now: Live With Mr News: 173,
                Poll Predicts Landslide Victory: 799,
                New Video Shows Shocking Car Chase: 164,
                Hilarious Security Footage: 222
            },
            unique: 2756,
            unique_min: 298
        }
    }
}
```

{% endtab %}
{% endtabs %}

| Optional "names" values | Metric Definitions                                                                                               |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `video_duration`        | Video length (in milliseconds)                                                                                   |
| `video_title`           | The video title                                                                                                  |
| `video_page`            | Path of the page(s) the video is on                                                                              |
| `video_host`            | Host domain of the page(s) where the video is being played                                                       |
| `video_state`           | Array of the number of visitors in different video states in the format: \[unplayed, playing, paused, completed] |


# Historical API

A guide to retrieving your historical audience data via our Advanced Queries Historical API.

## Articles included in this guide:

1. [Getting Started with our Historical API](/cbp/api/historical-api/getting-started-with-our-historical-api)
2. [One-time Queries](/cbp/api/historical-api/one-time-queries)
3. [Recurring Queries](/cbp/api/historical-api/recurring-queries)
4. [Metrics, Dimensions, and Filters](/cbp/api/historical-api/metrics-dimensions-filters)

{% hint style="warning" %}
Our Advanced Queries Historical API is only accessible to customers with Advanced Queries included in their Chartbeat subscription. Advanced Queries is a Premium feature — learn more about it on our [**Help Center**](https://chartbeat.zendesk.com/hc/en-us/articles/360018182853-Guide-to-Advanced-Queries).
{% endhint %}


# Getting Started with our Historical API

Get familiar with our Advanced Queries Historical API for fetching summary historical traffic data with specified start and end times. Note that Advanced Queries is a Premium feature.

Our Advanced Queries API endpoints may be used to retrieve summary historical data for your site in either one-time or recurring queries, downloadable in CSV or JSON format.

Advanced Queries reports are highly customizable with a wide array of metrics, filters, and dimensions available to return specific summary traffic data about your audience engagement with your online content over time.

## One-time vs recurring queries

There are two query types to pick from:&#x20;

**One-time queries** are good for pulling data over custom date ranges. One-time queries must be submitted anew for every unique run if your team wants to run the same report (using the same metrics, dimensions, and/or filters) over unique date ranges (using unique start and end date parameters).

**Recurring queries** are only submitted once, but they continue to generate new reports for every calendar day, week, or month. This is a good option for teams that want to pull in the same traffic data with every day, week, or month that passes. The query start and end times are defined by the selected date range option:&#x20;

* **Daily** recurring queries (`date_range=day`) return data for the previous 24-hour calendar day based on your selected timezone for the query. These queries begin a new run at 12:01AM each day for their selected timezone, and should be completed (available to fetch) by 4AM.
* **Weekly** recurring queries (`date_range=week`) return data for the previous 7-day calendar week (12:00AM Monday - 11:59PM Sunday) based on your selected timezone for the query. These queries begin a new run at 12:01AM every Monday for their selected timezone, and should be completed (available to fetch) by 4AM Monday.
* **Monthly** recurring queries (`date_range=month`) return data for the previous calendar month based on your selected timezone for the query. These queries begin a new run at 12:01AM on the first calendar day of every month for their selected timezone, and should be completed (available to fetch) by 4AM on the first day.

## Required parameters

**API Key**: This should be passed in the HTTP Header with "X-CB-AK". You can create and access your [**API key here**](https://chartbeat.com/publishing/settings/api-keys/) under your account settings. Make sure to select an API key that has access to all calls for your domain.&#x20;

### Example CURL request

`curl --header "X-CB-AK: <YOUR API KEY>" "https://api.chartbeat.com/query/v2/submit/page/?host=YOUR_HOST&limit=100&start=YYYY-MM-DD&end=YYYY-MM-DD&sort_column=page_views&sort_order=desc&tz=America/New_York&metrics=page_views,page_uniques&dimensions=path,title"`

The following parameters are necessary to include in both your one-time *and* recurring queries:

|             |                |                                                                                                                                                                                                                      |
| ----------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| API Key     | `apikey=`      | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/real-time-apis/getting-started-with-our-real-time-api#required-parameters) |
| Endpoint    | `endpoint=`    | Choose between `page` and `video` data. Video data is only available for Sites with active Video Dashboards.                                                                                                         |
| Host        | `host=`        | The Site you are querying (site ID is usually your website's root domain). If you are using our video API endpoint, the host value is your site ID prepended with "video@". For example, host=<video@mysite.com>.    |
| Limit       | `limit=`       | The number of rows you'd like to pull in your query. Large calls are susceptible to 504 gateway timeouts, so we recommend setting no more than 10,000 rows.                                                          |
| Sort Column | `sort_column=` | The column you'd like to sort the query on.                                                                                                                                                                          |
| Sort Order  | `sort_order=`  | The order of the sort (`asc`/`desc`).                                                                                                                                                                                |
| Timezone    | `tz=`          | The timezone you'd like to run the query in a valid Olson timezone format, e.g. America/New\_York.                                                                                                                   |
| Metrics     | `metrics=`     | Every query must include one or more specified metrics (traffic data returned in the report).                                                                                                                        |

{% hint style="info" %}
**Note:** The complete list of available metrics, dimensions, and filter parameters is included in the fourth and final page of this guide: [**Metrics, Dimensions, and Filters**](/cbp/api/historical-api/metrics-dimensions-filters)**.**
{% endhint %}

### Additional required parameters for one-time queries

One-time queries must also include the start and end date parameters:

| Parameter Name | Syntax             | Description                            |
| -------------- | ------------------ | -------------------------------------- |
| Start Date     | `start=yyyy-mm-dd` | Start of date range to pull data from. |
| End Date       | `end=yyyy-mm-dd`   | End of date range to pull data from.   |

### Additional required parameters for recurring queries

Recurring queries do not accept the start and end date parameters, and instead require the date range parameter set to day, week, or month:

| Parameter Name | Syntax        | Description                                                                                             |
| -------------- | ------------- | ------------------------------------------------------------------------------------------------------- |
| Date Range     | `date_range=` | The frequency for your recurring query to run; `day` for daily, `week` for weekly, `month` for monthly. |

## Next steps

Check out the next three pages of this guide to find all the information you need to get started using our Historical API.

{% content-ref url="/pages/-LnOLaH4JADZH3CpGe\_g" %}
[One-time Queries](/cbp/api/historical-api/one-time-queries)
{% endcontent-ref %}

{% content-ref url="/pages/-M2t0s73dfdLNOHU04Io" %}
[Recurring Queries](/cbp/api/historical-api/recurring-queries)
{% endcontent-ref %}

{% content-ref url="/pages/-LnOM7eGo-OdRYNpJWPO" %}
[Metrics, Dimensions, and Filters](/cbp/api/historical-api/metrics-dimensions-filters)
{% endcontent-ref %}


# One-time Queries

Learn how to use our three one-time query endpoints to submit a new query, check the query run status, and fetch the completed query results.

**One-time queries** are good for pulling data over custom date ranges. One-time queries must be submitted anew for every unique run if your team wants to run the same report (using the same metrics, dimensions, and/or filters) over unique date ranges (using unique start and end date parameters).

Getting data back from a one-time query is a three-step process using each of the below one-time API endpoints:

1. Run a [**`/submit/`**](/cbp/api/historical-api/one-time-queries#submit-one-time-query) call to submit your new query and receive a **query ID**.
2. Run a [**`/status/`**](/cbp/api/historical-api/one-time-queries#get-status-of-one-time-query) call to check the status of your submitted query using the **query ID** from your previous call to the `/submit/` endpoint. Continue running as frequently as every ten seconds or so until you receive a `completed` status for your query.
3. Once the status of your query is `completed`, run a [**`/fetch/`**](/cbp/api/historical-api/one-time-queries#fetch-one-time-query) call with the original **query ID** to download your completed query results.

## Submit One-time Query

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/query/v2/submit/`

Submitting a query to the `/submit/` endpoint will initiate your query run. More complex queries may take several minutes to complete. The expected return value of this call is a `query_id` that can be used to both check the status of your query as well as to access the returned data.\
\
The "filter" parameter is a stand-in for the dimension-parameter you want to filter by. To include a filter in your submitted query, use `dimension` as the query param key, and set it to a value (or multiple values) to filter by for that query.\
\
**Note:** Metrics and dimensions should be declared before including filters. They accept a comma separated list of values.

#### Path Parameters

| Name     | Type   | Description                                                                                                                                                                                                                               |
| -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| endpoint | string | Append `page/` or `video/` to the endpoint URL above after `/submit/`, before appending query parameters below. This determines whether your API call will query page or video data. The endpoint query parameter below is also required. |

#### Query Parameters

| Name                | Type    | Description                                                                                                                                                                                                           |
| ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apikey (*see note*) | string  | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/historical-api/getting-started-with-our-historical-api#required-parameters) |
| sort\_column        | string  | **Must match a value in your metrics or dimensions parameter**                                                                                                                                                        |
| sort\_order         | string  | Either `asc` or `desc`                                                                                                                                                                                                |
| start               | string  | YYYY-MM-DD                                                                                                                                                                                                            |
| end                 | string  | YYYY-MM-DD                                                                                                                                                                                                            |
| endpoint            | string  | Either `page` or `video`                                                                                                                                                                                              |
| host                | string  | The site on which you are querying                                                                                                                                                                                    |
| limit               | integer | Base-10 values only                                                                                                                                                                                                   |
| tz                  | string  | The timezone you'd like to run the query in a valid Olson timezone format, e.g. America/New\_York.                                                                                                                    |
| metrics             | string  | Comma-separated values                                                                                                                                                                                                |
| dimensions          | string  | Comma-separated values                                                                                                                                                                                                |
| {filter}=           | string  | `{filter}` is a placeholder value. Choose from our list of filter parameters (see the final page of this guide), and pass in a filter value. For example: `&section=news`                                             |

{% tabs %}
{% tab title="200 Returns a query ID for use in your subsequent calls to the status and fetch endpoints. Example ID:" %}

```scheme
{query_id: "4ed64430-9049-469b-8eee-e38d265017dc"}
```

{% endtab %}
{% endtabs %}

## Get Status of One-time Query

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/query/v2/status/`

Use the `/status/` endpoint to check on the status of your query. If you are programmatically querying Chartbeat data, you should wait until your `status` value returns "completed" before making the fetch call (see below), or you will get an error.\
\
Possible status responses include submitted, running, completed, downloaded, and deleted.

#### Query Parameters

| Name                | Type   | Description                                                                                                                                                                                                           |
| ------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| query\_id           | string | Value returned from call to `/submit/` endpoint                                                                                                                                                                       |
| host                | string | The site you are querying                                                                                                                                                                                             |
| apikey (*see note*) | string | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/historical-api/getting-started-with-our-historical-api#required-parameters) |

{% tabs %}
{% tab title="200 " %}

```scheme
{
    status: "", //submitted, running, completed, downloaded, or deleted 
    metrics: [
        //list of metrics being returned in the query
    ],
    host: "YOURSITE.com",
    query_id: "4ed64430-9049-469b-8eee-e38d265017dc", //<-example query ID
    dimensions: [
        //list of dimensions being returned in the query
    ],
    query_num: null
}

```

{% endtab %}
{% endtabs %}

## Fetch One-time Query

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/query/v2/fetch/`

The `/fetch/` endpoint will return the data for a completed query (default is in .csv format).

#### Query Parameters

| Name                | Type   | Description                                                                                                                                                                                                           |
| ------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query_id`          | string | Value returned from call to `/submit/` endpoint                                                                                                                                                                       |
| `host`              | string | The site you are querying                                                                                                                                                                                             |
| `format`            | string | Set to `json` to return data formatted in JSON instead of CSV                                                                                                                                                         |
| apikey (*see note*) | string | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/historical-api/getting-started-with-our-historical-api#required-parameters) |

{% tabs %}
{% tab title="200 Returns the traffic data from your query!" %}

```
//Will return .csv formatted data, unless providing format=json parameter
```

{% endtab %}
{% endtabs %}


# Recurring Queries

Learn how to use our six recurring query endpoints to submit a new query, check the query run status, fetch query results and manage existing queries.

**Recurring queries** are only submitted once, but they continue to generate new reports for every calendar day, week, or month. This is a good option for teams that want to pull in the same traffic data with every day, week, or month that passes. The query start and end times are defined by the selected date range option:&#x20;

* **Daily** recurring queries (`date_range=day`) return data for the previous 24-hour calendar day based on your selected timezone for the query. These queries begin a new run at 12:01AM each day for their selected timezone, and should be completed (available to fetch) by 4AM.
* **Weekly** recurring queries (`date_range=week`) return data for the previous 7-day calendar week (12:00AM Monday - 11:59PM Sunday) based on your selected timezone for the query. These queries begin a new run at 12:01AM every Monday for their selected timezone, and should be completed (available to fetch) by 4AM Monday.
* **Monthly** recurring queries (`date_range=month`) return data for the previous calendar month based on your selected timezone for the query. These queries begin a new run at 12:01AM on the first calendar day of every month for their selected timezone, and should be completed (available to fetch) by 4AM on the first day.

Getting data back from a recurring query for the first time is a three-step process:

1. Run a [**`/submit/`**](/cbp/api/historical-api/recurring-queries#submit-recurring-query) call to submit your new query and receive a **query ID**.
2. **Wait for your first query run to complete** — 4AM the next day for daily queries, 4AM the next Monday for weekly queries, 4AM the next first calendar month day for monthly queries. THEN, run a [**`/status/`**](/cbp/api/historical-api/recurring-queries#get-status-of-recurring-query) call to check the status of your submitted query using the **query ID** from your previous call to the `/submit/` endpoint. Continue running as frequently as every ten seconds or so until you receive a `completed` status for your query.
3. Once the status of your query is `completed`, run a [**`/fetch/`**](/cbp/api/historical-api/recurring-queries#fetch-recent-recurring-query-run) call with the original **query ID** to download your completed query results. Continue using the same **query ID** to check status and fetch your updated results with every day, week, or month that passes.

## Submit Recurring Query

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/query/v2/recurring/submit/`

The expected return value of this call is a query ID that can be used to both check the status of your query and fetch the results when ready.\
\
The "filter" parameter is simply a stand-in for the value that you want to filter by; select the dimension that you want to filter the data by, and specify the value for that dimension.\
\
**Note:** Metrics and Dimensions should come before Filters in the appended query parameter sequence.

#### Path Parameters

| Name       | Type   | Description                                                                                                                                                                         |
| ---------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `endpoint` | string | Append `page/` or `video/` to the API endpoint URL above after `/submit/`, before attaching the query params below. This determines whether your query accesses page or video data. |

#### Query Parameters

| Name                | Type    | Description                                                                                                                                                                                                                    |
| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| apikey (*see note*) | string  | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/historical-api/getting-started-with-our-historical-api#required-parameters)          |
| `host`              | string  | Site ID for the Site you are querying traffic data                                                                                                                                                                             |
| `limit`             | integer | Base-10 values only                                                                                                                                                                                                            |
| `sort_column`       | string  | Metric or Dimension to sort your queried data by                                                                                                                                                                               |
| `sort_order`        | string  | Either `asc` (ascending) or `desc` (descending) for your sort column                                                                                                                                                           |
| `tz`                | string  | Timezone in a valid Olson timezone format, e.g. America/New\_York                                                                                                                                                              |
| `metrics`           | string  | Comma-separated list of metrics to include in the query results                                                                                                                                                                |
| `date_range`        | string  | Set to `day`, `week`, or `month`                                                                                                                                                                                               |
| `user_id`           | number  | Unique user ID for your personal Chartbeat login (contact <support@chartbeat.com> to obtain)                                                                                                                                   |
| {filter}            | string  | `{filter}` is a placeholder value. Choose from our [list of filter parameters](https://docs.chartbeat.com/cbp/api/historical-api/metrics-dimensions-filters#filters), and pass in a filter value. For example: `&section=news` |
| `dimensions`        | string  | Comma-separated list of dimensions to include in the query results                                                                                                                                                             |
| `email`             | string  | Email address(es) to send recurring query results (comma-delimited)                                                                                                                                                            |

{% tabs %}
{% tab title="200 Returns a query ID for user in subsequent status and fetch calls. Example ID:" %}

```scheme
{query_id: "4ed64430-9049-469b-8eee-e38d265017dc"}
```

{% endtab %}
{% endtabs %}

## Get Status of Recurring Query

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/query/v2/recurring/status/`

Use the `/recurring/status/` endpoint to check on the status of your recurring query. Wait for your query status to return "completed" before making the fetch call. \
\
Run status will display as null, queued, or completed.

#### Query Parameters

| Name                | Type   | Description                                                                                                                                                                                                            |
| ------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query_id`          | string | Your query ID from the original submit call                                                                                                                                                                            |
| `host`              | string | Site ID for the Site of your submit call                                                                                                                                                                               |
| apikey (*see note*) | string | **NOTE**:  Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/historical-api/getting-started-with-our-historical-api#required-parameters) |

{% tabs %}
{% tab title="200 Wait for run\_status of "completed" before making your fetch call." %}

```scheme
{
    status: "active",
    host: "site.com",
    query_id: "fff04f41-f33e-4c93-aa5b-b0677b0703f7",
    run_status: "completed"
}
```

{% endtab %}
{% endtabs %}

## Fetch Recent Recurring Query Run

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/query/v2/recurring/fetch/`

The `/recurring/fetch/` endpoint will download the data for a completed query (default format is a CSV file).

#### Query Parameters

| Name                | Type   | Description                                                                                                                                                                                                           |
| ------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `host`              | string | Site ID for the Site of your submit call                                                                                                                                                                              |
| `query_id`          | string | Query ID from your original submit call                                                                                                                                                                               |
| `format`            | string | Set to `json` to return data in JSON format instead of CSV                                                                                                                                                            |
| apikey (*see note*) | string | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/historical-api/getting-started-with-our-historical-api#required-parameters) |

{% tabs %}
{% tab title="200 Returns traffic data from your query!" %}

```
//Will return .csv formatted data, unless providing format=json parameter
```

{% endtab %}
{% endtabs %}

## Get Previous Recurring Query Runs

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/query/v2/recurring/runs/`

Use this endpoint to return a list of run IDs associated with previous runs for your daily, weekly, or monthly query. To fetch data for a previous run, use the Fetch One-time Query call with your run ID assigned to the `query_id` parameter.

#### Query Parameters

| Name                | Type   | Description                                                                                                                                                                                                           |
| ------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query_id`          | string | Query ID from the original submit call                                                                                                                                                                                |
| `host`              | string | Site ID (usually your site's root domain)                                                                                                                                                                             |
| apikey (*see note*) | string | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/historical-api/getting-started-with-our-historical-api#required-parameters) |

{% tabs %}
{% tab title="200 Example response listing previous run IDs:" %}

```scheme
{
    host: "mysite.com",
    query_id: "10d92ad1-b3ae-4304-9b00-9cebe8e7e88d",
    runs:[
        {
            run_id:"358a1352-8236-4435-ad53-278d0207af3d",
            max_ts: 1591959600
        },
        {
            run_id:"3a6a5258-f3b9-4257-b073-c56cd7e4d711",
            max_ts: 1591873200
        },
        {
            run_id:"c834d709-912f-46b4-88bd-b47385927928",
            max_ts: 1591786800
        },
{...}
```

{% endtab %}
{% endtabs %}

## Get Recurring Queries List

<mark style="color:blue;">`GET`</mark> `https://dashapi.chartbeat.com/query/v2/recurring/list/`

Use the `/recurring/list/` endpoint to view details of all active recurring queries for a given Site on your account.

#### Query Parameters

| Name                | Type   | Description                                                                                                                                                                                                           |
| ------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `host`              | string | Chartbeat Site ID (usually root domain of the site you're querying)                                                                                                                                                   |
| apikey (*see note*) | string | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/historical-api/getting-started-with-our-historical-api#required-parameters) |

{% tabs %}
{% tab title="200 Example response:" %}

```scheme
{
  host: "site.com",
  queries: [{
      status: "active",
      expiration_ts: 1454634551,
      row_limit: 100,
      sort_column: [
        "page_views desc"
      ],
      date_range: "day",
      host: "site.com",
      query_timezone: "America/New_York",
      filters: {
        section: [
          "news"
        ]
      },
      interface: 0,
      next_report_time: 1587700800,
      metrics: [
        "page_views"
      ],
      endpoint: "page",
      user_id: "######",
      dimensions: [
        "user_status",
        "scroll_hist"
      ],
      allow_null: 0,
      query_num: 42,
      query_id: "tCsnemrT-2vQ7-glkA-KwYt-hkLjk3ecXvN0R",
      query_submission_ts: 1423116551,
      query_name: null,
      email_addresses: []
    },
{...}
```

{% endtab %}
{% endtabs %}

## Cancel Recurring Query

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/query/v2/recurring/cancel/`

Use this call to cancel a specific recurring query that you created.

#### Query Parameters

| Name                | Type   | Description                                                                                                                                                                                                           |
| ------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query_id`          | string | Query ID for the query you want to cancel                                                                                                                                                                             |
| `host`              | string | Site ID for the query you are canceling                                                                                                                                                                               |
| apikey (*see note*) | string | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/historical-api/getting-started-with-our-historical-api#required-parameters) |

{% tabs %}
{% tab title="200  Successful cancellation response lists your host and query ID:" %}

```scheme
{
    "host": "mysite.co.uk",
    "query_id": "83ea4ccf-5fea-4695-98b3-2d032ab3c1c7"
}
```

{% endtab %}
{% endtabs %}


# Metrics, Dimensions, and Filters

## Metrics

The Metrics parameter identifies which user engagement data points you want returned in your query. Any given query may include multiple metrics to create comprehensive queries.

{% hint style="success" %}
**Tip:** For each Metric included in your query, there will be a corresponding column for that data point in your returned CSV file.
{% endhint %}

### Page endpoint Metrics

| Metric                   | API Syntax           | Description                                                                                    |
| ------------------------ | -------------------- | ---------------------------------------------------------------------------------------------- |
| Average Scroll           | `page_avg_scroll`    | The average maximum depth that visitors have scrolled to, in pixels, from the top of the page. |
| Average Engaged Time     | `page_avg_time`      | The average amount of time in seconds visitors actively spent on a page.                       |
| Scroll Starts            | `page_scroll_starts` | The percentage of page views where a scroll event occurred, expressed as a decimal.            |
| Total Engaged Time       | `page_total_time`    | The total amount of time in seconds spent actively on a page across sites.                     |
| Uniques                  | `page_uniques`       | A count of the number of unique cookies who visited the page.                                  |
| Page Views               | `page_views`         | A count of the number of page views.                                                           |
| Loyal Visitor Page Views | `page_views_loyal`   | The number of pageviews from visitors who visit your site an average of every other day.       |
| Quality Page Views       | `page_views_quality` | The number of pageviews that received at least 15 seconds of engaged time.                     |

### **Video endpoint Metrics​** <a href="#metrics" id="metrics"></a>

| Metric               | API Syntax        | Description                                                                            |
| -------------------- | ----------------- | -------------------------------------------------------------------------------------- |
| Average Engaged Time | `video_avg_time`  | The average amount of time for which visitors watched a video (in seconds).            |
| Video Loads          | `video_loads`     | A count of the number of times a video loaded.                                         |
| Video Play Rate      | `video_play_rate` | The fraction of loaded videos that were subsequently played. (Video Plays/Video Loads) |
| Video Plays          | `video_plays`     | A count of the number of times a video was played.                                     |

## Dimensions

Use the dimensions to select the grouping criteria for which your metrics are returned, such as time of day, geographic location, and page specific data.

{% hint style="success" %}
**Tip:** Adding Dimensions to your query will return additional *rows*, as each combination of grouping criteria will now be split into a separate row.
{% endhint %}

### Page endpoint Dimensions

| Dimension                | API Syntax           | Description                                                                                                                                                                                                                            |
| ------------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Author                   | `author`             | The author of a page.                                                                                                                                                                                                                  |
| Canonical Referrer       | `canonical_referrer` | A rolled-up view of the referrer domain. For instance, traffic from m.facebook.com and facebook.com both have a canonical referrer of Facebook.                                                                                        |
| City                     | `city`               | The visitor's city.                                                                                                                                                                                                                    |
| Client-side Day          | `client_day`         | The calendar day, according to the timezone set by the visitor.                                                                                                                                                                        |
| Client-side Hour         | `client_hour`        | The hour of the day (0-23), according to the timezone set by the visitor.                                                                                                                                                              |
| Client-side Minute       | `client_minute`      | The minute, according to the timezone set by the visitor.                                                                                                                                                                              |
| Content Type             | `contenttype`        | The content type (e.g. gallery, article) as tagged on [your site](https://docs.chartbeat.com/cbp/tracking/standard-websites/configuration-variables#content-type) or [AMP page](/cbp/tracking/google-amp/amp-configuration-variables). |
| Country                  | `country`            | The visitor's country.                                                                                                                                                                                                                 |
| Device                   | `device`             | The device used (mobile/desktop/tablet).                                                                                                                                                                                               |
| Distribution             | `distribution`       | The distribution channel used (site/FBIA/app).                                                                                                                                                                                         |
| Internal Navigation Path | `internal_path`      | The page path that users clicked away from when they opened a new page. Useful for learning about historical click-through data.                                                                                                       |
| Operating System         | `os`                 | The operating system used.                                                                                                                                                                                                             |
| Page Load Histogram      | `page_load_hist`     | Histogram of the page load time (100 millisecond bins).                                                                                                                                                                                |
| Page Type                | `pagetype`           | The type of page, returned as either Article, LandingPage, or Unknown.                                                                                                                                                                 |
| Page Width Histogram     | `pagewidth_hist`     | Width of the page in pixels (50 pixels bins). Useful when analyzing responsive designs.                                                                                                                                                |
| Page Path                | `path`               | The URL/path of the page.                                                                                                                                                                                                              |
| Publish Date             | `publish_date`       | The date that a piece of content on your site was [first tracked by Chartbeat](https://help.chartbeat.com/hc/en-us/articles/208982958-Real-Time-metrics-glossary#h_8034396542001550335263977).                                         |
| Referrer                 | `referrer`           | The referring domain.                                                                                                                                                                                                                  |
| Referrer Type            | `referrer_type`      | The page referrer type: social, search, direct, internal, and external (aka links).                                                                                                                                                    |
| Region                   | `region`             | The visitor's region (returns states for US)                                                                                                                                                                                           |
| Scroll Depth Histogram   | `scroll_hist`        | Histogram of the number of pixels scrolled (100 pixel bins).                                                                                                                                                                           |
| Section                  | `section`            | The [section of a page](https://docs.chartbeat.com/cbp/tracking/standard-websites/configuration-variables#sections-and-authors) visited.                                                                                               |
| Site Experience          | `site_experience`    | The site experience for the visitor (AMP/Standard). An NA value indicates that the visitor comes from an off-site distribution channel.                                                                                                |
| Sponsor                  | `sponsor`            | The sponsor ID. Note that this is only available to domains that are using the [`sponsorName` config variable.](https://docs.chartbeat.com/cbp/tracking/standard-websites/configuration-variables#sponsor-name)                        |
| Subdomain                | `subdomain`          | The specific subdomain (e.g. blog.chartbeat.com, chartbeat.com.                                                                                                                                                                        |
| Subscriber               | `subscriber`         | The visitor's [subscriber status](https://docs.chartbeat.com/cbp/tracking/standard-websites/subscriber-engagement) (Guest, Registered, Subscribed, Unspecified).                                                                       |
| Title                    | `title`              | Page title.                                                                                                                                                                                                                            |
| Engaged Time Histogram   | `time_hist`          | Histogram of active engaged time (15 second bins).                                                                                                                                                                                     |
| Day                      | `tz_day`             | The calendar day in your time zone.                                                                                                                                                                                                    |
| Hour                     | `tz_hour`            | The hour of the day (0-23) in your time zone. Can be used with Day to get hourly time series.                                                                                                                                          |
| Minute                   | `tz_minute`          | The minute in your time zone. Can be used with Hour to get per-minute time series.                                                                                                                                                     |
| Month                    | `tz_month`           | The month, with day boundaries in your timezone.                                                                                                                                                                                       |
| Day (in UTC)             | `utc_day`            | The calendar day in the UTC time zone.                                                                                                                                                                                                 |
| Hour (in UTC)            | `utc_hour`           | The hour of the day (0-23) in the UTC time zone. Can be used with utc\_day to get hourly time series.                                                                                                                                  |
| Minute (in UTC)          | `utc_minute`         | The minute in the UTC time zone. Use paired with utc\_hour to get per-minute time series.                                                                                                                                              |
| UTM Campaign             | `utm_campaign`       | Identifies a specific promotion/campaign.                                                                                                                                                                                              |
| UTM Content              | `utm_content`        | Identifies what specifically was clicked.                                                                                                                                                                                              |
| UTM Medium               | `utm_medium`         | Identifies the type of link, ‘email’ for example.                                                                                                                                                                                      |
| UTM Source               | `utm_source`         | Identifies to which site a link was posted.                                                                                                                                                                                            |
| UTM Term                 | `utm_term`           | Identifies search term/terms.                                                                                                                                                                                                          |
| Visitor Frequency        | `visit_frequency`    | Returns New, Returning, or Loyal.                                                                                                                                                                                                      |

### **Video endpoint Dimensions** <a href="#metrics" id="metrics"></a>

| Dimension       | API Syntax    | Description                                                                                     |
| --------------- | ------------- | ----------------------------------------------------------------------------------------------- |
| Device          | `device`      | The device used (mobile/desktop/tablet).                                                        |
| Page path       | `page_path`   | The URL/path of the page a video appeared on.                                                   |
| Play state      | `play_state`  | Play state of video (loaded, playing, paused, completed).                                       |
| Day             | `tz_day`      | Play state of video (loaded, playing, paused, completed).                                       |
| Hour            | `tz_hour`     | The hour of day (0-23) in your timezone. Can be used with Day to get hourly timeseries.         |
| Minute          | `tz_minute`   | The minute in your time zone. Use paired with Hour to get per-minute time series.               |
| Month           | `tz_month`    | The month, with day boundaries in your timezone.                                                |
| Day (in UTC)    | `utc_day`     | The calendar day in the UTC time zone.                                                          |
| Hour (in UTC)   | `utc_hour`    | The hour of day (0-23) in the UTC timezone. Can be used with utc\_day to get hourly timeseries. |
| Minute (in UTC) | `utc_minute`  | The minute in the UTC timezone. Use paired with utc\_hour to get per-minute timeseries.         |
| Video path      | `video_path`  | The URL/path of the video.                                                                      |
| Video title     | `video_title` | The title of the video.                                                                         |

## Filters

Filters help narrow down the returned data such that it only pertains to relevant criteria. Use the filter parameter to create queries that reflect specific audience segments.

{% hint style="success" %}
**Tip**: Filters don't impact returned *rows* or *columns*, but do shape the data such that all returned values will meet the filter conditions being set.
{% endhint %}

### Page endpoint Filters

| Filter                   | API Syntax            | Description                                                                                                                                                                                                                      |
| ------------------------ | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Author                   | `author=`             | The author of a page.                                                                                                                                                                                                            |
| Canonical Referrer       | `canonical_referrer=` | A rolled-up view of the referrer domain. For instance, traffic from m.facebook.com and facebook.com both have a canonical referrer of Facebook.                                                                                  |
| City                     | `city`                | The city the visitor was in.                                                                                                                                                                                                     |
| Content type             | `contenttype=`        | The content type (e.g. gallery, article) as [**tagged on your site**.](/cbp/tracking/standard-websites/configuration-variables#content-type)                                                                                     |
| Country                  | `country`             | The country the visitor was in.                                                                                                                                                                                                  |
| Device                   | `device=`             | The device used (`desktop`, `mobile`, `tablet`).                                                                                                                                                                                 |
| Distribution             | `distribution=`       | The distribution channel used (`site`/`FBIA`/`app`).                                                                                                                                                                             |
| Internal Navigation Path | `internal_path=`      | The page path that users clicked off from when they traveled to a new page. Useful for learning about historical click-through data.                                                                                             |
| Page Path                | `path=`               | The reported path of the page (Unique to your domain).                                                                                                                                                                           |
| Page Type                | `pagetype=`           | The type of page. There are currently three types: `Article`, `LandingPage`, and `Unknown`.                                                                                                                                      |
| Publish Date             | `publish_date=`       | The date that a piece of content on your site was first tracked by Chartbeat. Filter must be set as `publish_date=match_query_range`; this will return articles that were published within the start and end date of your query. |
| Referrer                 | `referrer=`           | The referring domain.                                                                                                                                                                                                            |
| Referrer Type            | `referrer_type=`      | The page referrer type: `social`, `search`, `direct`, `internal`, `external`.                                                                                                                                                    |
| Section                  | `section=`            | The section of a site visited (Unique to your domain)                                                                                                                                                                            |
| Site Experience          | `site_experience=`    | The site experience for the visitor (`amp`, `standard`). An `NA` value indicates that the visitor comes from an off-site distribution channel.                                                                                   |
| Sponsor                  | `sponsor=`            | The sponsor ID or name, as tagged on the piece of sponsored content.                                                                                                                                                             |
| Subdomain                | `subdomain=`          | The specific subdomain (Unique to your domain).                                                                                                                                                                                  |
| Subscriber               | `subscriber=`         | The visitor's subscriber status (Guest, Registered, Subscribed, Unspecified).                                                                                                                                                    |
| Title                    | `title=`              | Page title.                                                                                                                                                                                                                      |
| UTM Campaign             | `utm_campaign=`       | Identifies a specific promotion/campaign.                                                                                                                                                                                        |
| UTM Content              | `utm_content=`        | Identifies what specifically was clicked.                                                                                                                                                                                        |
| UTM Medium               | `utm_medium=`         | Identifies the type of link, ‘email’ for example.                                                                                                                                                                                |
| UTM Source               | `utm_source=`         | Identifies to which site a link was posted.                                                                                                                                                                                      |
| UTM Term                 | `utm_term=`           | Identifies search term/terms.                                                                                                                                                                                                    |
| Visitor Frequency        | `visit_frequency=`    | New/Returning/Loyal/Unclassified.                                                                                                                                                                                                |

### **Video endpoint Filters** <a href="#metrics" id="metrics"></a>

| Metric     | API Syntax   | Description                                   |
| ---------- | ------------ | --------------------------------------------- |
| Device     | `device`     | The device used (mobile/desktop/tablet).      |
| Page path  | `page_path`  | The URL/path of the page a video appeared on. |
| Video path | `video_path` | The URL/path of the video.                    |


# Headline Testing API

Client facing API to fetch headline testing data programatically

1. [Getting Started with our Headline Testing API](/cbp/api/headline-testing-api/getting-started-with-our-headline-testing-api)
2. [Raw Data endpoint](/cbp/api/headline-testing-api/raw-data)
3. [Summary Report endpoint](/cbp/api/headline-testing-api/summary-report)
4. [Variant Report endpoint](/cbp/api/headline-testing-api/variant-report)


# Getting Started with our Headline Testing API

Get familiar with our Headline Testing API for fetching headline testing data with specified start and end times.

Our Headline Testing API endpoints can be used to retrieve active and archive headline test data for your site in JSON format programmatically.

{% hint style="info" %}
The API can be accessed by users with "View Only" or "Run Headline Tests" [permissions](https://help.chartbeat.com/hc/en-us/articles/360018183333-Roles-and-permissions-in-Chartbeat) for a site, using an "all" API key.
{% endhint %}

## Required API parameters

**API Key**: To be passed in the HTTP Header with "X-CB-AK". You can create and access your [**API key here**](https://chartbeat.com/publishing/settings/api-keys/) under your account settings. Make sure to select an API key that has access to all calls for your domain.&#x20;

### Example CURL request

`curl --header "X-CB-AK: <YOUR API KEY>" "https://api.chartbeat.com/mab/htapi/v1/experiments/raw_data/?host=YOUR_HOST.com&start=2024-01-01&end=2024-01-01&tz=America/New_York&status=active"`

The following parameters are necessary to include in all queries:

| Parameter Name       | Syntax    | Description                                                                                                                      |
| -------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------- |
| API Key (*see note*) | `apikey=` | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](#required-api-parameters) |
| Host                 | `host=`   | The Site you are querying (site ID is usually your website's root domain).                                                       |
| Start Date           | `start=`  | A valid date string in YYYY-MM-DD format.                                                                                        |
| End Date             | `end=`    | A valid date string in YYYY-MM-DD format.                                                                                        |
| Timezone             | `tz=`     | The timezone you'd like to run the query in a valid Olson timezone format, e.g. America/New\_York.                               |

### Optional parameters

Raw data returns comprehensive details about each headline test.

| Parameter Name | Syntax       | Description                                                                                                                                                 |
| -------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Status         | `status=`    | Status of the headline test. Choose from 'active', 'archived', 'all'. Defaults to 'all'. Note that this parameter is only available for raw data endpoint.  |
| Limit          | `limit=`     | The number of rows you'd like to pull in your query. Large calls are susceptible to 504 gateway timeouts, so we recommend setting no more than 10,000 rows. |
| Test Type      | `test_type=` | Chose from "headline","image","headline\_and\_image". Defaults to "all" which includes all 3.                                                               |

## Next steps

Check out the next three pages of this guide to find all the information you need to get started using our Headline Testing API.

{% content-ref url="/pages/qhwaMg7tYInVyP5lbRoS" %}
[Raw Data](/cbp/api/headline-testing-api/raw-data)
{% endcontent-ref %}

{% content-ref url="/pages/Fa2sByAYYwRvGL0ooWHK" %}
[Summary Report](/cbp/api/headline-testing-api/summary-report)
{% endcontent-ref %}

{% content-ref url="/pages/wJ1lF6K6xJTOY6uARGQa" %}
[Variant Report](/cbp/api/headline-testing-api/variant-report)
{% endcontent-ref %}


# Raw Data

Raw data endpoint is designed to fetch headline testing data with as much detail as possible.

**Raw data queries** are good for pulling data over custom date ranges, to dig deep into granular metrics.

## Get raw data

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/mab/htapi/v1/experiments/raw_data/`

**Note:** The payload can quickly become large, please use date ranges and limit parameter for a good result set.

#### Query Parameters

| Name                                    | Type    | Description                                                                                                                                                                                                 |
| --------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apikey (*see note*)                     | string  | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](/cbp/api/headline-testing-api/getting-started-with-our-headline-testing-api#required-api-parameters) |
| start<mark style="color:red;">\*</mark> | string  | YYYY-MM-DD                                                                                                                                                                                                  |
| end<mark style="color:red;">\*</mark>   | string  | YYYY-MM-DD                                                                                                                                                                                                  |
| host<mark style="color:red;">\*</mark>  | string  | The site on which you are querying                                                                                                                                                                          |
| limit                                   | integer | Base-10 values only                                                                                                                                                                                         |
| tz<mark style="color:red;">\*</mark>    | string  | The timezone you'd like to run the query in a valid Olson timezone format, e.g. America/New\_York.                                                                                                          |
| status                                  | string  | Choices are 'active', 'archived', 'all'. Defaults to 'all'.                                                                                                                                                 |

{% tabs %}
{% tab title="200 Successful response schema" %}

```json
{
  "status": "string",
  "data": [
    {
      "experiment_id": "string",
      "status": "string",
      "type": "string",
      "model": "string",
      "host": "string",
      "domain": "string",
      "path": "string",
      "location": "string",
      "specific_location": "string",
      "exp_metadata": "string",
      "sections": [
        "string"
      ],
      "created_at": "2023-07-21T20:15:05.971Z",
      "archived_at": "2023-07-21T20:15:05.971Z",
      "activated_at": "2023-07-21T20:15:05.971Z",
      "completed_at": "2023-07-21T20:15:05.971Z",
      "total_outcomes": 0,
      "total_failures": 0,
      "outcome_last_received_at": 0,
      "num_outcomes": 0,
      "payout_coefficients": [
        0
      ],
      "priors": [
        0
      ],
      "num_variants": 0,
      "strategy_last_updated_at": 0,
      "winning_variant": "string",
      "convergence_type": "string",
      "value_remaining": "string",
      "valid": "string",
      "user_id": 0,
      "user_email": "string",
      "engagement_mean": 0,
      "engagement_intervals": [
        0
      ],
      "ctr_mean": 0,
      "probability": 0,
      "ctr_intervals": [
        0
      ],
      "engaged_click_intervals": [
        0
      ],
      "engaged_clicks_mean": 0,
      "outcome_counts": [
        0
      ],
      "content_type": "string",
      "content": "string",
      "variant_id": "string",
      "variant_metadata": "string"
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# Summary Report

Summary data endpoint is designed to fetch summary of headline tests ran over a custom time range. Please note that active tests are not included.

**Summary Report queries** are good for pulling data over custom date ranges, to compare across tests.

## Get raw data

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/mab/htapi/v1/experiments/summary_report/`

**Note:** Response payload can quickly become huge, please use date ranges and limit parameter for a good result set.

#### Query Parameters

| Name                                    | Type   | Description                                                                                                                                                                                                 |
| --------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apikey (*see note*)                     | string | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](/cbp/api/headline-testing-api/getting-started-with-our-headline-testing-api#required-api-parameters) |
| start<mark style="color:red;">\*</mark> | string | YYYY-MM-DD                                                                                                                                                                                                  |
| end<mark style="color:red;">\*</mark>   | string | YYYY-MM-DD                                                                                                                                                                                                  |
| host<mark style="color:red;">\*</mark>  | string | The site on which you are querying                                                                                                                                                                          |
| tz<mark style="color:red;">\*</mark>    | string | The timezone you'd like to run the query in a valid Olson timezone format, e.g. America/New\_York.                                                                                                          |
| test\_type                              | string | Chose from "headline","image","headline\_and\_image". Defaults to "all" which includes all 3.                                                                                                               |

{% tabs %}
{% tab title="200 Successful response schema" %}

```json
{
  "status": "string",
  "data": [
    {
      "test_id": "string",
      "test_type": "string",
      "host": "string",
      "domain": "string",
      "path": "string",
      "location": "string",
      "created_at": "2023-07-21T20:21:56.245Z",
      "winner_found_at": "2023-07-21T20:21:56.245Z",
      "stopped_at": "2023-07-21T20:21:56.245Z",
      "num_variants": 0,
      "winning_variant": "string",
      "variant_id": "string",
      "ctr_mean": 0,
      "headline": "string",
      "status": "string",
      "test_duration_sec": 0,
      "sections": "string",
      "created_by": "string"
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# Variant Report

Variant Report data endpoint is designed to fetch summary of variants ran over a custom time range. Please note that active tests are not included.

**Variant report queries** are good for pulling data over custom date ranges, to comparisons across variants, regardless of headline test.<br>

## Get raw data

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/mab/htapi/v1/experiments/variant_report/`

**Note:** The payload can quickly become large, please use date ranges and limit parameter for good result set.

#### Query Parameters

| Name                                    | Type   | Description                                                                                                                                                                                                 |
| --------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| apikey (*see note*)                     | string | **NOTE**: Your API key should no longer be set as a query param; instead, [pass it in the HTTP header](/cbp/api/headline-testing-api/getting-started-with-our-headline-testing-api#required-api-parameters) |
| start<mark style="color:red;">\*</mark> | string | YYYY-MM-DD                                                                                                                                                                                                  |
| end<mark style="color:red;">\*</mark>   | string | YYYY-MM-DD                                                                                                                                                                                                  |
| host<mark style="color:red;">\*</mark>  | string | The site on which you are querying                                                                                                                                                                          |
| tz<mark style="color:red;">\*</mark>    | string | The timezone you'd like to run the query in a valid Olson timezone format, e.g. America/New\_York.                                                                                                          |
| test\_type                              | string | Chose from "headline","image","headline\_and\_image". Defaults to "all" which includes all 3.                                                                                                               |

{% tabs %}
{% tab title="200 Successful response schema" %}

```json
{
  "status": "string",
  "data": {
    "test_id": "string",
    "test_type": "string",
    "created_at": "2023-07-21T20:22:41.589Z",
    "domain": "string",
    "path": "string",
    "headline_id": "string",
    "headline": "string",
    "is_winner": true,
    "views": 0,
    "clicks": 0,
    "engaged_clicks": 0,
    "ctr_mean_percent": 0,
    "winning_variant": "string",
    "status": "string"
  }
}
```

{% endtab %}
{% endtabs %}


# Conversion API

A guide to retrieving your conversion data via our API.

## Articles included in this guide:

1. [Getting Started with our Conversion API](/cbp/api/conversion-api/getting-started-with-our-conversion-api)
2. [Top Articles API](/cbp/api/conversion-api/top-articles)


# Getting Started with our Conversion API

Get familiar with our Conversion API to fetch the top articles that drove a given event.

This section contains information on how to make requests to Chartbeat’s Top Converting Articles API endpoint, allowing you to fetch the top articles that drove a given event (e.g. subscriptions) for a specific time range. Counts are assigned *per attributed path.*&#x20;

For example, you can ask, "What were my top 10 articles over the last 3 hours that drove subscriptions?"

### Retrieving Results for Event Counts

The Top Converting Articles API only provides Last Touch attribution for event categories that include a `Shown` event. For categories that do not include a `Shown` event, all conversions have a null attribution path; this means that you will see a count of total conversions for the selected event category, where conversions includes the total number of `Complete` events for the event category specified.

### Required API Parameters

**API Key**: To be passed in the HTTP Header with "X-CB-AK". You can create and access your [**API key here**](https://chartbeat.com/publishing/settings/api-keys/) under your account settings. Make sure to select an API key that has access to all calls for your domain.&#x20;

#### Example CURL request

`curl --header "X-CB-AK: <YOUR API KEY>" "https://api.chartbeat.com/milkyway_dashboard/v1/top_articles_by_event_type?host=YOUR_HOST.com&start=2024-01-01&end=2024-01-02&tz=America/New_York&limit=10&sort_event_type=paywall_complete"`

| Parameter name       | Syntax             | Description                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| -------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Host name            | `host=`            | The site you are querying (host is usually your website's root domain, e.g. `host=site.com`).                                                                                                                                                                                                                                                                                                                                                          |
| API Key (*see note*) | `apikey=`          | **NOTE**: Your API key should no longer be set as a query param; instead. [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/conversion-api/getting-started-with-our-conversion-api#required-api-parameters)                                                                                                                                                                                                                              |
| Sort by Event Type   | `sort_event_type=` | [Event type](https://docs.chartbeat.com/cbp/feature-integrations/conversion/conversion-events) used to sort articles. Use a combination of the event category name (e.g. `paywall`, `registration,`  `newsletter_signup, download` - *see full list below*) and the action (`shown`, `start`, `complete`, or `cancel`) in the format `categoryname_action`. (e.g.`registration_complete`). **Note that these are case sensitive.** One value accepted. |
| Row limit            | `limit=`           | The maximum number of distinct articles to return.                                                                                                                                                                                                                                                                                                                                                                                                     |

### Optional API Parameters

| Parameter name | Syntax   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Start Date     | `start=` | If using a start/end date, including a timestamp. The format of the timestamp should be [ISO 8601 compliant](https://www.iso.org/iso-8601-date-and-time-format.html) (e.g. `2023-05-11T20:00:00Z`, i.e. a UTC timestamp) or if you want to query in a different time zone (e.g. `2013-05-11T20:00:00+07:00`).                                                                                                                                                                                                                                              |
| End Date       | `end=`   | <p>The API group event counts for articles by the hour. Set this argument to be the ending hour. This is an exclusive bound so the API will return counts at times less than the end parameter. <br><br>If using a start/end date, including a timestamp. The format of the timestamp should be <a href="https://www.iso.org/iso-8601-date-and-time-format.html">ISO 8601 compliant</a> (e.g. <code>2023-05-11T20:00:00Z</code>, i.e. a UTC timestamp) or if you want to query in a different time zone (e.g. <code>2013-05-11T20:00:00+07:00</code>).</p> |

To query for all events received on May 11, 2023 from 20:00:00 to 22:59:59 (i.e. not including events received at 23:00:00 or later), you would set `start=2023-05-11T20:00:00Z&end=2023-05-11T23:00:00Z`.&#x20;

To query data for a single day, you would set `start=2023-05-11T00:00:00Z&end=2023-05-11T23:59:59Z`.


# Top Articles

Retrieve event counts for a specified host and date range. Counts are assigned per attributed path.

## Top Articles

<mark style="color:blue;">`GET`</mark> `https://api.chartbeat.com/milkyway_dashboard/v1/top_articles_by_event_type`

Fetch the top articles that drove a given event type

#### Query Parameters

| Name                                                | Type   | Description                                                                                                                                                                                                                                                                                                                                                                                                             |
| --------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| host<mark style="color:red;">\*</mark>              | String | The site on which you are querying.                                                                                                                                                                                                                                                                                                                                                                                     |
| end                                                 | String | Include timestamp. The format of the timestamp should be ISO8601 compliant (e.g. `2013-05-11T20:00:00Z`) or if you want to query in a different time zone (e.g. `2013-05-11T20:00:00+07:00`).                                                                                                                                                                                                                           |
| start                                               | String | Include timestamp. The format of the timestamp should be ISO8601 compliant (e.g. 2013-05-11T20:00:00Z) or if you want to query in a different time zone (e.g. 2013-05-11T20:00:00+07:00).                                                                                                                                                                                                                               |
| sort\_event\_type<mark style="color:red;">\*</mark> | String | [Event type](https://docs.chartbeat.com/cbp/feature-integrations/conversion/conversion-events) used to sort articles. Use a combination of the event category name (e.g. `paywall`, `registration,`  `newsletter_signup, download` - *see full list below*) and the action (`shown`, `start`, `complete`, or `cancel`) in the format `categoryname_action`. **Note that these are case sensitive.** One value accepted. |
| limit<mark style="color:red;">\*</mark>             | Number | The maximum number of distinct articles to return.                                                                                                                                                                                                                                                                                                                                                                      |
| apikey (*see note*)                                 | String | **NOTE**: Your API key should no longer be set as a query param; instead. [pass it in the HTTP header](https://docs.chartbeat.com/cbp/api/conversion-api/getting-started-with-our-conversion-api#required-api-parameters)                                                                                                                                                                                               |

Available `sort_event_type` values (e.g. `&sort_event_type=survey`)&#x20;

```json
paywall, registration, newsletter_signup, offer, survey, unsubscribe, user_profile_info, download, donation, payment, search, save
```

{% tabs %}
{% tab title="200: OK " %}

```
            {
              "status": "success",
              "data": {
                "top_pages": [
                  {
                    "sort_value": 12,
                    "event_types": {
                      "paywall_complete": 6,
                      "paywall_show ": 27,
                    },
                    "attribution_path": "site.com/myarticle/2"
                  },
                  {
                    "sort_value": 11,
                    "event_types": {
                      "paywall_complete": 1,
                      "paywall_shown": 26
                    },
                    "attribution_path": "site.com/myarticle/1"
                  },
                  {
                    "sort_value": 6,
                    "event_types": {
                      "paywall_complete": 5,
                      "paywall_shown": 33
                    },
                    "attribution_path": "site.com/myarticle/3"
                  }
                ]
              }
            }
```

{% endtab %}
{% endtabs %}


# Data Lab API

A guide to retrieving data with the Data Lab API.

## Articles included in this guide:

1. [Getting Started with our Data Lab API](/cbp/api/data-lab-api/getting-started-with-data-lab-api)
   1. [Required Params](https://docs.chartbeat.com/cbp/api/data-lab-api/getting-started-with-data-lab-api#required-api-parameters)
   2. [Columns](https://docs.chartbeat.com/cbp/api/data-lab-api/getting-started-with-data-lab-api#columns)


# Getting Started with Data Lab API

This section contains information on how to make requests to Chartbeat’s Data Lab API endpoint to return a CSV or JSON gzipped file.

### Required API Parameters

**API Key**: To be passed in the HTTP Header with "X-CB-AK". You can create and access your [**API key here**](https://chartbeat.com/publishing/settings/api-keys/) under your account settings. Make sure to select an API key that has access to all calls for your domain.&#x20;

#### Example CURL request

`curl --header "X-CB-AK: YOUR-API-KEY"`\
`"https://dashapi.chartbeat.com/datalab_api/get_data?host=chartbeat.com&start=2025-01-01T00:00:00&end=2025-01-02T00:00:00&tz=America%2FNew_York&columns=ts_bin,host,path,page_views&serialization=csv" > foo.csv.gz`

**Note**: `> foo.csv.gz` is the name of the gzipped file that will be automatically downloaded when you make this request. For json, use the format `foo.json.gz`&#x20;

The following parameters are necessary to include in the query.

<table><thead><tr><th width="186.19270833333331">Parameter name</th><th width="161.21875">Syntax</th><th>Description</th></tr></thead><tbody><tr><td>API Key</td><td><code>apikey=</code></td><td>Note: Your API key should no longer be set as a query param; instead. <a href="https://docs.chartbeat.com/cbp/api/conversion-api/getting-started-with-our-conversion-api#required-api-parameters">pass it in the HTTP header</a></td></tr><tr><td>Host name</td><td><code>host=</code></td><td>The site dashboard you are querying (this is usually your website's root domain, e.g. <code>host=website.com</code>). Accepts one value. Do not include https or www.</td></tr><tr><td>Start Date/Time</td><td><code>start=</code></td><td>Start date and timestamp of query. Format: <code>YYYY-MM-DDTXX:XX:XX</code>  </td></tr><tr><td>End Date/Time</td><td><code>end=</code></td><td>End date and timestamp of query. Format: <code>YYYY-MM-DDTXX:XX:XX</code></td></tr><tr><td>Timezone</td><td><code>tz=</code></td><td>Timezone (e.g. <code>America/New_York</code>)</td></tr><tr><td>Columns</td><td><code>columns=</code></td><td>A comma-separated list of values to return (e.g. <code>column=ts_bin,page_views,visit_frequency</code>). See list of accepted column options <a href="#columns">below</a>.</td></tr><tr><td>Serialization</td><td><code>serialization=</code></td><td>Format for the returned data. JSON or CSV. (e.g. <code>&#x26;serialization=json</code>). Responses can be downloaded as a gzipped file (i.e. <code>filename.csv.gz</code> or <code>filname.json.gz</code></td></tr></tbody></table>

### Columns

Use the `columns` param and include one or more of the accepted options below in a comma-separated list.&#x20;

<table><thead><tr><th width="222.765625">Column name</th><th>Description</th></tr></thead><tbody><tr><td>browser</td><td>The web browser used by a visitor to access the site (e.g., Chrome, Firefox, Safari).</td></tr><tr><td>content_type</td><td>The content type (e.g. gallery, article), as set in your <a href="https://docs.chartbeat.com/cbp/tracking/standard-websites/configuration-variables#content-type">site</a> or <a href="https://docs.chartbeat.com/cbp/tracking/google-amp/amp-configuration-variables#additional-amp-variables">AMP</a> code via the <code>type</code> or <code>contentType</code> variable. </td></tr><tr><td>country</td><td>The visitor's country.</td></tr><tr><td>device</td><td>The visitor's device (desktop, mobile, or tablet)</td></tr><tr><td>engaged_page_views</td><td>Pageviews that received at least 15 seconds of engaged time.</td></tr><tr><td>engaged_time</td><td>The amount of time in seconds visitors were actively engaged (scrolling/typing/tapping)  on a page.</td></tr><tr><td>host</td><td>The site dashboard name, usually the root domain.</td></tr><tr><td>internal_path</td><td>The internal page path that users clicked away from when they opened a new page on the site.</td></tr><tr><td>loyalty_type</td><td>New, Returning, or Loyal. (Also known as Visitor Frequency)</td></tr><tr><td>os</td><td>The operating system used (e.g. <code>android</code>, <code>ios</code>, <code>chrome</code>)</td></tr><tr><td>page_avg_scroll</td><td>The average maximum depth that visitors have scrolled to, in pixels, from the top of the page.</td></tr><tr><td>page_scroll_starts</td><td>The percentage of page views where a scroll event occurred, expressed as a decimal.</td></tr><tr><td>page_type</td><td>The type of page. Article, LandingPage, or Unknown.</td></tr><tr><td>page_views_loyal</td><td>Pageviews from visitors who visit your site an average of every other day, or a minimum of eight unique days in a 16 day period.</td></tr><tr><td>page_views_quality</td><td>Pageviews that received at least 15 seconds of engaged time.</td></tr><tr><td>page_views</td><td>The number of pages viewed.</td></tr><tr><td>path</td><td>The URL/path of the page.</td></tr><tr><td>pinger_source</td><td>The distribution channel used (Site/App).</td></tr><tr><td>referrer_type</td><td>The page referrer type: social, search, direct, internal, and external (aka links).</td></tr><tr><td>referrer</td><td>The referring domain.</td></tr><tr><td>region</td><td>The visitor's region (returns states for US).</td></tr><tr><td>scroll_hist</td><td>Histogram of the number of pixels scrolled in 100 pixel bins.</td></tr><tr><td>scrolled_page_views</td><td>Count of pageviews where a user has scrolled.</td></tr><tr><td>site_experience</td><td>The site experience for the visitor (AMP/Standard). An NA value indicates that the visitor comes from an off-site distribution channel.</td></tr><tr><td>subdomain</td><td>The specific subdomain (e.g. blog.chartbeat.com, chartbeat.com).</td></tr><tr><td>subscriber_type</td><td>The visitor's <a href="https://docs.chartbeat.com/cbp/tracking/standard-websites/subscriber-engagement">subscriber status</a> (Guest, Registered, Subscribed, Unspecified).</td></tr><tr><td>total_scroll</td><td>Sum of the total number of pixels scrolled.</td></tr><tr><td>ts_bin</td><td>Hourly time bin.</td></tr><tr><td>utm_campaign</td><td>Identifies a specific promotion/campaign.</td></tr><tr><td>utm_content</td><td>Identifies what specifically was clicked.</td></tr><tr><td>utm_medium</td><td>Identifies the type of link, ‘email’ for example.</td></tr><tr><td>utm_source</td><td>Identifies to which site a link was posted.</td></tr><tr><td>utm_term</td><td>Identifies search term/terms.</td></tr><tr><td>visit_frequency</td><td>Number of days out of the last 15 that this user has visited; min 0, max 15. Can be used to calculate a custom "Visitor Frequency" metric.</td></tr></tbody></table>


