Tutorial: See Page Visits by Page in Near Real Time

Tutorial: See Page Visits by Page in Near Real Time

This tutorial shows UltraCart merchants how to monitor page activity by page URL using two approaches:

  1. Near-real-time reporting with the uc_analytics_session_streaming table

  2. A lightweight live visitor indicator script for approximate current page activity

The reporting approach is the best fit for saved reports, analysis, and dashboards. The live script approach is best when you want an on-page or admin-facing indicator that shows how many visitors are currently active on a page.

UltraCart’s reporting tools include AI Reports and custom reporting workflows under Operations → Reporting, and the AI Report Builder can generate SQL, parameters, sample data, and visualizations from natural-language prompts.


Overview

Merchants often ask a version of this question:

How do I see page visits by page in real time?

There is not a single prepared report described in the supplied source material that acts as a literal second-by-second “who is on this page right now” monitor. The best UltraCart-native path is to build a custom report using the streaming analytics dataset. The AI Report Builder is designed for exactly this kind of need: you can ask a question in plain English, review the generated SQL, inspect parameters like @start_date and @end_date, preview sample data, and save the report for later reuse. ucdoc-AI-Powered Report Builder…

For merchants who need a more immediate “current visitors on this page” approximation, you can also add a small custom heartbeat script to the storefront. That script is separate from UltraCart’s saved reporting flow and should be treated as a custom live-presence feature rather than authoritative analytics.

What You'll Need

Prerequisite: You must have access to your UltraCart account and the appropriate reporting permissions. The AI Report Builder documentation states that access to Data Warehouse data requires the account owner or granted access, and that the email used for access must be a Google account. ucdoc-AI-Powered Report Builder…

Before you begin, make sure you have:

  • Access to Operations → Reporting

  • Permission to use AI Reports / Build Custom Reports

  • Access to the ultracart_dw_streaming dataset if you want to query uc_analytics_session_streaming

  • A developer resource, webhook endpoint, or small backend service if you want to implement the live visitor indicator script

  • A clear understanding that the live script is approximate and not a replacement for reporting-grade analytics

When to Use Each Method

Use the streaming analytics report when you need:

  • Page views by full URL

  • Unique visitors by page

  • New versus returning visitor breakdowns

  • Saved reports and reusable analysis

  • Dashboards and AI-powered summaries

  • Historical and near-real-time trend reporting

Use the live script when you need:

  • An approximate “visitors active now” count

  • A badge on the storefront or a private admin panel

  • A lightweight live-presence indicator

  • A custom real-time merchandising or monitoring aid

Note: The reporting query is the better source of truth for analytics. The live script is a convenience feature for presence monitoring.

Step 1: Open the Reporting Area

  1. Log in to your UltraCart account.

  2. Go to Operations → Reporting.

  3. Review the available reporting options, including Available Reports, AI Reports, Reports Dashboards, Schedule Reports, and Report Pickup. The Reporting documentation describes these tools and explains that AI Reports are used to build custom reports and charts, while Report Pickup is used for asynchronously generated reports.

image-20260317-135836.png

 

Step 2: Use AI Report Builder to Create a Page Visits Report

image-20260317-135727.png

 

The AI Report Builder documentation describes the workflow as:

  1. Navigate to Operations → Reporting

  2. Click Build Custom Reports

  3. Click Create New Report

  4. Type a natural-language question

  5. Review the SQL, parameters, sample data, and visualization

  6. Save the report with a name and optional group for reuse ucdoc-AI-Powered Report Builder…

Follow these steps:

  1. In Operations → Reporting, click Build Custom Reports.

  2. Click Create New Report.

  3. In the prompt box, ask for the report in plain English.

Example prompt:

Show total page views and unique visitors by page URL using the streaming analytics sessions table for the selected date range. Include average views per visitor, new visitor views, returning visitor views, first visit date, and last visit date. Sort by total page views descending.
  1. Review the generated SQL.

  2. Review the parameters panel.

  3. Check the sample data preview.

  4. Review or request a visualization.

  5. Save the report.

