diff --git a/google-analytics-data/src/main/java/com/google/analytics/data/samples/RunReportTaskSample.java b/google-analytics-data/src/main/java/com/google/analytics/data/samples/RunReportTaskSample.java
new file mode 100644
index 0000000..7de7643
--- /dev/null
+++ b/google-analytics-data/src/main/java/com/google/analytics/data/samples/RunReportTaskSample.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.analytics.data.samples;
+
+import com.google.analytics.data.v1alpha.AlphaAnalyticsDataClient;
+import com.google.analytics.data.v1alpha.CreateReportTaskRequest;
+import com.google.analytics.data.v1alpha.DateRange;
+import com.google.analytics.data.v1alpha.Dimension;
+import com.google.analytics.data.v1alpha.DimensionHeader;
+import com.google.analytics.data.v1alpha.Metric;
+import com.google.analytics.data.v1alpha.MetricHeader;
+import com.google.analytics.data.v1alpha.QueryReportTaskRequest;
+import com.google.analytics.data.v1alpha.QueryReportTaskResponse;
+import com.google.analytics.data.v1alpha.ReportTask;
+import com.google.analytics.data.v1alpha.ReportTask.ReportDefinition;
+import com.google.analytics.data.v1alpha.ReportTask.ReportMetadata;
+import com.google.analytics.data.v1alpha.ReportTaskMetadata;
+import com.google.analytics.data.v1alpha.Row;
+import com.google.api.gax.longrunning.OperationFuture;
+import com.google.api.gax.longrunning.OperationSnapshot;
+import com.google.api.gax.retrying.RetryingFuture;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import com.google.protobuf.Any;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * Google Analytics Data API sample application demonstrating the creation of a basic report.
+ *
+ *
See
+ * https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runReport
+ * for more information.
+ *
+ *
Before you start the application, please review the comments starting with "TODO(developer)"
+ * and update the code to use correct values.
+ *
+ *
To run this sample using Maven:
+ *
+ *
{@code
+ * cd google-analytics-data
+ * mvn compile exec:java -Dexec.mainClass="com.google.analytics.data.samples.RunReportSample"
+ * }
+ */
+public class RunReportTaskSample {
+
+ public static void main(String... args) throws Exception {
+ /**
+ * TODO(developer): Replace this variable with your Google Analytics 4 property ID before
+ * running the sample.
+ */
+ Deque argsDeque = Arrays.stream(args).collect(Collectors.toCollection(ArrayDeque::new));
+ String propertyId = MoreObjects.firstNonNull(argsDeque.pollFirst(), "YOUR-GA4-PROPERTY-ID");
+ int taskRowLimit = Integer.parseInt(MoreObjects.firstNonNull(argsDeque.pollFirst(), "10000"));
+ int queryPageSize = Integer.parseInt(MoreObjects.firstNonNull(argsDeque.pollFirst(), "5000"));
+ sampleRunReport(propertyId, taskRowLimit, queryPageSize);
+ }
+
+ // Runs a report of active users grouped by country.
+ static void sampleRunReport(String propertyId, int taskRowsLimit, int queryPageSize)
+ throws Exception {
+ System.out.printf("ARGS: taskRowsLimit=%,d queryPageSize=%,d%n", taskRowsLimit, queryPageSize);
+
+ // Using a default constructor instructs the client to use the credentials
+ // specified in GOOGLE_APPLICATION_CREDENTIALS environment variable.
+ try (AlphaAnalyticsDataClient analyticsData = AlphaAnalyticsDataClient.create()) {
+ List dimensions =
+ Arrays.asList("date", "hour", "minute", "source", "medium", "campaignId", "eventName")
+ .stream()
+ .map(n -> Dimension.newBuilder().setName(n).build())
+ .collect(Collectors.toList());
+ final ReportDefinition reportDefinition =
+ ReportDefinition.newBuilder()
+ .addAllDimensions(dimensions)
+ .addMetrics(Metric.newBuilder().setName("activeUsers"))
+ .addDateRanges(DateRange.newBuilder().setStartDate("90daysAgo").setEndDate("today"))
+ // Uses the specified limit for number of rows per task.
+ .setLimit(taskRowsLimit)
+ .build();
+ System.out.printf("Report definition:%n%s%n", reportDefinition);
+ CreateReportTaskRequest createReportTaskRequest =
+ CreateReportTaskRequest.newBuilder()
+ .setParent("properties/" + propertyId)
+ .setReportTask(
+ ReportTask.newBuilder()
+ .setName("Minute breakdown")
+ .setReportDefinition(reportDefinition))
+ .build();
+
+ int taskOffset = 0;
+ int totalRowCount;
+ do {
+ System.out.printf("BEGIN: ReportTask for task_offset=%,d%n", taskOffset);
+ // Updates the task offset.
+ createReportTaskRequest =
+ createReportTaskRequest.toBuilder()
+ .setReportTask(
+ createReportTaskRequest.getReportTask().toBuilder()
+ .setReportDefinition(reportDefinition.toBuilder().setOffset(taskOffset)))
+ .build();
+ // Make the request.
+ OperationFuture createReportTaskResponse = analyticsData.createReportTaskAsync(
+ createReportTaskRequest);
+ RetryingFuture pollingFuture =
+ createReportTaskResponse.getPollingFuture();
+ // Waits for the createReportTask async call to complete.
+ // If the call fails, this will throw an exception.
+ OperationSnapshot operationSnapshot = pollingFuture.get(5, TimeUnit.MINUTES);
+ Preconditions.checkState(
+ operationSnapshot.isDone(), "ReportTask not created within timeout");
+ ReportTask reportTask = ((Any) operationSnapshot.getResponse()).unpack(ReportTask.class);
+ System.out.printf("ReportTask: %s%n", reportTask.getName());
+ totalRowCount = reportTask.getReportMetadata().getTotalRowCount();
+ // System.out.printf(
+ // "Reached %s on attempt %d%n",
+ // reportTask.getReportMetadata().getState(),
+ // pollingFuture.getAttemptSettings().getOverallAttemptCount());
+ printReportTask(reportTask);
+
+ int taskRowCount = reportTask.getReportMetadata().getTaskRowCount();
+ int queryOffset = 0;
+ int limit = queryPageSize;
+ while (queryOffset < taskRowCount) {
+ QueryReportTaskRequest queryReportTaskRequest =
+ QueryReportTaskRequest.newBuilder()
+ .setName(reportTask.getName())
+ .setOffset(queryOffset)
+ .setLimit(limit)
+ .build();
+ QueryReportTaskResponse queryReportTaskResponse =
+ analyticsData.queryReportTask(queryReportTaskRequest);
+ printQueryReportTaskResponse(queryReportTaskResponse, queryOffset, limit, reportTask);
+ // IMPORTANT: Uses the count of response.rows, which is the number of rows in *this* query
+ // response. It does *not* use response.row_count, which is the number of total rows for
+ // the ReportTask (which is dictated by the *task's* `limit`).
+ queryOffset += queryReportTaskResponse.getRowsCount();
+ break;
+ }
+ System.out.printf("END : ReportTask for task_offset=%,d%n", taskOffset);
+ taskOffset += reportTask.getReportMetadata().getTaskRowCount();
+ } while (taskOffset < totalRowCount);
+
+ System.out.printf("Total rows received: %,d%n", taskOffset);
+ }
+ }
+
+ private static void printReportTask(ReportTask reportTask) {
+ ReportMetadata metadata = reportTask.getReportMetadata();
+ System.out.printf(
+ "offset=%,d limit=%,d%n",
+ reportTask.getReportDefinition().getOffset(), reportTask.getReportDefinition().getLimit());
+ System.out.printf("task_row_count=%,d%n", metadata.getTaskRowCount());
+ System.out.printf("total_row_count=%,d%n", metadata.getTotalRowCount());
+ }
+
+ // Prints results of a runReport call.
+ static void printQueryReportTaskResponse(
+ QueryReportTaskResponse response, int offset, int limit, ReportTask reportTask) {
+ // [START analyticsdata_print_run_report_response_header]
+ System.out.printf("BEGIN: Response for offset=%,d limit=%,d%n", offset, limit);
+ System.out.printf("rows[] size=%,d%n", response.getRowsCount());
+ System.out.printf(
+ "row_count=%,d (aka, TOTAL rows for the ReportTask report_metadata.task_row_count=%,d)%n",
+ response.getRowCount(), reportTask.getReportMetadata().getTaskRowCount());
+ System.out.printf("%,d rows received%n", response.getRowsList().size());
+
+ int i;
+ System.out.printf("Dimension headers count: %d%n", response.getDimensionHeadersCount());
+ i = 0;
+ for (DimensionHeader header : response.getDimensionHeadersList()) {
+ System.out.printf("- Dimension header[%d] name: %s%n", i++, header.getName());
+ }
+
+ i = 0;
+ System.out.printf("Metric headers count: %d%n", response.getMetricHeadersCount());
+ for (MetricHeader header : response.getMetricHeadersList()) {
+ System.out.printf("- Metric header[%d] name: %s (%s)%n", i++, header.getName(), header.getType());
+ }
+ // [END analyticsdata_print_run_report_response_header]
+
+ // [START analyticsdata_print_run_report_response_rows]
+ System.out.println();
+ System.out.println("Report rows:");
+ i = 0;
+ for (Row row : response.getRowsList()) {
+ System.out.printf(
+ "- Row[%d]: %s, %s%n",
+ i++,
+ row.getDimensionValuesList().stream()
+ .map(dv -> dv.getValue())
+ .collect(Collectors.joining(", ")),
+ row.getMetricValues(0).getValue());
+ if (i > 5) {
+ break;
+ }
+ }
+ System.out.printf("END : Response for offset=%,d limit=%,d%n", offset, limit);
+ // [END analyticsdata_print_run_report_response_rows]
+ }
+}
+// [END analyticsdata_run_report]