6 min read

How to Track Pages Per Session in Google Analytics 4

Pages per session tells you how many pages users visit before leaving. In GA4, this metric lives under different names and requires a specific setup to access cleanly. Here's how to measure it and act on the data.

Understanding Pages Per Session in GA4

GA4 tracks page views automatically, but the metric itself appears under a different label than you might expect.

Navigate to the Engagement Report

In the GA4 property, go to Reports > Engagement > Pages and Screens. This is where GA4 surfaces pageview-level data. You'll see a list of pages ranked by event count, not pages per session directly.

javascript
// GA4 automatically tracks page_view events via gtag.js
// No additional code needed — this happens by default
gtag('config', 'GA_MEASUREMENT_ID', {
  'page_path': window.location.pathname
});
GA4's default page tracking configuration

Create a Custom Metric for Pages Per Session

GA4's standard reports show total pageviews, not pageviews per session. To surface this metric directly, create a custom metric in Admin > Custom Definitions > Custom Metrics. Name it 'Pages Per Session' and define it as event_count / sessions.

javascript
// Query GA4 Data API v1 to retrieve pages per session
const query = {
  "property": "properties/PROPERTY_ID",
  "dateRanges": [{
    "startDate": "2024-01-01",
    "endDate": "2024-01-31"
  }],
  "metrics": [{"name": "eventCount"}, {"name": "sessions"}],
  "dimensions": [{"name": "pagePath"}]
};
// Calculate pages per session: eventCount / sessions for each page
Querying GA4 Data API to calculate pages per session by page
Watch out: GA4 counts page_view events, not pageviews. If a user reloads the same page, that's two events. Also, single-page apps may not fire page_view events unless you manually track them.

Tracking Pages Per Session with the Measurement Protocol

If you want direct control over how GA4 counts pages, use the Measurement Protocol to send page_view events explicitly.

Send Page View Events via Measurement Protocol

Use the Google Analytics Measurement Protocol (v2) to send structured page_view events to GA4. This approach works for server-side tracking or hybrid setups. Each event must include a valid session_id to group pages correctly.

javascript
// Send a page_view event via Measurement Protocol v2
const payload = {
  "measurement_id": "G-XXXXXXXXXX",
  "api_secret": "your_api_secret",
  "events": [
    {
      "name": "page_view",
      "params": {
        "page_title": "Homepage",
        "page_location": "https://example.com/",
        "session_id": "session_123"
      }
    }
  ]
};

fetch('https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=your_api_secret', {
  method: 'POST',
  body: JSON.stringify(payload)
});
Sending page_view events server-side via Measurement Protocol

Ensure Session IDs Are Consistent

GA4 groups events into sessions using session_id. Without it, each event is treated independently. Pass the same session_id across all page_view events within a single user session. This ensures pages are counted correctly.

javascript
// Generate and store a session ID to ensure consistency
const getSessionId = () => {
  let sessionId = sessionStorage.getItem('ga4_session_id');
  if (!sessionId) {
    sessionId = `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    sessionStorage.setItem('ga4_session_id', sessionId);
  }
  return sessionId;
};

// Use it when tracking page views
gtag('event', 'page_view', {
  'session_id': getSessionId(),
  'page_path': window.location.pathname
});
Managing session IDs to ensure pages are counted per session
Tip: Session storage clears when the tab closes. For longer-lived sessions (e.g., backend integrations), store session IDs in a database or cache.

Analyzing Pages Per Session in Reports

Once GA4 is tracking pages correctly, extract the metric using the Data API or explore it in reports.

Query Pages Per Session via Data API

Use the GA4 Data API v1 (the modern alternative to Reporting API v4) to pull pages per session by page or traffic source. Create a report that divides total events by unique sessions.

javascript
// GA4 Data API v1 request to calculate pages per session
const request = {
  "property": "properties/PROPERTY_ID",
  "dateRanges": [{"startDate": "30daysAgo", "endDate": "today"}],
  "metrics": [
    {"name": "eventCount"},
    {"name": "sessions"},
    {"name": "engagementRate"}
  ],
  "dimensions": [{"name": "pageTitle"}, {"name": "deviceCategory"}],
  "orderBys": [{"metric": {"metricName": "eventCount"}, "desc": true}],
  "limit": 25
};

// In your Node.js backend:
const analyticsdata = google.analyticsdata('v1beta');
const response = await analyticsdata.properties.runReport({
  property: 'properties/PROPERTY_ID',
  requestBody: request
});

response.data.rows.forEach(row => {
  const eventCount = parseInt(row.metricValues[0].value);
  const sessions = parseInt(row.metricValues[1].value);
  const pagesPerSession = (eventCount / sessions).toFixed(2);
  console.log(`${row.dimensionValues[0].value}: ${pagesPerSession} pages/session`);
});
Using GA4 Data API v1 to calculate and display pages per session

Segment by Traffic Source or Device

Add dimensions like traffic source, device category, or country to see which segments have higher pages per session. This reveals which traffic sources or devices drive deeper engagement.

javascript
// Query pages per session by traffic source
const request = {
  "property": "properties/PROPERTY_ID",
  "dateRanges": [{"startDate": "30daysAgo", "endDate": "today"}],
  "metrics": [{"name": "eventCount"}, {"name": "sessions"}],
  "dimensions": [{"name": "sessionSource"}],
  "orderBys": [{"metric": {"metricName": "eventCount"}, "desc": true}]
};
// High-quality traffic sources will have higher pages per session
// Organic search > Direct > Paid ads (usually)
Comparing pages per session across traffic sources
Watch out: GA4 counts event_count, which includes scroll events, clicks, and other engagement events — not just page_view. If you only want page_view events, filter by eventName = 'page_view'.

Common Pitfalls

  • Confusing eventCount with pageviews — GA4 counts all events, including scrolls, clicks, and video plays. Filter by event_name = 'page_view' if you want pages only.
  • Forgetting to pass session_id when using the Measurement Protocol — without it, GA4 creates a new session for each event, inflating pages per session.
  • Single-page apps not firing page_view events — SPAs don't automatically trigger page_view on route changes. Use gtag('event', 'page_view') manually when the route changes.
  • Using old Reporting API v4 instead of Data API v1 — the newer API is cleaner, faster, and aligned with GA4's data model. Start with Data API v1 for new projects.

Wrapping Up

Pages per session reveals which parts of your site keep users engaged. Track it via GA4's automatic page_view events or the Data API, segment by traffic source and device, and use it to identify high-friction pages. If you want to track this automatically across tools, Product Analyst can help.

Track these metrics automatically

Product Analyst connects to your stack and surfaces the insights that matter.

Try Product Analyst — Free