image-20260317-140132.png


Tip: The AI Report Builder shows the raw SQL it generated and supports reuse in other tools like BigQuery. It also supports chart requests such as bar, line, pie, map, and other common visualizations. ucdoc-AI-Powered Report Builder…

Step 3: Use the Streaming Analytics Query

If the merchant has access to the streaming analytics dataset, use the following query.

-- =========================================== -- STOREFRONT PAGE VISITS ANALYTICS REPORT -- =========================================== -- Purpose: Analyze visitor traffic by page URL to understand which pages are most popular -- and track visitor engagement patterns across the storefront SELECT -- PAGE IDENTIFICATION hit.page_view.url AS page_url, -- VISIT METRICS COUNT(*) AS total_page_views, COUNT(DISTINCT s.client_id) AS unique_visitors, -- ENGAGEMENT QUALITY METRICS ROUND(COUNT(*) * 1.0 / COUNT(DISTINCT s.client_id), 2) AS avg_views_per_visitor, -- VISITOR TYPE BREAKDOWN COUNTIF(s.new_visitor = TRUE) AS new_visitor_views, COUNTIF(s.new_visitor = FALSE) AS returning_visitor_views, -- TIME ANALYSIS DATE(DATETIME(TIMESTAMP(MIN(hit.ts)), 'America/New_York')) AS first_visit_date, DATE(DATETIME(TIMESTAMP(MAX(hit.ts)), 'America/New_York')) AS last_visit_date FROM `ultracart-dw-demo.ultracart_dw_streaming.uc_analytics_session_streaming` s, UNNEST(hits) AS hit WHERE session_dts BETWEEN @start_date AND @end_date AND partition_date >= DATE_TRUNC(DATE_SUB(@start_date, INTERVAL 1 MONTH), WEEK(SUNDAY)) AND partition_date <= DATE_TRUNC(DATE_ADD(@end_date, INTERVAL 1 MONTH), WEEK(SUNDAY)) AND hit.page_view.url IS NOT NULL GROUP BY page_url ORDER BY total_page_views DESC

Parameters

Use these parameters when running the report:

Name

Type

Required

Description

Name

Type

Required

Description

@start_date

DATETIME

Yes

Beginning of the reporting window

@end_date

DATETIME

Yes

End of the reporting window

Example values:

{ "start_date": "2026-03-01 00:00:00", "end_date": "2026-03-17 23:59:59" }

What the report returns

Field

Description

Field

Description

page_url

Full URL of the page viewed

total_page_views

Total number of page-view events

unique_visitors

Distinct visitor count by client_id

avg_views_per_visitor

Average repeat-view rate

new_visitor_views

Page views from new visitors

returning_visitor_views

Page views from returning visitors

first_visit_date

First date that page was viewed in the selected period

last_visit_date

Most recent date that page was viewed in the selected period

Step 4: Save and Organize the Report

Once the query looks correct:

  1. Click Save.

  2. Enter a descriptive Report Name.

  3. Optionally enter a Group Name to organize related reports.

  4. Reopen the report later in the Report Viewer for refreshed analysis.

The AI Report Builder documentation recommends consistent naming conventions and explains that saved reports can be revisited for updated visualizations, SQL review, and AI analysis. ucdoc-AI-Powered Report Builder…

Suggested names:

  • Storefront Page Visits by URL

  • Top Landing Pages - Streaming

  • Page Traffic by URL - Near Real Time

 

Step 5: Handle Permission Issues

The merchant’s stated access issue is:

The streaming analytics dataset requires additional permissions.

The needed resolution is:

Request access to the ultracart_dw_streaming dataset for analytics reporting.

What to do

  1. Confirm the user has access to Operations → Reporting.

  2. Confirm the user can access AI Reports or custom reporting.

  3. If the query fails due to dataset permissions, request access to the streaming dataset from the UltraCart account owner or administrator.

  4. Make sure the approved email account used for BigQuery access is a Google account, as noted in the AI Report Builder documentation. ucdoc-AI-Powered Report Builder…

