Class: Seo::Ga4ApiClient

Inherits:
Object
  • Object
show all
Defined in:
app/services/seo/ga4_api_client.rb

Overview

Client for Google Analytics 4 Data API using Service Account authentication.
Fetches engagement metrics (page views, sessions, bounce rate, time on page).

GA4 provides first-party engagement data that complements Search Console metrics.
Use this to understand how users interact with pages after they arrive.

Examples:

Basic usage

client = Seo::Ga4ApiClient.new
data = client.page_metrics(
  page_path: '/posts/my-article',
  start_date: 30.days.ago.to_date,
  end_date: Date.yesterday
)

Get all pages

data = client.all_pages(
  start_date: 30.days.ago.to_date,
  end_date: Date.yesterday
)

Instance Method Summary collapse

Constructor Details

#initialize(property_id: nil) ⇒ Ga4ApiClient

GA4 Property ID (numeric ID from GA4 admin)
Format: properties/123456789



27
28
29
30
31
32
# File 'app/services/seo/ga4_api_client.rb', line 27

def initialize(property_id: nil)
  @property_id = property_id || Heatwave::Configuration.fetch(:google, :ga4_property_id)
  raise 'GA4 Property ID not configured' if @property_id.blank?

  @client = build_client
end

Instance Method Details

#all_pages(start_date:, end_date:, limit: 5000) ⇒ Array<Hash>

Get engagement metrics for all pages

Parameters:

  • start_date (Date)

    Start of date range

  • end_date (Date)

    End of date range

  • limit (Integer) (defaults to: 5000)

    Max rows to return (default 5000)

Returns:

  • (Array<Hash>)

    Array of page metrics



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'app/services/seo/ga4_api_client.rb', line 68

def all_pages(start_date:, end_date:, limit: 5000)
  response = run_report(
    date_ranges: [{ start_date: start_date.to_s, end_date: end_date.to_s }],
    dimensions: [{ name: 'pagePath' }],
    metrics: core_metrics,
    limit: limit,
    order_bys: [
      { metric: { metric_name: 'screenPageViews' }, desc: true }
    ]
  )

  return [] if response.rows.blank?

  response.rows.map do |row|
    metrics = parse_row(row, response.metric_headers)
    metrics[:page_path] = row.dimension_values.first.value
    metrics
  end
end

#page_daily_metrics(page_path:, start_date:, end_date:) ⇒ Array<Hash>

Get metrics grouped by page path and date
Useful for trend analysis

Parameters:

  • page_path (String)

    Page path to filter by

  • start_date (Date)

    Start of date range

  • end_date (Date)

    End of date range

Returns:

  • (Array<Hash>)

    Daily metrics



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'app/services/seo/ga4_api_client.rb', line 105

def page_daily_metrics(page_path:, start_date:, end_date:)
  response = run_report(
    date_ranges: [{ start_date: start_date.to_s, end_date: end_date.to_s }],
    dimensions: [{ name: 'date' }, { name: 'pagePath' }],
    metrics: core_metrics,
    dimension_filter: {
      filter: {
        field_name: 'pagePath',
        string_filter: {
          value: page_path,
          match_type: 'EXACT'
        }
      }
    },
    order_bys: [{ dimension: { dimension_name: 'date' }, desc: false }],
    limit: 365
  )

  return [] if response.rows.blank?

  response.rows.map do |row|
    metrics = parse_row(row, response.metric_headers)
    metrics[:date] = Date.parse(row.dimension_values.first.value)
    metrics[:page_path] = row.dimension_values[1].value
    metrics
  end
end

#page_metrics(page_path:, start_date:, end_date:) ⇒ Hash?

Get engagement metrics for a specific page path

Parameters:

  • page_path (String)

    Page path (e.g., '/posts/my-article')

  • start_date (Date)

    Start of date range

  • end_date (Date)

    End of date range

Returns:

  • (Hash, nil)

    Metrics hash or nil if no data



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'app/services/seo/ga4_api_client.rb', line 40

def page_metrics(page_path:, start_date:, end_date:)
  response = run_report(
    date_ranges: [{ start_date: start_date.to_s, end_date: end_date.to_s }],
    dimensions: [{ name: 'pagePath' }],
    metrics: core_metrics,
    dimension_filter: {
      filter: {
        field_name: 'pagePath',
        string_filter: {
          value: page_path,
          match_type: 'EXACT'
        }
      }
    },
    limit: 1
  )

  return nil if response.rows.blank?

  parse_row(response.rows.first, response.metric_headers)
end

#page_traffic_sources(page_path:, start_date:, end_date:) ⇒ Array<Hash>

Get traffic sources for a specific page

Parameters:

  • page_path (String)

    Page path

  • start_date (Date)

    Start of date range

  • end_date (Date)

    End of date range

Returns:

  • (Array<Hash>)

    Traffic sources with metrics



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'app/services/seo/ga4_api_client.rb', line 139

def page_traffic_sources(page_path:, start_date:, end_date:)
  response = run_report(
    date_ranges: [{ start_date: start_date.to_s, end_date: end_date.to_s }],
    dimensions: [{ name: 'sessionDefaultChannelGroup' }, { name: 'pagePath' }],
    metrics: [
      { name: 'sessions' },
      { name: 'screenPageViews' },
      { name: 'engagedSessions' }
    ],
    dimension_filter: {
      filter: {
        field_name: 'pagePath',
        string_filter: {
          value: page_path,
          match_type: 'EXACT'
        }
      }
    },
    order_bys: [{ metric: { metric_name: 'sessions' }, desc: true }],
    limit: 20
  )

  return [] if response.rows.blank?

  response.rows.map do |row|
    {
      channel: row.dimension_values.first.value,
      sessions: row.metric_values[0].value.to_i,
      page_views: row.metric_values[1].value.to_i,
      engaged_sessions: row.metric_values[2].value.to_i
    }
  end
end

#test_connectionBoolean

Test the connection

Returns:

  • (Boolean)

    true if connection is successful



175
176
177
178
179
180
181
182
183
184
185
186
# File 'app/services/seo/ga4_api_client.rb', line 175

def test_connection
  run_report(
    date_ranges: [{ start_date: '7daysAgo', end_date: 'yesterday' }],
    dimensions: [{ name: 'pagePath' }],
    metrics: [{ name: 'screenPageViews' }],
    limit: 1
  )
  true
rescue StandardError => e
  Rails.logger.error "[Ga4ApiClient] Connection test failed: #{e.message}"
  false
end

#top_pages(start_date:, end_date:, limit: 100) ⇒ Array<Hash>

Get top pages by page views

Parameters:

  • start_date (Date)

    Start of date range

  • end_date (Date)

    End of date range

  • limit (Integer) (defaults to: 100)

    Max pages to return

Returns:

  • (Array<Hash>)

    Array of { page_path:, page_views:, ... }



94
95
96
# File 'app/services/seo/ga4_api_client.rb', line 94

def top_pages(start_date:, end_date:, limit: 100)
  all_pages(start_date: start_date, end_date: end_date, limit: limit)
end