Chartbeat: Real-Time Editorial Analytics for Publishers | OpsBlu Docs

Chartbeat: Real-Time Editorial Analytics for Publishers

Implement Chartbeat real-time content analytics with _sf_async_config, headline testing, engaged time tracking, scroll depth measurement, and...

How Chartbeat Works

Chartbeat is a real-time analytics platform built specifically for publishers and editorial teams. While general-purpose analytics tools measure pageviews and sessions, Chartbeat focuses on attention: how long readers actually engage with content, how far they scroll, which headlines draw clicks, and which stories drive readers deeper into the site.

The data collection model centers on a persistent JavaScript ping. After the initial pageview beacon, the Chartbeat script sends a lightweight ping to Chartbeat's servers every 15 seconds while the reader remains on the page and the browser tab is active. Each ping includes the current scroll position, viewport dimensions, and whether the reader is actively engaged (mouse movement, scrolling, or keyboard activity within the last 5 seconds). This is how Chartbeat calculates "engaged time" rather than relying on session duration, which inflates when a reader opens a tab and walks away.

The ping-based architecture means Chartbeat's real-time dashboard updates within seconds. The Heads Up Display (HUD) shows concurrent visitors, top pages, traffic sources, and engagement metrics as they happen. For newsrooms, this is the operational dashboard that editors watch throughout the day to make homepage placement decisions, swap underperforming headlines, and allocate social promotion.

Chartbeat stores historical data for trend analysis, but its primary value is in the present moment. The platform is designed for teams that publish frequently and need to optimize content while it is still receiving peak traffic.

Installing the Chartbeat Script

Chartbeat requires a configuration object (_sf_async_config) defined before the tracking script loads. Add both elements before the closing </body> tag.

<script type="text/javascript">
  var _sf_async_config = _sf_async_config || {};

  /** REQUIRED SETTINGS **/
  _sf_async_config.uid = 12345;                    // Your Chartbeat account ID
  _sf_async_config.domain = 'example.com';         // Your site domain
  _sf_async_config.flickerControl = false;          // Set true for headline testing

  /** CONTENT METADATA **/
  _sf_async_config.authors = 'Jane Martinez';       // Article author(s)
  _sf_async_config.sections = 'Technology,News';    // Content sections
  _sf_async_config.title = document.title;          // Page title

  /** OPTIONAL SETTINGS **/
  _sf_async_config.useCanonical = true;            // Use canonical URL as page identifier
  _sf_async_config.useCanonicalDomain = false;     // Keep the actual domain in reports
  _sf_async_config.noCookies = false;              // Set true to disable cookies
</script>

<script type="text/javascript">
  (function () {
    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);
    }
    var oldonload = window.onload;
    window.onload = (typeof window.onload !== 'function')
      ? loadChartbeat
      : function () { oldonload(); loadChartbeat(); };
  })();
</script>

The uid and domain are required. The content metadata fields (authors, sections, title) power Chartbeat's editorial reporting, letting you filter dashboards by author, section, and individual article.

Loading via Google Tag Manager

Create a Custom HTML tag with both script blocks. Set the trigger to All Pages. Because Chartbeat is typically loaded at the end of the document, configure the tag to fire on DOM Ready or Window Loaded rather than Page View.

<!-- GTM Custom HTML Tag for Chartbeat -->
<script type="text/javascript">
  var _sf_async_config = _sf_async_config || {};
  _sf_async_config.uid = 12345;
  _sf_async_config.domain = 'example.com';
  _sf_async_config.useCanonical = true;
  _sf_async_config.authors = document.querySelector('meta[name="author"]')
    ? document.querySelector('meta[name="author"]').content
    : '';
  _sf_async_config.sections = document.querySelector('meta[name="section"]')
    ? document.querySelector('meta[name="section"]').content
    : '';
</script>

<script type="text/javascript">
  (function () {
    var e = document.createElement('script');
    e.type = 'text/javascript';
    e.async = true;
    e.src = '//static.chartbeat.com/js/chartbeat.js';
    var n = document.getElementsByTagName('script')[0];
    n.parentNode.insertBefore(e, n);
  })();
</script>

Verifying Installation

Open your site and check the Network tab in DevTools. Filter for chartbeat.com. You should see the initial chartbeat.js script load followed by periodic ping requests to ping.chartbeat.net every 15 seconds. Each ping returns a 204 No Content status.

In the Chartbeat dashboard, your site should appear under the real-time HUD within 30 seconds of loading a page.

Core Tracking Features

