Google Analytics 4 doesn't expose session duration the same way Universal Analytics did, which trips up analysts moving from GA3. You can calculate it three ways: through the Data API, BigQuery SQL, or from raw event timestamps. Here's how to get that metric regardless of your setup.
Understanding GA4 Sessions
GA4 sessions are the foundation for calculating duration. Unlike Universal Analytics, GA4 ties everything to a session_id and session number parameter.
Know when GA4 creates and ends sessions
GA4 creates a new session when a user visits your site for the first time or after 30 minutes of inactivity (the default timeout). Sessions end when the user leaves your site or the timeout is hit. The session_id parameter in your events table is what ties all user actions to a single session.
// In your gtag configuration, GA4 automatically creates session_id
// You don't need to manually set it, but you can view it in the DebugView
gtag('config', 'GA_MEASUREMENT_ID', {
'session_timeout': 1800000 // 30 minutes in milliseconds (default)
});
// The session_id is automatically attached to all events
// You'll see it in real-time debug view under session_id parameterAccess session metrics in the GA4 UI
In your GA4 property, navigate to Reports > Engagement to see Average Session Duration and Session Duration metrics. This is the average time per session, not individual session durations. If you need to drill down into specific sessions, you'll need the Data API or BigQuery.
// The Data API returns session duration as a metric
// Install the GA4 Data API client: npm install @google-analytics/data
const analyticsdata = require('@google-analytics/data');
const client = new analyticsdata.BetaAnalyticsDataClient();
const request = {
property: 'properties/YOUR_PROPERTY_ID',
dateRanges: [
{
startDate: '2024-01-01',
endDate: '2024-01-31'
}
],
metrics: [
{
name: 'averageSessionDuration'
}
]
};
const [response] = await client.runReport(request);
console.log(response.rows[0].metricValues[0].value);Extract Session Duration via Google Analytics Data API
The Data API is the quickest way to pull session metrics programmatically. It returns aggregated data, perfect for dashboards and reports.
Set up the Google Analytics Data API client
Install the official Node.js client library and authenticate with a service account. You'll need a service account JSON file from your Google Cloud project that has access to your GA4 property.
npm install @google-analytics/data
// Load your service account credentials
const fs = require('fs');
const serviceAccount = JSON.parse(
fs.readFileSync('./path-to-service-account.json')
);
const analyticsdata = require('@google-analytics/data');
const client = new analyticsdata.BetaAnalyticsDataClient({
credentials: serviceAccount,
projectId: serviceAccount.project_id
});
module.exports = client;Query average session duration for a date range
Use the runReport method to fetch averageSessionDuration (in seconds) grouped by date or other dimensions. This gives you the mean session duration across all sessions in your query.
const client = require('./ga4-client'); // from previous step
const request = {
property: 'properties/123456789', // Replace with your GA4 Property ID
dateRanges: [
{
startDate: '2024-01-01',
endDate: '2024-01-31'
}
],
metrics: [
{
name: 'averageSessionDuration' // Returns seconds
},
{
name: 'sessions'
}
],
dimensions: [
{
name: 'date'
}
]
};
const [response] = await client.runReport(request);
response.rows.forEach((row) => {
const date = row.dimensionValues[0].value;
const avgDuration = row.metricValues[0].value; // in seconds
const sessions = row.metricValues[1].value;
console.log(`${date}: ${avgDuration}s avg (${sessions} sessions)`);
});Calculate Session Duration via BigQuery SQL
BigQuery gives you row-level event data, so you can calculate exact session duration per session. This requires GA4 → BigQuery export to be enabled.
Enable GA4 → BigQuery export
In your GA4 property, go to Admin > BigQuery Links and set up a link to your BigQuery project. GA4 will export raw events daily to a dataset like analytics_XXXXXXX. This takes a few hours to populate.
// After GA4 → BigQuery is linked, you can query the events table
// The table is in your BigQuery dataset as 'events_YYYYMMDD' (intraday) or 'events_*' (all days)
const {BigQuery} = require('@google-cloud/bigquery');
const bigquery = new BigQuery({
projectId: 'your-gcp-project'
});
const sqlQuery = `
SELECT
session_id,
user_id,
MIN(event_timestamp) as session_start,
MAX(event_timestamp) as session_end,
ROUND((MAX(event_timestamp) - MIN(event_timestamp)) / 1000000, 2) as session_duration_seconds
FROM \`your-gcp-project.analytics_123456789.events_*\`
WHERE event_date >= '2024-01-01' AND event_date <= '2024-01-31'
GROUP BY session_id, user_id
ORDER BY session_start DESC
LIMIT 1000
`;
const [rows] = await bigquery.query({query: sqlQuery});
rows.forEach((row) => {
console.log(`Session ${row.session_id}: ${row.session_duration_seconds}s`);
});Interpret the session duration result
The query returns session duration in seconds (event_timestamp is in microseconds, so divide by 1,000,000). Sessions with very short durations (< 1 second) often indicate bounce sessions. Sessions longer than 30+ minutes may indicate multiple sessions merged (depending on your timeout setting).
// Post-process the BigQuery results
const rows = []; // from previous BigQuery query
const sessions = rows.map((row) => ({
session_id: row.session_id,
user_id: row.user_id,
duration_seconds: row.session_duration_seconds,
duration_minutes: Math.round(row.session_duration_seconds / 60),
is_bounce: row.session_duration_seconds < 1,
session_date: new Date(row.session_start / 1000).toISOString().split('T')[0]
}));
// Filter, sort, or aggregate as needed
const avgDuration = sessions.reduce((sum, s) => sum + s.duration_seconds, 0) / sessions.length;
console.log(`Average session duration: ${Math.round(avgDuration)}s`);
const bounceRate = sessions.filter((s) => s.is_bounce).length / sessions.length;
console.log(`Bounce rate: ${(bounceRate * 100).toFixed(2)}%`);Common Pitfalls
- Confusing 'average session duration' (returned by the Data API) with individual session durations. The API metric is always an aggregate.
- Forgetting that GA4 event_timestamp is in microseconds, not seconds. Divide by 1,000,000 when calculating duration from BigQuery.
- Expecting to see a 'session duration' dimension in the GA4 UI. GA4 only exposes it as a metric, not a dimension you can filter by.
- Not accounting for GA4's default 30-minute session timeout. Sessions ending exactly at 30 minutes likely hit the timeout, not a real user exit.
Wrapping Up
You now have three ways to calculate session duration in GA4: via the UI metric average, the Data API for programmatic access, or BigQuery SQL for row-level precision. Start with the Data API if you just need aggregates, or BigQuery if you need to drill down into individual sessions. If you want to track session metrics automatically across all your tools, Product Analyst can help.