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.
// 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
});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.
// 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 pagepage_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.
// 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)
});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.
// 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
});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.
// 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`);
});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.
// 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)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.