Twig API
Everything is under craft.craftAnalytics. It's all read-only and all aggregate - there is no method that can return anything about an individual, because there's nothing about an individual to return. You can't accidentally leak what nobody's holding.
Most read entries
The one everybody wants:
<h2>Most read this month</h2>
<ul>
{% for entry in craft.craftAnalytics.popularEntries(5).all() %}
<li>
<a href="{{ entry.url }}">{{ entry.title }}</a>
<span>{{ craft.craftAnalytics.views(entry)|number_format }} views</span>
</li>
{% endfor %}
</ul>popularEntries() gives you an entry query, not an array, so carry on refining it:
{# Most read blog posts of the last week #}
{% set popular = craft.craftAnalytics.popularEntries(5, '7d')
.section('blog')
.all() %}It stays in view order through .all(). Adding .section() filters the list without silently re-sorting it by post date, which is the thing you'd want and the thing that usually doesn't happen.
Signature: popularEntries(limit = 5, period = '30d', siteId = null)
Periods are today, yesterday, 7d, 30d, 90d, 12mo.
TIP
It over-fetches internally, because some of the most-viewed elements will since have been unpublished. Asking for 5 gives you 5 where 5 exist, rather than 3 with no explanation.
Views for one entry
{{ craft.craftAnalytics.views(entry)|number_format }} views
{{ craft.craftAnalytics.views(entry, '7d') }} views this week
{{ craft.craftAnalytics.views(1234) }} {# by ID #}Site totals
{% set stats = craft.craftAnalytics.totals() %}
{{ stats.views|number_format }} views
{{ stats.uniques|number_format }} visitors
{{ stats.sessions|number_format }} visits
{{ stats.bounceRate|number_format(1) }}% bouncedAvailable: views, uniques, sessions, bounceRate, avgDurationMs, avgViewsPerSession, avgDwellMs.
Signature: totals(siteId = null, period = '30d')
WARNING
uniques is on a daily-unique basis - somebody who visits on three days counts three times - and it's an estimate, accurate to about 1.6%. Both are deliberate. See How visitors are counted if you're about to put this number somewhere it matters.
Popular paths
When you want paths rather than elements:
{% for page in craft.craftAnalytics.popularPages(10, '7d') %}
{{ page.path }} - {{ page.views }} views
{% endfor %}Each row has path, views, elementId, entrances, exits, bounces, bounceRate, avgDwellMs.
Has this visitor opted out?
For suppressing your own consent banner:
{% if not craft.craftAnalytics.gpcDetected %}
{% include '_partials/cookie-banner' %}
{% endif %}gpcDetected is true when the browser sent Sec-GPC: 1. Those visitors are never tracked beyond the anonymous tier no matter what any consent tool says, so there's nothing to ask them.
Custom events from JavaScript
craftAnalytics.event('newsletter-signup');
craftAnalytics.event('quote-requested', { value: 250 });Then make a goal of type Event with target newsletter-signup.
Custom events from PHP
For things that happen on a POST, where there's no pageview to hang them off:
use coyshdigital\craftanalytics\Plugin;
Plugin::getInstance()->getCapture()->trackEvent('quote-requested', 250.0);Returns false if nothing was recorded, which is the normal answer on Lite, with events off, or for a visitor sending GPC. It respects every privacy rule the rest of the plugin does - your code can't opt someone in.
Caching your own analytics-driven markup
If you're rendering "most read" on every page load, cache it. The queries are cheap but they're not free, and this changes about as often as you'd expect:
{% cache for 1 hour %}
{% for entry in craft.craftAnalytics.popularEntries(5).all() %}
…
{% endfor %}
{% endcache %}