Warning: Without the required dataset permission, the report may build successfully in concept but fail when run against the streaming analytics table.

Step 6: Add a Live "Visitors Active Now" Indicator

If the merchant wants a near real-time indicator showing how many visitors are currently active on a page, there are two recommended no-server options that are free-tier eligible and require no infrastructure setup.

Note: This feature provides an approximate live visitor count and is not a replacement for analytics reporting. For reporting, continue using the streaming analytics report.

Choosing the Right Option

Option

Setup Complexity

Persistence

Best For

Option

Setup Complexity

Persistence

Best For

Firebase Realtime DB

Very Low

Yes

No-server, quick deploy

Cloudflare Workers

Low (serverless)

Yes

Global edge, free tier

In-Memory Node.js

Medium (server req.)

No

Testing / internal use only

SQLite Node.js

Medium (server req.)

Yes

Single-server deployment

Firebase and Cloudflare are the recommended paths for most UltraCart merchants. The Node.js options are listed for reference only and are not covered in this tutorial.


Option A: Firebase Realtime Database (Recommended — No Backend Required)

Firebase Realtime Database is a cloud-hosted NoSQL database from Google. It has built-in presence APIs, a generous free tier (the Spark plan), and requires no backend server whatsoever — the storefront script communicates directly with Firebase.

When to Use

  • No developer or server infrastructure available

  • Quick deployment needed

  • Small to medium storefronts

  • Already using Google services

Limitations

  • Requires a free Firebase (Google) account

  • Visitor counts are visible in the Firebase console, not UltraCart

  • Free tier limits apply (generous for this use case: 1 GB storage, 10 GB/month transfer)