Engaged Time

Engaged time is Chartbeat's primary metric. It measures seconds of active attention, not just time the page was open. The script determines engagement by detecting user activity: mouse movement, scrolling, typing, or touch events. If the reader stops interacting for 5 seconds, engaged time pauses until activity resumes.

This metric is automatically tracked with no additional code. It appears in the dashboard as average engaged time per page, per article, per author, and per section.

Scroll Depth

Chartbeat measures how far down the page each reader scrolls, reported as a percentage. The dashboard shows a histogram of scroll depth distribution, so you can see that 95% of readers reach the first paragraph but only 40% make it to the conclusion.

Scroll depth is captured every 15 seconds (on each ping) as the reader's current maximum scroll position. No additional code is required.

Recirculation Tracking

Recirculation measures whether readers navigate to another page on your site after reading an article. A high recirculation rate means your content strategy and internal linking are keeping readers engaged across multiple pages.

Chartbeat tracks this automatically by correlating sequential pageviews within a session. The dashboard shows recirculation rate per article, per section, and per referral source. This helps editors identify which content types drive the deepest sessions.

Content Metadata

The metadata you set in _sf_async_config powers Chartbeat's editorial filtering. Set these values dynamically based on the page:

// Dynamic metadata for article pages
_sf_async_config.authors = getArticleAuthors();  // Comma-separated author names
_sf_async_config.sections = getArticleSections(); // Comma-separated sections
_sf_async_config.title = getArticleTitle();
_sf_async_config.type = 'article';                // 'article' or 'landing'

// For non-article pages (homepage, category pages)
_sf_async_config.type = 'landing';

The type field distinguishes article pages from landing pages. Chartbeat reports engagement differently for each: articles emphasize read-through and engaged time, while landing pages emphasize click-through and recirculation.

Headline Testing

Chartbeat's headline testing (part of Chartbeat Heads Up Display) lets editors run A/B tests on article headlines directly from the CMS. The system shows different headlines to different visitors and measures click-through rate from the homepage or section page.

Enabling Headline Testing

Set flickerControl to true in the config to prevent the original headline from flashing before the variant loads:

_sf_async_config.flickerControl = true;

Then load the headline testing script alongside the main Chartbeat script:

<script type="text/javascript">
  (function () {
    var e = document.createElement('script');
    e.type = 'text/javascript';
    e.async = true;
    e.src = '//static.chartbeat.com/js/chartbeat_mab.js';
    var n = document.getElementsByTagName('script')[0];
    n.parentNode.insertBefore(e, n);
  })();
</script>

How It Works

  1. An editor creates 2-4 headline variants in the CMS
  2. Chartbeat rotates headlines for visitors on the homepage or section page
  3. The system measures click-through rate for each variant
  4. After statistical significance is reached, Chartbeat automatically promotes the winning headline

No additional JavaScript is needed beyond the configuration above. Headline variants are managed through the Chartbeat dashboard or CMS integration.

Integration with Other Tools

CMS Integration

Chartbeat provides plugins for major CMS platforms that inject the tracking code and metadata automatically:

  • WordPress: The Chartbeat plugin reads the post author, categories, and title
  • Drupal: Module available that maps Drupal taxonomy to Chartbeat sections
  • Arc Publishing: Native integration built into the Arc CMS
  • Custom CMS: Use meta tags to pass metadata, then read them in the config
<!-- Set metadata in your CMS template -->
<meta name="author" content="Jane Martinez" />
<meta name="section" content="Technology" />
<meta name="cb-type" content="article" />
// Read metadata in the Chartbeat config
_sf_async_config.authors = document.querySelector('meta[name="author"]')?.content || '';
_sf_async_config.sections = document.querySelector('meta[name="section"]')?.content || '';
_sf_async_config.type = document.querySelector('meta[name="cb-type"]')?.content || 'landing';

Chartbeat API

Chartbeat exposes a real-time API for building custom dashboards or feeding data into other systems:

# Get real-time data for a page
curl "https://api.chartbeat.com/live/toppages/v3/?apikey=YOUR_API_KEY&host=example.com&limit=10"
# Get historical engaged time data
curl "https://api.chartbeat.com/historical/traffic/stats/?apikey=YOUR_API_KEY&host=example.com&start=2026-03-01&end=2026-03-04&fields=engaged_time_avg,pageviews"

Forwarding to GA4

Chartbeat and GA4 serve different purposes (real-time editorial vs. long-term audience analysis), but you can correlate data by setting consistent content groupings in both:

