You can create an Interactive report, run an existing report, and read report results using the Ad Manager API.
If you are unfamiliar with Interactive reporting in Ad Manager, see Create an Interactive report for an overview of how to use Interactive reports in the Ad Manager UI.
For complex reports, you can use the Ad Manager UI to check dimension and metric compatibility. All UI reports can be run with the API.
This guide covers how to initiate an asynchronous run of a
Report
,
poll the returned
Operation
status, obtain the
Result
resource name from the completed
Operation
and fetch a paginated set of result
Rows
.
Prerequisite
Before continuing, ensure you have access to a Google Ad Manager network. To get access, see Get started with Google Ad Manager.
Run a report
To run a report, you need the report ID. You can obtain a report ID in the Ad
Manager UI through the report URL. For example, in the URL
https://admanager.google.com/234093456#reports/interactive/detail/report_id=4555265029
the report ID is 4555265029
.
You can also read reports your user has access to using the
networks.reports.list
method and get the ID from the resource name:
networks/234093456/reports/4555265029
After you have your report ID, you can initiate an asynchronous run of the
report using the
networks.reports.run
method. This method returns the resource name of a long running
Operation
.
Note that in the following example code, REPORT_ID
is a placeholder for the
report ID and NETWORK_CODE
is a placeholder for your network code. To find
you network code, see
Find Ad Manager account information.
Java
import com.google.ads.admanager.v1.ReportName;
import com.google.ads.admanager.v1.ReportServiceClient;
import com.google.ads.admanager.v1.RunReportResponse;
public class SyncRunReportReportname {
public static void main(String[] args) throws Exception {
syncRunReportReportname();
}
public static void syncRunReportReportname() throws Exception {
try (ReportServiceClient reportServiceClient = ReportServiceClient.create()) {
ReportName name = ReportName.of("NETWORK_CODE", "REPORT_ID");
RunReportResponse response = reportServiceClient.runReportAsync(name).get();
}
}
}
Python
from google.ads import admanager_v1 def sample_run_report(): # Create a client client = admanager_v1.ReportServiceClient() # Initialize request argument(s) request = admanager_v1.RunReportRequest( name="networks/NETWORK_CODE/reports/REPORT_ID", ) # Make the request operation = client.run_report(request=request) print("Waiting for operation to complete...") response = operation.result() # Handle the response print(response)
.NET
using Google.Ads.AdManager.V1;
using Google.LongRunning;
public sealed partial class GeneratedReportServiceClientSnippets
{
public void RunReportResourceNames()
{
// Create client
ReportServiceClient reportServiceClient = ReportServiceClient.Create();
// Initialize request argument(s)
ReportName name = ReportName.FromNetworkCodeReport("NETWORK_CODE", "REPORT_ID");
// Make the request
Operation<RunReportResponse, RunReportMetadata> response = reportServiceClient.RunReport(name);
// Poll until the returned long-running operation is complete
Operation<RunReportResponse, RunReportMetadata> completedResponse = response.PollUntilCompleted();
// Retrieve the operation result
RunReportResponse result = completedResponse.Result;
// Or get the name of the operation
string operationName = response.Name;
// This name can be stored, then the long-running operation retrieved later by name
Operation<RunReportResponse, RunReportMetadata> retrievedResponse = reportServiceClient.PollOnceRunReport(operationName);
// Check if the retrieved long-running operation has completed
if (retrievedResponse.IsCompleted)
{
// If it has completed, then access the result
RunReportResponse retrievedResult = retrievedResponse.Result;
}
}
}
PHP
use Google\Ads\AdManager\V1\Client\ReportServiceClient;
use Google\Ads\AdManager\V1\RunReportRequest;
use Google\Ads\AdManager\V1\RunReportResponse;
use Google\ApiCore\ApiException;
use Google\ApiCore\OperationResponse;
use Google\Rpc\Status;
function run_report_sample(string $formattedName): void
{
// Create a client.
$reportServiceClient = new ReportServiceClient();
// Prepare the request message.
$request = (new RunReportRequest())
->setName($formattedName);
// Call the API and handle any network failures.
try {
$response = $reportServiceClient->runReport($request);
$response->pollUntilComplete();
if ($response->operationSucceeded()) {
$result = $response->getResult();
printf('Operation successful with response data: %s' . PHP_EOL, $result->serializeToJsonString());
} else {
$error = $response->getError();
printf('Operation failed with error data: %s' . PHP_EOL, $error->serializeToJsonString());
}
} catch (ApiException $ex) {
printf('Call failed with message: %s' . PHP_EOL, $ex->getMessage());
}
}
function callSample(): void
{
$formattedName = ReportServiceClient::reportName('NETWORK_CODE', 'REPORT_ID');
run_report_sample($formattedName);
}
Ruby
require "google/ads/ad_manager/v1"
def run_report
# Create a client object. The client can be reused for multiple calls.
client = Google::Ads::AdManager::V1::ReportService::Rest::Client.new
# Create a request. To set request fields, pass in keyword arguments.
request = Google::Ads::AdManager::V1::RunReportRequest.new(
:name => 'networks/NETWORK_CODE/reports/REPORT_ID'
)
# Call the run_report method.
result = client.run_report request
# The returned object is of type Gapic::Operation. You can use it to
# check the status of an operation, cancel it, or wait for results.
# Here is how to wait for a response.
result.wait_until_done! timeout: 60
if result.response?
p result.response
else
puts "No response received."
end
end
Node.js
const name = 'networks/NETWORK_CODE/reports/REPORT_ID';
// Imports the Admanager library
const {ReportServiceClient} = require('@google-ads/admanager').v1;
// Instantiates a client
const admanagerClient = new ReportServiceClient();
async function callRunReport() {
// Construct request
const request = {
name,
};
// Run request
const [operation] = await admanagerClient.runReport(request);
const [response] = await operation.promise();
console.log(response);
}
callRunReport();
cURL
Request
curl -X POST -H "Authorization: Bearer ${ACCESS_TOKEN}" \ "https://admanager.googleapis.com/v1/networks/${NETWORK_CODE}/reports/{$REPORT_ID}:run"
Response
{ "name": "networks/234093456/operations/reports/runs/6485392645", "metadata": { "@type": "type.googleapis.com/google.ads.admanager.v1.RunReportMetadata", "report": "networks/234093456/reports/4555265029" } }
Poll the report status
If you are using a client library, the previous section's example code polls
the status of the report run
Operation
at the recommended intervals and provides the result when it is complete. For
more information on the recommended polling intervals, see
networks.reports.run
.
If you want more control over polling, make an individual request to retrieve
the current status of a running report using the
networks.operations.reports.runs.get
method. Some client libraries also support setting custom polling strategies:
Java
import com.google.ads.admanager.v1.ReportServiceSettings;
import com.google.api.gax.longrunning.OperationalTimedPollAlgorithm;
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.retrying.TimedRetryAlgorithm;
import java.time.Duration;
public class SyncRunReport {
public static void main(String[] args) throws Exception {
syncRunReport();
}
public static void syncRunReport() throws Exception {
ReportServiceSettings.Builder reportServiceSettingsBuilder = ReportServiceSettings.newBuilder();
TimedRetryAlgorithm timedRetryAlgorithm =
OperationalTimedPollAlgorithm.create(
RetrySettings.newBuilder()
.setInitialRetryDelayDuration(Duration.ofMillis(500))
.setRetryDelayMultiplier(1.5)
.setMaxRetryDelayDuration(Duration.ofMillis(5000))
.setTotalTimeoutDuration(Duration.ofHours(24))
.build());
reportServiceSettingsBuilder
.createClusterOperationSettings()
.setPollingAlgorithm(timedRetryAlgorithm)
.build();
}
}
Python
from google.ads import admanager_v1 from google.longrunning.operations_pb2 import GetOperationRequest def sample_poll_report(): # Run the report client = admanager_v1.ReportServiceClient() response = client.run_report(name="networks/NETWORK_CODE/reports/REPORT_ID") # Check if the long-running operation has completed operation = client.get_operation( GetOperationRequest(name=response.operation.name)) if(operation.done): # If it has completed, then access the result run_report_response = admanager_v1.RunReportResponse.deserialize(payload=operation.response.value)
.NET
Operation<RunReportResponse, RunReportMetadata> retrievedResponse = reportServiceClient.PollOnceRunReport(operationName); // Check if the retrieved long-running operation has completed if (retrievedResponse.IsCompleted) { // If it has completed, then access the result RunReportResponse retrievedResult = retrievedResponse.Result; }
PHP
$options = [
'initialPollDelayMillis' => 500, // Initial delay of 500ms
'pollDelayMultiplier' => 1.5,
'maxPollDelayMillis' => 30000, // Max poll delay of 30 seconds
'totalPollTimeoutMillis' => 60 * 60 * 1000, // Total timeout of 1 hour in milliseconds
];
$response = $reportServiceClient->runReport($request);
$response->pollUntilComplete($options);
Ruby
options = {
initial_delay: 0.5, # Initial delay of 500ms (0.5 seconds)
multiplier: 1.5,
max_delay: 30.0, # Max poll delay of 30 seconds
timeout: 60 * 60 # Total timeout of 1 hour in seconds
}
result = client.run_report request
result.wait_until_done!(retry_policy: options)
if result.response?
p result.response
else
puts "No response received."
end
Node.js
const options = {
initialRetryDelayMillis: 500, // Initial delay of 500ms
retryDelayMultiplier: 1.5,
maxRetryDelayMillis: 30, // Max poll delay of 30 seconds
totalTimeoutMillis: 60 * 60 * 1000 // Total timeout of 1 hour
}
const [operation] = await admanagerClient.runReport(request);
operation.backoffSettings = options;
const [response] = await operation.promise();
console.log(response);
cURL
Request
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
"https://admanager.googleapis.com/v1/networks/${NETWORK_CODE}/operations/reports/runs/${OPERATION_ID}"
Response
{ "name": "networks/234093456/operations/reports/runs/6485392645", "metadata": { "@type": "type.googleapis.com/google.ads.admanager.v1.RunReportMetadata", "percentComplete": 50, "report": "networks/234093456/reports/4555265029" }, "done": false, }
Get the result resource name
After the report run
Operation
is complete, it contains the resource name of the
Result
.
Java
RunReportResponse response = reportServiceClient.runReportAsync(name).get();
// Result name in the format networks/[NETWORK_CODE]/reports/[REPORT_ID]/results/[RESULT_ID]
String resultName = response.getReportResult();
Python
operation = client.run_report(request=request)
response = operation.result()
# Result name in the format networks/[NETWORK_CODE]/reports/[REPORT_ID]/results/[RESULT_ID]
result_name = response.report_result
.NET
Operation<RunReportResponse, RunReportMetadata> response = reportServiceClient.RunReport(request);
// Poll until the returned long-running operation is complete
Operation<RunReportResponse, RunReportMetadata> completedResponse = response.PollUntilCompleted();
RunReportResponse result = completedResponse.Result;
// Result name in the format networks/[NETWORK_CODE]/reports/[REPORT_ID]/results/[RESULT_ID]
string resultName = result.ReportResult;
PHP
$response = $reportServiceClient->runReport($request);
$response->pollUntilComplete();
if ($response->operationSucceeded()) {
$result = $response->getResult();
// Result name in the format networks/[NETWORK_CODE]/reports/[REPORT_ID]/results/[RESULT_ID]
$resultName = $result->getReportResult();
}
Ruby
result = client.run_report request
result.wait_until_done!
if result.response?
# Result name in the format networks/[NETWORK_CODE]/reports/[REPORT_ID]/results/[RESULT_ID]
p result.response.report_result
else
puts "No response received."
end
Node.js
// Run request
const [operation] = await admanagerClient.runReport(request);
const [response] = await operation.promise();
// Result name in the format networks/[NETWORK_CODE]/reports/[REPORT_ID]/results/[RESULT_ID]
console.log(response.reportResult);
cURL
Request
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
"https://admanager.googleapis.com/v1/networks/${NETWORK_CODE}/operations/reports/runs/${OPERATION_ID}"
Response
{ "name": "networks/234093456/operations/reports/runs/6485392645", "metadata": { "@type": "type.googleapis.com/google.ads.admanager.v1.RunReportMetadata", "percentComplete": 100, "report": "networks/234093456/reports/4555265029" }, "done": true, "response": { "@type": "type.googleapis.com/google.ads.admanager.v1.RunReportResponse", "reportResult": "networks/234093456/reports/4555265029/results/7031632628" } }
Read result rows
The Result
resource has a single method,
networks.reports.results.fetchRows
,
to read a paginated list of rows. Each row
has a list of dimension values and a list of grouped metric
values. Each group contains the metric value and any comparison values
or flags. For more information on flags, see
Use flags in your interactive report.
For reports with no date range comparisons or splits, there is a
single
MetricValueGroup
with metric values (for example, impressions or clicks) for the entire date
range of the report.
The order of dimension and metric values are the same as the order in the
ReportDefinition
of the Report
.
The following is a JSON example of a
ReportDefinition
and a corresponding
fetchRows
response:
Report Definition
{
"name": "networks/234093456/reports/4555265029",
"visibility": "SAVED",
"reportId": "4555265029",
"reportDefinition": {
"dimensions": [
"LINE_ITEM_NAME",
"LINE_ITEM_ID"
],
"metrics": [
"AD_SERVER_IMPRESSIONS"
],
"currencyCode": "USD",
"dateRange": {
"relative": "YESTERDAY"
},
"reportType": "HISTORICAL"
},
"displayName": "Example Report",
"updateTime": "2024-09-01T13:00:00Z",
"createTime": "2024-08-01T02:00:00Z",
"locale": "en-US",
"scheduleOptions": {}
}
fetchRows
Response
{
"rows": [
{
"dimensionValues": [
{
"stringValue": "Line Item #1"
},
{
"intValue": "6378470710"
}
],
"metricValueGroups": [
{
"primaryValues": [
{
"intValue": "100"
}
]
}
]
},
{
"dimensionValues": [
{
"stringValue": "Line Item #2"
},
{
"intValue": "5457147368"
}
],
"metricValueGroups": [
{
"primaryValues": [
{
"intValue": "95"
}
]
}
]
}
],
"runTime": "2024-10-02T10:00:00Z",
"dateRanges": [
{
"startDate": {
"year": 2024,
"month": 10,
"day": 1
},
"endDate": {
"year": 2024,
"month": 10,
"day": 1
}
}
],
"totalRowCount": 2
}
If you are using a client library, the response has an iterator that lazily
requests additional pages. You can also use the pageToken
and pageSize
parameters. For details on these parameters, see
Query parameters.
If another page exists, the response contains a
nextPageToken
field with the token to use in the next request.
Java
import com.google.ads.admanager.v1.Report;
import com.google.ads.admanager.v1.ReportServiceClient;
public class SyncFetchReportResultRowsString {
public static void main(String[] args) throws Exception {
syncFetchReportResultRowsString();
}
public static void syncFetchReportResultRowsString() throws Exception {
try (ReportServiceClient reportServiceClient = ReportServiceClient.create()) {
String name = "networks/NETWORK_CODE/reports/REPORT_ID/results/RESULT_ID";
for (Report.DataTable.Row element :
reportServiceClient.fetchReportResultRows(name).iterateAll()) {
}
}
}
}
Python
from google.ads import admanager_v1
def sample_fetch_report_result_rows():
# Create a client
client = admanager_v1.ReportServiceClient()
# Initialize request argument(s)
request = admanager_v1.FetchReportResultRowsRequest(
name = "networks/NETWORK_CODE/reports/REPORT_ID/results/RESULT_ID"
)
# Make the request
page_result = client.fetch_report_result_rows(request=request)
# Handle the response
for response in page_result:
print(response)
.NET
using Google.Ads.AdManager.V1;
using Google.Api.Gax;
using System;
public sealed partial class GeneratedReportServiceClientSnippets
{
public void FetchReportResultRows()
{
// Create client
ReportServiceClient reportServiceClient = ReportServiceClient.Create();
// Initialize request argument(s)
string name = "";
// Make the request
PagedEnumerable<FetchReportResultRowsResponse, Report.Types.DataTable.Types.Row> response = reportServiceClient.FetchReportResultRows(name);
// Iterate over all response items, lazily performing RPCs as required
foreach (Report.Types.DataTable.Types.Row item in response)
{
// Do something with each item
Console.WriteLine(item);
}
// Or iterate over pages (of server-defined size), performing one RPC per page
foreach (FetchReportResultRowsResponse page in response.AsRawResponses())
{
// Do something with each page of items
Console.WriteLine("A page of results:");
foreach (Report.Types.DataTable.Types.Row item in page)
{
// Do something with each item
Console.WriteLine(item);
}
}
// Or retrieve a single page of known size (unless it's the final page), performing as many RPCs as required
int pageSize = 10;
Page<Report.Types.DataTable.Types.Row> singlePage = response.ReadPage(pageSize);
// Do something with the page of items
Console.WriteLine($"A page of {pageSize} results (unless it's the final page):");
foreach (Report.Types.DataTable.Types.Row item in singlePage)
{
// Do something with each item
Console.WriteLine(item);
}
// Store the pageToken, for when the next page is required.
string nextPageToken = singlePage.NextPageToken;
}
}
PHP
use Google\Ads\AdManager\V1\Client\ReportServiceClient;
use Google\Ads\AdManager\V1\FetchReportResultRowsRequest;
use Google\Ads\AdManager\V1\FetchReportResultRowsResponse;
use Google\ApiCore\ApiException;
function fetch_report_result_rows_sample(): void
{
// Create a client.
$reportServiceClient = new ReportServiceClient();
// Prepare the request message.
$request = (new FetchReportResultRowsRequest())
->setName('networks/NETWORK_CODE/reports/REPORT_ID/results/RESULT_ID');
// Call the API and handle any network failures.
try {
$response = $reportServiceClient->fetchReportResultRows($request);
printf('Response data: %s' . PHP_EOL, $response->serializeToJsonString());
} catch (ApiException $ex) {
printf('Call failed with message: %s' . PHP_EOL, $ex->getMessage());
}
}
Ruby
require "google/ads/ad_manager/v1"
def fetch_report_result_rows
# Create a client object. The client can be reused for multiple calls.
client = Google::Ads::AdManager::V1::ReportService::Rest::Client.new
# Create a request. To set request fields, pass in keyword arguments.
request = Google::Ads::AdManager::V1::FetchReportResultRowsRequest.new(
:name => 'networks/NETWORK_CODE/reports/REPORT_ID/results/RESULT_ID'
)
# Call the fetch_report_result_rows method.
result = client.fetch_report_result_rows request
# The returned object is of type Gapic::PagedEnumerable. You can iterate
# over elements, and API calls will be issued to fetch pages as needed.
result.each do |item|
# Each element is of type ::Google::Ads::AdManager::V1::Report::DataTable::Row.
p item
end
end
Node.js
const name = 'networks/NETWORK_CODE/reports/REPORT_ID/results/RESULT_ID';
// Imports the Admanager library
const {ReportServiceClient} = require('@google-ads/admanager').v1;
// Instantiates a client
const admanagerClient = new ReportServiceClient();
async function callFetchReportResultRows() {
// Construct request
const request = {
name,
};
// Run request
const iterable = admanagerClient.fetchReportResultRowsAsync(request);
for await (const response of iterable) {
console.log(response);
}
}
callFetchReportResultRows();
cURL
Initial request
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
"https://admanager.googleapis.com/v1/networks/${NETWORK_CODE}/reports/${REPORT_ID}/results/${RESULT_ID}:fetchRows"
Next page request
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
"https://admanager.googleapis.com/v1/networks/${NETWORK_CODE}/reports/${REPORT_ID}/results/${RESULT_ID}:fetchRows?pageToken=${PAGE_TOKEN}"
Troubleshoot issues with a report
- Why isn't my report returned in the API?
- Make sure that the Ad Manager user you are authenticating as has access to the Interactive report. You can only read Interactive reports from the Ad Manager API.
- Why are report results on my test network empty?
- Test networks don't serve ads, so delivery reports don't have data.
- Why are the report results on my production network empty?
- The user you're authenticating as might not have access to the data you are attempting to report on. Verify that their role permissions and teams are set correctly.
- Why don't the lifetime clicks or impressions match my report in the UI?
- Lifetime impressions are for the entire life of the line item, regardless of the date range of the report. If a line item is still delivering, the value might change between two runs of the same report.