Setup: Create a Firebase Project

  1. Go to console.firebase.google.com and sign in with your Google account.

  2. Click Add project and follow the prompts (no need to enable Google Analytics).

  3. In the left sidebar, click Build → Realtime Database.

  4. Click Create Database, choose a region, and start in test mode.

  5. Copy your database URL (looks like: https://your-project-default-rtdb.firebaseio.com).

  6. In Project Settings → General, scroll to Your apps, click the web icon (</>), register your app, and copy the firebaseConfig object.

Security Rules

In the Firebase console under Realtime Database → Rules, set the following to allow the presence script to read and write:

json

{ "rules": { "presence": { ".read": true, ".write": true } } }

Note: Test mode rules expire after 30 days. Replace them with the rules above before launch to maintain access.

Storefront Script (No Backend Required)

Add the following to your UltraCart storefront. Replace the firebaseConfig values with your own from the Firebase console.

html

<div id="uc-live-visitors"></div> <script type="module"> import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.0/firebase-app.js"; import { getDatabase, ref, set, onDisconnect, onValue } from "https://www.gstatic.com/firebasejs/10.12.0/firebase-database.js"; // Replace with your Firebase project config const firebaseConfig = { apiKey: "YOUR_API_KEY", authDomain: "YOUR_PROJECT.firebaseapp.com", databaseURL: "https://YOUR_PROJECT-default-rtdb.firebaseio.com", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_PROJECT.appspot.com", messagingSenderId: "YOUR_SENDER_ID", appId: "YOUR_APP_ID" }; const app = initializeApp(firebaseConfig); const db = getDatabase(app); // Stable session ID stored in localStorage const STORAGE_KEY = "uc_live_presence_id"; let sessionId = localStorage.getItem(STORAGE_KEY); if (!sessionId) { sessionId = "vp_" + Math.random().toString(36).slice(2) + Date.now(); localStorage.setItem(STORAGE_KEY, sessionId); } // Encode page path (Firebase keys cannot contain dots or slashes) const pagePath = btoa(window.location.pathname + window.location.search).replace(/=/g, ""); const presenceRef = ref(db, `presence/${pagePath}/${sessionId}`); // Write presence; auto-remove when visitor leaves or tab closes set(presenceRef, { active: true, ts: Date.now() }); onDisconnect(presenceRef).remove(); // Listen for live visitor count on this page onValue(ref(db, `presence/${pagePath}`), (snap) => { const count = snap.exists() ? Object.keys(snap.val()).length : 0; const el = document.getElementById("uc-live-visitors"); if (el) el.textContent = count + " visitors active now"; }); </script>

Why Firebase Works Without a Backend

Firebase's onDisconnect() API automatically removes a visitor's entry when their browser tab closes or they navigate away — no server-side cleanup job required. This makes it inherently more accurate than a heartbeat-based approach and requires zero maintenance.


Option B: Cloudflare Workers + KV (Serverless)

Cloudflare Workers is a serverless edge computing platform. Your code runs on Cloudflare's global network with no server to provision or maintain. The free tier includes 100,000 requests per day, which is sufficient for most storefronts.

When to Use

  • Prefer a self-contained solution with full control over the logic

  • Want global low-latency delivery (Cloudflare's edge network)

  • Already using Cloudflare for DNS or CDN

  • Need more customization than Firebase allows

Limitations

  • Requires a free Cloudflare account and brief CLI setup

  • Slightly more technical than Firebase (requires running one terminal command)

  • KV storage has eventual consistency (fine for presence monitoring)

Setup: Deploy the Worker

  1. Create a free account at cloudflare.com.

  2. Install Wrangler (the Cloudflare CLI): npm install -g wrangler

  3. Log in: wrangler login

  4. Create a new Worker project: wrangler init uc-presence

  5. Create a KV namespace: wrangler kv:namespace create PRESENCE

  6. Copy the KV namespace ID shown in the output.

  7. Replace the contents of wrangler.toml with the configuration below.

  8. Replace src/index.js with the Worker script below.

  9. Deploy: wrangler deploy

wrangler.toml Configuration

toml

name = "uc-presence" main = "src/index.js" compatibility_date = "2024-01-01" [[kv_namespaces]] binding = "PRESENCE" id = "YOUR_KV_NAMESPACE_ID" # paste from step 5 above

Worker Script (src/index.js)

javascript

export default { async fetch(request, env) { const url = new URL(request.url); const origin = request.headers.get("Origin") || "*"; const corsHeaders = { "Access-Control-Allow-Origin": origin, "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type", }; // Handle CORS preflight if (request.method === "OPTIONS") return new Response(null, { headers: corsHeaders }); if (request.method !== "POST") return new Response("Method not allowed", { status: 405 }); const { session_id, page_url } = await request.json(); if (!session_id || !page_url) return new Response("Missing fields", { status: 400 }); const ACTIVE_WINDOW = 60; // seconds const now = Math.floor(Date.now() / 1000); const key = `${encodeURIComponent(page_url)}:${session_id}`; // Store this session (TTL auto-expires stale sessions) await env.PRESENCE.put(key, now.toString(), { expirationTtl: ACTIVE_WINDOW }); // List all active sessions for this page const prefix = encodeURIComponent(page_url) + ":"; const listed = await env.PRESENCE.list({ prefix }); const active = listed.keys.length; return new Response( JSON.stringify({ page_url, active_visitors: active }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } };

Frontend Script for Cloudflare Option

Add the following to your UltraCart storefront. Replace the endpoint URL with your deployed Worker URL.

html

<div id="uc-live-visitors"></div> <script> (function () { const ENDPOINT = "https://uc-presence.YOUR_SUBDOMAIN.workers.dev/presence"; const PAGE_URL = window.location.pathname + window.location.search; const STORAGE_KEY = "uc_live_presence_id"; let sessionId = localStorage.getItem(STORAGE_KEY); if (!sessionId) { sessionId = "vp_" + Math.random().toString(36).slice(2) + Date.now(); localStorage.setItem(STORAGE_KEY, sessionId); } async function sendHeartbeat() { try { const res = await fetch(ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ session_id: sessionId, page_url: PAGE_URL }) }); const data = await res.json(); const el = document.getElementById("uc-live-visitors"); if (el && typeof data.active_visitors === "number") el.textContent = data.active_visitors + " visitors active now"; } catch (e) { console.error("Presence heartbeat failed", e); } } sendHeartbeat(); setInterval(sendHeartbeat, 20000); // every 20 seconds })(); </script>

Why Cloudflare Uses TTL Instead of Cleanup Jobs

Cloudflare KV supports native key expiration (expirationTtl). Each heartbeat refreshes the 60-second TTL. If a visitor stops sending heartbeats (tab closed, navigated away), the key expires automatically — no cleanup script needed.

Step 7: Decide Where the Live Indicator Should Appear

There are two common patterns:

Public storefront indicator

Show a message such as:

  • 3 visitors active now

  • 12 shoppers are currently viewing this page

Use this carefully. It may be useful for promotions or urgency messaging, but it should be approximate and should not mislead shoppers.

Private admin dashboard indicator

Send the counts to an internal dashboard or lightweight reporting view for the merchant team. This is usually the safer and more useful option.

Tip: For most merchants, a private dashboard is a better fit than showing live presence publicly on the storefront.


Troubleshooting

The report builder opens, but the query fails The most likely cause is missing access to the ultracart_dw_streaming dataset. Request the required dataset permission from the account owner or UltraCart administrator.

The merchant wants "real time," but the report is delayed Use the saved streaming report for analytics and supplement it with the live visitor script for a near-live page indicator.

The live visitor count looks too high (Firebase) Check for duplicate tabs sharing the same localStorage session ID. The Firebase option handles this correctly by design since each tab writes to the same key. If counts are still high, check whether test data or bots are inflating counts.

The live visitor count looks too high (Cloudflare) Check for duplicate tabs, short session IDs, or a stale TTL window that is set too long. Reduce the ACTIVE_WINDOW value in the Worker script and redeploy.

The live visitor count looks too low For the Cloudflare option, make sure the heartbeat interval is firing consistently (every 20 seconds) and that the TTL is long enough (60 seconds minimum). For the Firebase option, confirm onDisconnect is not removing entries prematurely due to unstable connections.

Firebase script shows 'Permission denied' Your Firebase Security Rules have expired (test mode lasts 30 days) or were not set correctly. Update the Realtime Database rules in the Firebase console to match the rules shown in Step 6.

Cloudflare Worker returns CORS errors Make sure your Worker is deployed and the ENDPOINT URL in the frontend script matches your actual Worker URL exactly. Verify the corsHeaders in the Worker include your storefront domain.

The merchant wants a prepared built-in report instead The UltraCart reporting documentation does not describe a built-in prepared report for literal real-time page presence. AI Reports are the better fit for this use case.

Best Practices

  • Use the streaming analytics report for reporting-grade analysis

  • Use the live script only for approximate presence monitoring

  • Save the report with a clear, reusable name

  • Add a visualization request in AI Report Builder (bar chart of top pages by total views, line chart of page views over time, table sorted by unique visitors)

  • Keep live heartbeat windows short — typically 60 seconds

  • Treat live counts as approximations, not exact analytics totals

  • Label public-facing indicators as "Active now" or "Visitors in the last minute" to set expectations

  • Use the Firebase option if you want zero backend maintenance

  • Use the Cloudflare option if you want more control or are already on Cloudflare's network


Expected Outcome

After completing this tutorial, the merchant should have:

  • A saved UltraCart custom report that shows page visits by page URL using streaming analytics

  • A clear path to resolve access issues for ultracart_dw_streaming

  • An optional live visitor indicator using either Firebase (zero backend) or Cloudflare Workers (serverless edge)

  • A strong understanding of when to use reporting versus live presence monitoring


Conclusion

For UltraCart merchants who want to see page visits by page in near real time, the best starting point is a custom report built against uc_analytics_session_streaming. UltraCart’s AI Report Builder is well suited for this workflow because it can generate the SQL, show parameters, preview data, and save the report for reuse. ucdoc-AI-Powered Report Builder…

If the merchant also wants a simple “how many people are on this page right now?” indicator, add a lightweight heartbeat script and backend endpoint. That gives you a practical live-presence display, while the streaming report remains your authoritative analytics tool.