// Set Chartbeat metadata
_sf_async_config.sections = 'Technology';
_sf_async_config.authors = 'Jane Martinez';

// Set matching GA4 content group
gtag('set', {
  content_group: 'Technology',
  author: 'Jane Martinez'
});

This lets you compare Chartbeat's engaged time data with GA4's audience and acquisition metrics for the same content segments.

Data Export

Chartbeat provides bulk data export through its Historical API and BigQuery integration. Use these to build custom dashboards in Looker, Tableau, or your own data warehouse. The export includes per-article engaged time, scroll depth, traffic sources, and recirculation metrics at hourly granularity.

Common Errors

Error Cause Fix
Dashboard shows 0 concurrent visitors uid or domain in config does not match the Chartbeat account settings Verify _sf_async_config.uid and _sf_async_config.domain match exactly what is shown in Chartbeat account settings
Pings not firing (no requests to ping.chartbeat.net) Chartbeat script blocked by ad blocker or CSP Add static.chartbeat.com and ping.chartbeat.net to CSP script-src and connect-src directives
Author and section filters show "Unknown" _sf_async_config.authors and _sf_async_config.sections not set or set to empty strings Populate metadata from CMS template variables or meta tags before the Chartbeat script loads
Engaged time shows 0 for most pages _sf_async_config defined after the Chartbeat script loads, so pings fire without config Define the config object before the script tag, not after
Headline test variants not showing flickerControl not set to true, or chartbeat_mab.js not loaded Enable flickerControl and load the MAB script alongside the main Chartbeat script
Scroll depth shows 100% for all visitors Page content is shorter than the viewport, so every visitor technically scrolls to the bottom This is expected for short pages; focus scroll analysis on long-form content
Duplicate pageviews on SPA Chartbeat fires a new pageview on each _sf_async_config change without using the SPA API Use pSUPERFLY.virtualPage() for SPA page transitions instead of reinitializing the config
Data discrepancy between Chartbeat and GA4 Chartbeat measures engaged time (active attention); GA4 measures session duration (total time on page) These metrics are fundamentally different; expect Chartbeat engaged time to be lower than GA4 time-on-page
Real-time dashboard shows stale data Browser or proxy caching the ping responses Pings should return 204 No Content with no-cache headers; verify your CDN is not caching requests to ping.chartbeat.net

SPA (Single-Page Application) Support

For single-page applications, Chartbeat provides a virtual pageview API. Call this on each client-side route change:

// After SPA navigation
if (typeof pSUPERFLY !== 'undefined') {
  pSUPERFLY.virtualPage({
    sections: 'Technology',
    authors: 'Jane Martinez',
    path: '/articles/new-page-slug',
    title: 'New Page Title'
  });
}

In a React application:

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

function useChartbeatTracking(metadata) {
  const location = useLocation();

  useEffect(() => {
    if (typeof pSUPERFLY !== 'undefined') {
      pSUPERFLY.virtualPage({
        sections: metadata.section || '',
        authors: metadata.author || '',
        path: location.pathname,
        title: document.title
      });
    }
  }, [location.pathname]);
}

Performance Considerations

The Chartbeat script is approximately 15 KB gzipped. It loads asynchronously and begins sending pings after the initial pageview. The 15-second ping interval adds approximately 500 bytes of network traffic per ping, which is negligible even on slow mobile connections.

Because the script is loaded at the end of the document (or deferred via window.onload), it has no impact on Largest Contentful Paint or First Contentful Paint. The engagement tracking (mouse/scroll event listeners) runs on the main thread but is debounced to fire only on the 15-second ping cycle, so it does not cause input delay or scroll jank.

The headline testing script (chartbeat_mab.js) adds approximately 8 KB and should load early enough to prevent headline flicker. If flickerControl is enabled, Chartbeat hides headline elements briefly until the variant is applied. Set a fallback timeout to ensure headlines become visible even if the script fails:

/* Chartbeat flicker control: hide headlines initially */
[data-cb-headline] {
  visibility: hidden;
}
// Fallback: show headlines after 2 seconds even if Chartbeat fails
setTimeout(function () {
  var headlines = document.querySelectorAll('[data-cb-headline]');
  headlines.forEach(function (el) { el.style.visibility = 'visible'; });
}, 2000);

For high-traffic publisher sites running Chartbeat across thousands of pages, the primary cost is on Chartbeat's servers, not yours. The ping-based model is specifically designed to be lightweight on the client side while providing rich real-time data to the dashboard.