Share your feedback about the Google Ads (AdWords) API. Take the 2021 AdWords API and Google Ads API Annual Survey.

The AdWords API will sunset on April 27, 2022. Migrate to the Google Ads API to take advantage of the latest Google Ads features.

Reporting Samples

The code samples below provide examples of common reporting functions using the AdWords API. Client Library.

Download a criteria performance report with AWQL

// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 adwords.axis.v201809.reporting;

import static com.google.api.ads.common.lib.utils.Builder.DEFAULT_CONFIGURATION_FILENAME;

import com.google.api.ads.adwords.axis.factory.AdWordsServices;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.adwords.lib.client.reporting.ReportingConfiguration;
import com.google.api.ads.adwords.lib.factory.AdWordsServicesInterface;
import com.google.api.ads.adwords.lib.jaxb.v201809.DownloadFormat;
import com.google.api.ads.adwords.lib.jaxb.v201809.ReportDefinitionDateRangeType;
import com.google.api.ads.adwords.lib.jaxb.v201809.ReportDefinitionReportType;
import com.google.api.ads.adwords.lib.utils.DetailedReportDownloadResponseException;
import com.google.api.ads.adwords.lib.utils.ReportDownloadResponse;
import com.google.api.ads.adwords.lib.utils.ReportDownloadResponseException;
import com.google.api.ads.adwords.lib.utils.ReportException;
import com.google.api.ads.adwords.lib.utils.v201809.ReportDownloaderInterface;
import com.google.api.ads.adwords.lib.utils.v201809.ReportQuery;
import com.google.api.ads.common.lib.auth.OfflineCredentials;
import com.google.api.ads.common.lib.auth.OfflineCredentials.Api;
import com.google.api.ads.common.lib.conf.ConfigurationLoadException;
import com.google.api.ads.common.lib.exception.OAuthException;
import com.google.api.ads.common.lib.exception.ValidationException;
import com.google.api.client.auth.oauth2.Credential;
import java.io.File;
import java.io.IOException;

/**
 * This example downloads a criteria performance report with AWQL.
 *
 * <p>Credentials and properties in {@code fromFile()} are pulled from the "ads.properties" file.
 * See README for more info.
 */
public class DownloadCriteriaReportWithAwql {

  public static void main(String[] args) {
    AdWordsSession session;
    try {
      // Generate a refreshable OAuth2 credential.
      Credential oAuth2Credential =
          new OfflineCredentials.Builder()
              .forApi(Api.ADWORDS)
              .fromFile()
              .build()
              .generateCredential();

      // Construct an AdWordsSession.
      session =
          new AdWordsSession.Builder().fromFile().withOAuth2Credential(oAuth2Credential).build();
    } catch (ConfigurationLoadException cle) {
      System.err.printf(
          "Failed to load configuration from the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, cle);
      return;
    } catch (ValidationException ve) {
      System.err.printf(
          "Invalid configuration in the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, ve);
      return;
    } catch (OAuthException oe) {
      System.err.printf(
          "Failed to create OAuth credentials. Check OAuth settings in the %s file. "
              + "Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, oe);
      return;
    }

    AdWordsServicesInterface adWordsServices = AdWordsServices.getInstance();

    // Location to download report to.
    String reportFile = System.getProperty("user.home") + File.separatorChar + "report.csv";

    try {
      runExample(adWordsServices, session, reportFile);
    } catch (DetailedReportDownloadResponseException dre) {
      // A DetailedReportDownloadResponseException will be thrown if the HTTP status code in the
      // response indicates an error occurred and the response body contains XML with further
      // information, such as the fieldPath and trigger.
      System.err.printf(
          "Report was not downloaded due to a %s with errorText '%s', trigger '%s' and "
              + "field path '%s'%n",
          dre.getClass().getSimpleName(),
          dre.getErrorText(),
          dre.getTrigger(),
          dre.getFieldPath());
    } catch (ReportDownloadResponseException rde) {
      // A ReportDownloadResponseException will be thrown if the HTTP status code in the response
      // indicates an error occurred, but the response did not contain further details.
      System.err.printf("Report was not downloaded due to: %s%n", rde);
    } catch (ReportException re) {
      // A ReportException will be thrown if the download failed due to a transport layer exception.
      System.err.printf("Report was not downloaded due to transport layer exception: %s%n", re);
    } catch (IOException ioe) {
      // An IOException in this example indicates that the report's contents could not be written
      // to the output file.
      System.err.printf(
          "Report was not written to file %s due to an IOException: %s%n", reportFile, ioe);
    }
  }

  /**
   * Runs the example.
   *
   * @param adWordsServices the services factory.
   * @param session the session.
   * @param reportFile the output file for the report contents.
   * @throws DetailedReportDownloadResponseException if the report request failed with a detailed
   *     error from the reporting service.
   * @throws ReportDownloadResponseException if the report request failed with a general error from
   *     the reporting service.
   * @throws ReportException if the report request failed due to a transport layer error.
   * @throws IOException if the report's contents could not be written to {@code reportFile}.
   */
  public static void runExample(
      AdWordsServicesInterface adWordsServices, AdWordsSession session, String reportFile)
      throws ReportDownloadResponseException, ReportException, IOException  {
    // Create query.
    ReportQuery query =
        new ReportQuery.Builder()
            .fields(
                "CampaignId",
                "AdGroupId",
                "Id",
                "Criteria",
                "CriteriaType",
                "Impressions",
                "Clicks",
                "Cost")
            .from(ReportDefinitionReportType.CRITERIA_PERFORMANCE_REPORT)
            .where("Status").in("ENABLED", "PAUSED")
            .during(ReportDefinitionDateRangeType.LAST_7_DAYS)
            .build();

    // Optional: Set the reporting configuration of the session to suppress header, column name, or
    // summary rows in the report output. You can also configure this via your ads.properties
    // configuration file. See AdWordsSession.Builder.from(Configuration) for details.
    // In addition, you can set whether you want to explicitly include or exclude zero impression
    // rows.
    ReportingConfiguration reportingConfiguration =
        new ReportingConfiguration.Builder()
            .skipReportHeader(false)
            .skipColumnHeader(false)
            .skipReportSummary(false)
            // Set to false to exclude rows with zero impressions.
            .includeZeroImpressions(true)
            .build();
    session.setReportingConfiguration(reportingConfiguration);

    ReportDownloaderInterface reportDownloader =
        adWordsServices.getUtility(session, ReportDownloaderInterface.class);

    // Set the property api.adwords.reportDownloadTimeout or call
    // ReportDownloader.setReportDownloadTimeout to set a timeout (in milliseconds)
    // for CONNECT and READ in report downloads.
    ReportDownloadResponse response =
        reportDownloader.downloadReport(query.toString(), DownloadFormat.CSV);
    response.saveToFile(reportFile);
    
    System.out.printf("Report successfully downloaded to: %s%n", reportFile);
  }
}

Download a criteria performance report using selectors

// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 adwords.axis.v201809.reporting;

import static com.google.api.ads.common.lib.utils.Builder.DEFAULT_CONFIGURATION_FILENAME;

import com.google.api.ads.adwords.axis.factory.AdWordsServices;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.adwords.lib.client.reporting.ReportingConfiguration;
import com.google.api.ads.adwords.lib.factory.AdWordsServicesInterface;
import com.google.api.ads.adwords.lib.jaxb.v201809.DownloadFormat;
import com.google.api.ads.adwords.lib.jaxb.v201809.ReportDefinition;
import com.google.api.ads.adwords.lib.jaxb.v201809.ReportDefinitionDateRangeType;
import com.google.api.ads.adwords.lib.jaxb.v201809.ReportDefinitionReportType;
import com.google.api.ads.adwords.lib.jaxb.v201809.Selector;
import com.google.api.ads.adwords.lib.utils.DetailedReportDownloadResponseException;
import com.google.api.ads.adwords.lib.utils.ReportDownloadResponse;
import com.google.api.ads.adwords.lib.utils.ReportDownloadResponseException;
import com.google.api.ads.adwords.lib.utils.ReportException;
import com.google.api.ads.adwords.lib.utils.v201809.ReportDownloaderInterface;
import com.google.api.ads.common.lib.auth.OfflineCredentials;
import com.google.api.ads.common.lib.auth.OfflineCredentials.Api;
import com.google.api.ads.common.lib.conf.ConfigurationLoadException;
import com.google.api.ads.common.lib.exception.OAuthException;
import com.google.api.ads.common.lib.exception.ValidationException;
import com.google.api.client.auth.oauth2.Credential;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;

/**
 * This example downloads a criteria performance report.
 *
 * <p>Credentials and properties in {@code fromFile()} are pulled from the
 * "ads.properties" file. See README for more info.
 */
public class DownloadCriteriaReportWithSelector {

  public static void main(String[] args) {
    AdWordsSession session;
    try {
      // Generate a refreshable OAuth2 credential.
      Credential oAuth2Credential =
          new OfflineCredentials.Builder()
              .forApi(Api.ADWORDS)
              .fromFile()
              .build()
              .generateCredential();

      // Construct an AdWordsSession.
      session =
          new AdWordsSession.Builder().fromFile().withOAuth2Credential(oAuth2Credential).build();
    } catch (ConfigurationLoadException cle) {
      System.err.printf(
          "Failed to load configuration from the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, cle);
      return;
    } catch (ValidationException ve) {
      System.err.printf(
          "Invalid configuration in the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, ve);
      return;
    } catch (OAuthException oe) {
      System.err.printf(
          "Failed to create OAuth credentials. Check OAuth settings in the %s file. "
              + "Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, oe);
      return;
    }

    AdWordsServicesInterface adWordsServices = AdWordsServices.getInstance();

    // Location to download report to.
    String reportFile = System.getProperty("user.home") + File.separatorChar + "report.csv";

    try {
      runExample(adWordsServices, session, reportFile);
    } catch (DetailedReportDownloadResponseException dre) {
      // A DetailedReportDownloadResponseException will be thrown if the HTTP status code in the
      // response indicates an error occurred and the response body contains XML with further
      // information, such as the fieldPath and trigger.
      System.err.printf(
          "Report was not downloaded due to a %s with errorText '%s', trigger '%s' and "
              + "field path '%s'%n",
          dre.getClass().getSimpleName(),
          dre.getErrorText(),
          dre.getTrigger(),
          dre.getFieldPath());
    } catch (ReportDownloadResponseException rde) {
      // A ReportDownloadResponseException will be thrown if the HTTP status code in the response
      // indicates an error occurred, but the response did not contain further details.
      System.err.printf("Report was not downloaded due to: %s%n", rde);
    } catch (ReportException re) {
      // A ReportException will be thrown if the download failed due to a transport layer exception.
      System.err.printf("Report was not downloaded due to transport layer exception: %s%n", re);
    } catch (IOException ioe) {
      // An IOException in this example indicates that the report's contents could not be written
      // to the output file.
      System.err.printf(
          "Report was not written to file %s due to an IOException: %s%n", reportFile, ioe);
    }
  }

  /**
   * Runs the example.
   *
   * @param adWordsServices the services factory.
   * @param session the session.
   * @param reportFile the output file for the report contents.
   * @throws DetailedReportDownloadResponseException if the report request failed with a detailed
   *     error from the reporting service.
   * @throws ReportDownloadResponseException if the report request failed with a general error from
   *     the reporting service.
   * @throws ReportException if the report request failed due to a transport layer error.
   * @throws IOException if the report's contents could not be written to {@code reportFile}.
   */
  public static void runExample(
      AdWordsServicesInterface adWordsServices, AdWordsSession session, String reportFile)
      throws ReportDownloadResponseException, ReportException, IOException {
    // Create selector.
    Selector selector = new Selector();
    selector.getFields().addAll(Arrays.asList("CampaignId",
        "AdGroupId",
        "Id",
        "CriteriaType",
        "Criteria",
        "FinalUrls",
        "Impressions",
        "Clicks",
        "Cost"));

    // Create report definition.
    ReportDefinition reportDefinition = new ReportDefinition();
    reportDefinition.setReportName("Criteria performance report #" + System.currentTimeMillis());
    reportDefinition.setDateRangeType(ReportDefinitionDateRangeType.YESTERDAY);
    reportDefinition.setReportType(ReportDefinitionReportType.CRITERIA_PERFORMANCE_REPORT);
    reportDefinition.setDownloadFormat(DownloadFormat.CSV);
    
    // Optional: Set the reporting configuration of the session to suppress header, column name, or
    // summary rows in the report output. You can also configure this via your ads.properties
    // configuration file. See AdWordsSession.Builder.from(Configuration) for details.
    // In addition, you can set whether you want to explicitly include or exclude zero impression
    // rows.
    ReportingConfiguration reportingConfiguration =
        new ReportingConfiguration.Builder()
            .skipReportHeader(false)
            .skipColumnHeader(false)
            .skipReportSummary(false)
            // Enable to allow rows with zero impressions to show.
            .includeZeroImpressions(false)
            .build();
    session.setReportingConfiguration(reportingConfiguration);
    
    reportDefinition.setSelector(selector);

    ReportDownloaderInterface reportDownloader =
        adWordsServices.getUtility(session, ReportDownloaderInterface.class);

    // Set the property api.adwords.reportDownloadTimeout or call
    // ReportDownloader.setReportDownloadTimeout to set a timeout (in milliseconds)
    // for CONNECT and READ in report downloads.
    ReportDownloadResponse response = reportDownloader.downloadReport(reportDefinition);
    response.saveToFile(reportFile);

    System.out.printf("Report successfully downloaded to: %s%n", reportFile);
  }
}

Get the report fields from a report

// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 adwords.axis.v201809.reporting;

import static com.google.api.ads.common.lib.utils.Builder.DEFAULT_CONFIGURATION_FILENAME;

import com.google.api.ads.adwords.axis.factory.AdWordsServices;
import com.google.api.ads.adwords.axis.v201809.cm.ApiError;
import com.google.api.ads.adwords.axis.v201809.cm.ApiException;
import com.google.api.ads.adwords.axis.v201809.cm.ReportDefinitionField;
import com.google.api.ads.adwords.axis.v201809.cm.ReportDefinitionReportType;
import com.google.api.ads.adwords.axis.v201809.cm.ReportDefinitionServiceInterface;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.adwords.lib.factory.AdWordsServicesInterface;
import com.google.api.ads.common.lib.auth.OfflineCredentials;
import com.google.api.ads.common.lib.auth.OfflineCredentials.Api;
import com.google.api.ads.common.lib.conf.ConfigurationLoadException;
import com.google.api.ads.common.lib.exception.OAuthException;
import com.google.api.ads.common.lib.exception.ValidationException;
import com.google.api.client.auth.oauth2.Credential;
import java.rmi.RemoteException;

/**
 * This example gets report fields.
 *
 * <p>Credentials and properties in {@code fromFile()} are pulled from the
 * "ads.properties" file. See README for more info.
 */
public class GetReportFields {

  public static void main(String[] args) {
    AdWordsSession session;
    try {
      // Generate a refreshable OAuth2 credential.
      Credential oAuth2Credential =
          new OfflineCredentials.Builder()
              .forApi(Api.ADWORDS)
              .fromFile()
              .build()
              .generateCredential();

      // Construct an AdWordsSession.
      session =
          new AdWordsSession.Builder().fromFile().withOAuth2Credential(oAuth2Credential).build();
    } catch (ConfigurationLoadException cle) {
      System.err.printf(
          "Failed to load configuration from the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, cle);
      return;
    } catch (ValidationException ve) {
      System.err.printf(
          "Invalid configuration in the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, ve);
      return;
    } catch (OAuthException oe) {
      System.err.printf(
          "Failed to create OAuth credentials. Check OAuth settings in the %s file. "
              + "Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, oe);
      return;
    }

    AdWordsServicesInterface adWordsServices = AdWordsServices.getInstance();

    try {
      runExample(adWordsServices, session);
    } catch (ApiException apiException) {
      // ApiException is the base class for most exceptions thrown by an API request. Instances
      // of this exception have a message and a collection of ApiErrors that indicate the
      // type and underlying cause of the exception. Every exception object in the adwords.axis
      // packages will return a meaningful value from toString
      //
      // ApiException extends RemoteException, so this catch block must appear before the
      // catch block for RemoteException.
      System.err.println("Request failed due to ApiException. Underlying ApiErrors:");
      if (apiException.getErrors() != null) {
        int i = 0;
        for (ApiError apiError : apiException.getErrors()) {
          System.err.printf("  Error %d: %s%n", i++, apiError);
        }
      }
    } catch (RemoteException re) {
      System.err.printf("Request failed unexpectedly due to RemoteException: %s%n", re);
    }
  }

  /**
   * Runs the example.
   *
   * @param adWordsServices the services factory.
   * @param session the session.
   * @throws ApiException if the API request failed with one or more service errors.
   * @throws RemoteException if the API request failed due to other errors.
   */
  public static void runExample(
      AdWordsServicesInterface adWordsServices, AdWordsSession session) throws RemoteException {
    // Get the ReportDefinitionService.
    ReportDefinitionServiceInterface reportDefinitionService =
        adWordsServices.get(session, ReportDefinitionServiceInterface.class);

    // Get report fields.
    ReportDefinitionField[] reportDefinitionFields =
        reportDefinitionService
            .getReportFields(ReportDefinitionReportType.KEYWORDS_PERFORMANCE_REPORT);

    // Display report fields.
    System.out.println("Available fields for report:");

    for (ReportDefinitionField reportDefinitionField : reportDefinitionFields) {
      System.out.printf("\t %s(%s) := [", reportDefinitionField.getFieldName(),
          reportDefinitionField.getFieldType());
      if (reportDefinitionField.getEnumValues() != null) {
        for (String enumValue : reportDefinitionField.getEnumValues()) {
          System.out.printf("%s, ", enumValue);
        }
      }
      System.out.println("]");
    }
  }
}

Download a report for multiple accounts

// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 adwords.axis.v201809.reporting;

import static com.google.api.ads.common.lib.utils.Builder.DEFAULT_CONFIGURATION_FILENAME;

import com.google.api.ads.adwords.axis.factory.AdWordsServices;
import com.google.api.ads.adwords.axis.utils.v201809.SelectorBuilder;
import com.google.api.ads.adwords.axis.v201809.cm.ApiError;
import com.google.api.ads.adwords.axis.v201809.cm.ApiException;
import com.google.api.ads.adwords.axis.v201809.mcm.ManagedCustomer;
import com.google.api.ads.adwords.axis.v201809.mcm.ManagedCustomerPage;
import com.google.api.ads.adwords.axis.v201809.mcm.ManagedCustomerServiceInterface;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.adwords.lib.client.AdWordsSession.ImmutableAdWordsSession;
import com.google.api.ads.adwords.lib.client.reporting.ReportingConfiguration;
import com.google.api.ads.adwords.lib.factory.AdWordsServicesInterface;
import com.google.api.ads.adwords.lib.jaxb.v201809.DownloadFormat;
import com.google.api.ads.adwords.lib.jaxb.v201809.ReportDefinition;
import com.google.api.ads.adwords.lib.jaxb.v201809.ReportDefinitionDateRangeType;
import com.google.api.ads.adwords.lib.jaxb.v201809.ReportDefinitionReportType;
import com.google.api.ads.adwords.lib.jaxb.v201809.Selector;
import com.google.api.ads.adwords.lib.selectorfields.v201809.cm.ManagedCustomerField;
import com.google.api.ads.adwords.lib.utils.DetailedReportDownloadResponseException;
import com.google.api.ads.adwords.lib.utils.ReportDownloadResponse;
import com.google.api.ads.adwords.lib.utils.ReportDownloadResponseException;
import com.google.api.ads.adwords.lib.utils.ReportException;
import com.google.api.ads.adwords.lib.utils.v201809.ReportDownloaderInterface;
import com.google.api.ads.common.lib.auth.OfflineCredentials;
import com.google.api.ads.common.lib.auth.OfflineCredentials.Api;
import com.google.api.ads.common.lib.conf.ConfigurationLoadException;
import com.google.api.ads.common.lib.exception.OAuthException;
import com.google.api.ads.common.lib.exception.ValidationException;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.util.BackOff;
import com.google.api.client.util.ExponentialBackOff;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 * This example gets and downloads an Ad Hoc report from an XML report definition for all accounts
 * directly under a manager account in multiple parallel threads. This example should be run against
 * an AdWords manager account.
 *
 * <p>
 * Credentials and properties in {@code fromFile()} are pulled from the "ads.properties" file. See
 * README for more info.
 */
public class ParallelReportDownload {

  private static final int PAGE_SIZE = 500;

  public static void main(String[] args) {
    ImmutableAdWordsSession session;
    try {
      // Generate a refreshable OAuth2 credential.
      Credential oAuth2Credential =
          new OfflineCredentials.Builder()
              .forApi(Api.ADWORDS)
              .fromFile()
              .build()
              .generateCredential();

      // Construct an ImmutableAdWordsSession to use as a prototype when creating a session for each
      // managed customer.
      session =
          new AdWordsSession.Builder()
              .fromFile()
              .withOAuth2Credential(oAuth2Credential)
              .buildImmutable();
    } catch (ConfigurationLoadException cle) {
      System.err.printf(
          "Failed to load configuration from the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, cle);
      return;
    } catch (ValidationException ve) {
      System.err.printf(
          "Invalid configuration in the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, ve);
      return;
    } catch (OAuthException oe) {
      System.err.printf(
          "Failed to create OAuth credentials. Check OAuth settings in the %s file. "
              + "Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, oe);
      return;
    }

    AdWordsServicesInterface adWordsServices = AdWordsServices.getInstance();

    // Adjust these values as needed.
    int numberOfThreads = 5;
    int maxElapsedSecondsPerCustomer = 60 * 5;

    try {
      runExample(adWordsServices, session, numberOfThreads, maxElapsedSecondsPerCustomer);
    } catch (DetailedReportDownloadResponseException dre) {
      // A DetailedReportDownloadResponseException will be thrown if the HTTP status code in the
      // response indicates an error occurred and the response body contains XML with further
      // information, such as the fieldPath and trigger.
      System.err.printf(
          "Report was not downloaded due to a %s with errorText '%s', trigger '%s' and "
              + "field path '%s'%n",
          dre.getClass().getSimpleName(), dre.getErrorText(), dre.getTrigger(), dre.getFieldPath());
    } catch (ApiException apiException) {
      // ApiException is the base class for most exceptions thrown by an API request. Instances
      // of this exception have a message and a collection of ApiErrors that indicate the
      // type and underlying cause of the exception. Every exception object in the adwords.axis
      // packages will return a meaningful value from toString
      //
      // ApiException extends RemoteException, so this catch block must appear before the
      // catch block for RemoteException.
      System.err.println("Request failed due to ApiException. Underlying ApiErrors:");
      if (apiException.getErrors() != null) {
        int i = 0;
        for (ApiError apiError : apiException.getErrors()) {
          System.err.printf("  Error %d: %s%n", i++, apiError);
        }
      }
    } catch (RemoteException re) {
      System.err.printf("Request failed unexpectedly due to RemoteException: %s%n", re);
    } catch (ReportDownloadResponseException rde) {
      // A ReportDownloadResponseException will be thrown if the HTTP status code in the response
      // indicates an error occurred, but the response did not contain further details.
      System.err.printf("Report was not downloaded due to: %s%n", rde);
    } catch (ReportException re) {
      // A ReportException will be thrown if the download failed due to a transport layer exception.
      System.err.printf("Report was not downloaded due to transport layer exception: %s%n", re);
    } catch (IOException ioe) {
      // An IOException in this example indicates that the report's contents could not be read from
      // the response.
      System.err.printf("Report was not read due to an IOException: %s%n", ioe);
    } catch (InterruptedException ie) {
      System.err.printf("Thread was interrupted while waiting for reports to complete: %s%n", ie);
    } catch (ValidationException ve) {
      System.err.printf("Failed to create a session for a customer: %s%n", ve);
    }
  }

  /**
   * Runs the example.
   *
   * @param adWordsServices the services factory.
   * @param session the session.
   * @param numberOfThreads number of threads to use for concurrent report requests.
   * @param maxElapsedSecondsPerCustomer the maximum number of seconds to wait for each report
   *     request to complete.
   * @throws ApiException if the API request to retrieve managed customers failed with one or more
   *     service errors.
   * @throws RemoteException if the API request to retrieve managed customers failed due to other
   *     errors.
   * @throws DetailedReportDownloadResponseException if the report request failed with a detailed
   *     error from the reporting service.
   * @throws ReportDownloadResponseException if the report request failed with a general error from
   *     the reporting service.
   * @throws ReportException if the report request failed due to a transport layer error.
   * @throws IOException if the report's contents could not be read from the response.
   * @throws ValidationException if creation of an ImmutableAdWordsSession for a customer failed due
   *     to validation issues.
   * @throws InterruptedException if the thread was interrupted while waiting for all report
   *     downloads to complete.
   */
  public static void runExample(
      AdWordsServicesInterface adWordsServices,
      ImmutableAdWordsSession session,
      int numberOfThreads,
      int maxElapsedSecondsPerCustomer)
      throws ReportDownloadResponseException, ReportException, IOException, ValidationException,
          InterruptedException {

    // Retrieve all accounts under the manager account.
    Map<Long, ManagedCustomer> managedCustomers = getAllManagedCustomers(adWordsServices, session);
    System.out.printf("Downloading report for %d managed customers.%n", managedCustomers.size());

    // Create selector for the report definition.
    Selector selector = new Selector();
    selector
        .getFields()
        .addAll(Arrays.asList("CampaignId", "AdGroupId", "Impressions", "Clicks", "Cost"));

    // Create report definition.
    ReportDefinition reportDefinition = new ReportDefinition();
    reportDefinition.setReportName("Custom ADGROUP_PERFORMANCE_REPORT");
    reportDefinition.setDateRangeType(ReportDefinitionDateRangeType.LAST_7_DAYS);
    reportDefinition.setReportType(ReportDefinitionReportType.ADGROUP_PERFORMANCE_REPORT);
    reportDefinition.setDownloadFormat(DownloadFormat.CSV);
    reportDefinition.setSelector(selector);

    // Optional: Set the reporting configuration of the session to suppress header, column name, or
    // summary rows in the report output. You can also configure this via your ads.properties
    // configuration file. See AdWordsSession.Builder.from(Configuration) for details.
    // In addition, you can set whether you want to explicitly include or exclude zero impression
    // rows.
    ReportingConfiguration reportingConfiguration =
        new ReportingConfiguration.Builder()
            .skipReportHeader(false)
            .skipColumnHeader(false)
            .skipReportSummary(false)
            // Enable to allow rows with zero impressions to show.
            .includeZeroImpressions(false)
            .build();

    // Create a thread pool for submitting report requests.
    ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads);

    // Customize this builder if you want to change the backoff policy on retryable report
    // failures.
    ExponentialBackOff.Builder backOffBuilder = new ExponentialBackOff.Builder()
        .setMaxElapsedTimeMillis(maxElapsedSecondsPerCustomer * 1000);

    File reportDirectory = Files.createTempDir();

    // List to keep track of the progress of each customer's report download task.
    List<ReportDownloadFutureTask> reportDownloadFutureTasks = new ArrayList<>();

    for (ManagedCustomer managedCustomer : managedCustomers.values()) {
      File outputFile =
          new File(
              reportDirectory, String.format("adgroup_%010d.csv", managedCustomer.getCustomerId()));
      ImmutableAdWordsSession sessionForCustomer =
          session
              .newBuilder()
              .withClientCustomerId(Long.toString(managedCustomer.getCustomerId()))
              .withReportingConfiguration(reportingConfiguration)
              .buildImmutable();
      ReportDownloadFutureTask reportDownloadFutureTask =
          new ReportDownloadFutureTask(
              new ReportDownloadCallable(
                  sessionForCustomer,
                  adWordsServices,
                  reportDefinition,
                  outputFile,
                  backOffBuilder.build()));

      // Use execute instead of submit since there is no need to get a Future for a FutureTask.
      // Instead, store this ReportDownloadFutureTask in the list so that it can be used later
      // to check the result and determine the task context (client customer ID).
      threadPool.execute(reportDownloadFutureTask);

      reportDownloadFutureTasks.add(reportDownloadFutureTask);
    }

    // All callables have been submitted. Shut down the thread pool.
    threadPool.shutdown();

    // Wait for the thread pool to terminate.
    threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);

    System.out.println();
    System.out.println("All downloads completed. Results:");

    Map<String, File> successfulReports = Maps.newHashMap();
    Map<String, Exception> failedReports = Maps.newHashMap();
    for (ReportDownloadFutureTask reportDownloadFutureTask : reportDownloadFutureTasks) {
      String clientCustomerId = reportDownloadFutureTask.getClientCustomerId();
      try {
        File reportFile = reportDownloadFutureTask.get();
        successfulReports.put(clientCustomerId, reportFile);
      } catch (CancellationException | InterruptedException | ExecutionException e) {
        failedReports.put(clientCustomerId, e);
      }
    }

    System.out.println("Successful reports:");
    successfulReports.forEach(
        (clientCustomerId, reportFile) ->
            System.out.printf("\tClient ID %s => '%s'%n", clientCustomerId, reportFile));

    System.out.println("Failed reports:");
    failedReports.forEach(
        (clientCustomerId, exception) ->
            System.out.printf("\tClient ID %s => Exception: %s%n", clientCustomerId, exception));

    System.out.println("End of results.");
  }

  /**
   * Retrieves all managed customers under the manager account identified by
   * {@link AdWordsSession#getClientCustomerId()}.
   */
  private static Map<Long, ManagedCustomer> getAllManagedCustomers(
      AdWordsServicesInterface adWordsServices, ImmutableAdWordsSession session)
      throws RemoteException {
    // Get the ManagedCustomerService.
    ManagedCustomerServiceInterface managedCustomerService =
        adWordsServices.get(session, ManagedCustomerServiceInterface.class);

    SelectorBuilder selectorBuilder =
        new SelectorBuilder()
            .fields(ManagedCustomerField.CustomerId)
            .equals(ManagedCustomerField.CanManageClients, "false")
            .limit(PAGE_SIZE)
            .offset(0);

    ManagedCustomerPage managedCustomerPage;
    int offset = 0;
    Map<Long, ManagedCustomer> managedCustomers = Maps.newHashMap();
    do {
      selectorBuilder.offset(offset);
      managedCustomerPage = managedCustomerService.get(selectorBuilder.build());
      if (managedCustomerPage.getEntries() != null) {
        for (ManagedCustomer managedCustomer : managedCustomerPage.getEntries()) {
          managedCustomers.put(managedCustomer.getCustomerId(), managedCustomer);
        }
      }
      offset += PAGE_SIZE;
    } while (offset < managedCustomerPage.getTotalNumEntries());

    return managedCustomers;
  }

  /**
   * Extension of {@link FutureTask} that provides the context (client customer ID) for the task.
   */
  private static class ReportDownloadFutureTask extends FutureTask<File> {

    private final String clientCustomerId;

    public ReportDownloadFutureTask(ReportDownloadCallable callable) {
      super(callable);
      this.clientCustomerId = callable.session.getClientCustomerId();
    }

    String getClientCustomerId() {
      return clientCustomerId;
    }
  }

  /**
   * Callable for the download attempts of a single report for a single customer.
   */
  private static class ReportDownloadCallable implements Callable<File> {

    private final ImmutableAdWordsSession session;
    private final AdWordsServicesInterface adWordsServices;
    private final ReportDefinition reportDefinition;
    private final File reportOutputFile;
    private final ExponentialBackOff backOff;

    private ReportDownloadCallable(
        ImmutableAdWordsSession session,
        AdWordsServicesInterface adWordsServices,
        ReportDefinition reportDefinition,
        File reportOutputFile,
        ExponentialBackOff backOff) {
      this.session = session;
      this.adWordsServices = adWordsServices;
      this.reportDefinition = reportDefinition;
      this.reportOutputFile = reportOutputFile;
      this.backOff = backOff;
    }

    /**
     * Downloads the report for a specific customer account. On request failure, retries the request
     * using an exponential backoff strategy. If all attempts fail, throws the exception from the
     * last attempt.
     *
     * @return the output file containing the report results.
     * @throws DetailedReportDownloadResponseException if the report request failed with a detailed
     *     error from the reporting service.
     * @throws ReportDownloadResponseException if the report request failed with a general error
     *     from the reporting service.
     * @throws ReportException if the report request failed due to a transport layer error.
     * @throws IOException if the report's contents could not be written to {@code reportFile}.
     * @throws InterruptedException if the thread was interrupted while waiting between retries.
     */
    @Override
    public File call()
        throws ReportException, ReportDownloadResponseException, IOException, InterruptedException {
      long clientCustomerId = Long.valueOf(session.getClientCustomerId());
      int numberOfAttempts = 0;
      boolean doContinue = true;
      ReportException lastException = null;
      ReportDownloaderInterface reportDownloader =
          adWordsServices.getUtility(session, ReportDownloaderInterface.class);
      do {
        numberOfAttempts++;
        try {
          // Set the property api.adwords.reportDownloadTimeout or call
          // ReportDownloader.setReportDownloadTimeout to set a timeout (in milliseconds)
          // for CONNECT and READ in report downloads.
          ReportDownloadResponse response = reportDownloader.downloadReport(reportDefinition);
          response.saveToFile(reportOutputFile.getPath());

          System.out.printf(
              "Report for client customer ID %s successfully downloaded to: %s%n",
              clientCustomerId,
              reportOutputFile);
          return reportOutputFile;
        } catch (ReportException e) {
          lastException = e;
          // ReportException represents a potentially retryable error, so apply the backoff
          // policy if the max elapsed time per customer has not passed. All other exceptions
          // will be thrown.
          System.err.printf(
              "Report attempt #%d for client customer ID %s was not downloaded due to: %s%n",
              numberOfAttempts,
              session.getClientCustomerId(),
              e);
          long sleepMillis = backOff.nextBackOffMillis();
          if (sleepMillis == BackOff.STOP) {
            doContinue = false;
            lastException = new ReportException(
                "Report request failed after maximum elapsed millis: "
                    + backOff.getMaxElapsedTimeMillis(),
                e);
          } else {
            System.out.printf(
                "Sleeping %d milliseconds before retrying report for client customer ID %s.%n",
                sleepMillis,
                session.getClientCustomerId());
            Thread.sleep(sleepMillis);
          }
        }
      } while (doContinue);

      // Throw the last exception. This is only reachable if the do/while loop was unable
      // to retrieve the report within the backoff policy's max elapsed time.
      throw lastException;
    }
  }
}

Stream results from a report

// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 adwords.axis.v201809.reporting;

import static com.google.api.ads.common.lib.utils.Builder.DEFAULT_CONFIGURATION_FILENAME;
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.api.ads.adwords.axis.factory.AdWordsServices;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.adwords.lib.client.reporting.ReportingConfiguration;
import com.google.api.ads.adwords.lib.factory.AdWordsServicesInterface;
import com.google.api.ads.adwords.lib.jaxb.v201809.DownloadFormat;
import com.google.api.ads.adwords.lib.jaxb.v201809.ReportDefinitionDateRangeType;
import com.google.api.ads.adwords.lib.jaxb.v201809.ReportDefinitionReportType;
import com.google.api.ads.adwords.lib.utils.DetailedReportDownloadResponseException;
import com.google.api.ads.adwords.lib.utils.ReportDownloadResponse;
import com.google.api.ads.adwords.lib.utils.ReportDownloadResponseException;
import com.google.api.ads.adwords.lib.utils.ReportException;
import com.google.api.ads.adwords.lib.utils.v201809.ReportDownloaderInterface;
import com.google.api.ads.adwords.lib.utils.v201809.ReportQuery;
import com.google.api.ads.common.lib.auth.OfflineCredentials;
import com.google.api.ads.common.lib.auth.OfflineCredentials.Api;
import com.google.api.ads.common.lib.conf.ConfigurationLoadException;
import com.google.api.ads.common.lib.exception.OAuthException;
import com.google.api.ads.common.lib.exception.ValidationException;
import com.google.api.client.auth.oauth2.Credential;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.google.common.primitives.Longs;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.SystemUtils;

/**
 * This example streams the results of an ad hoc report, collecting total impressions
 * by campaign from each line. This demonstrates how you can extract data from a large
 * report without holding the entire result set in memory or using files.
 *
 * <p>Credentials and properties in {@code fromFile()} are pulled from the
 * "ads.properties" file. See README for more info.
 */
public class StreamCriteriaReportResults {

  public static void main(String[] args) {
    AdWordsSession session;
    try {
      // Generate a refreshable OAuth2 credential.
      Credential oAuth2Credential =
          new OfflineCredentials.Builder()
              .forApi(Api.ADWORDS)
              .fromFile()
              .build()
              .generateCredential();

      // Construct an AdWordsSession.
      session =
          new AdWordsSession.Builder().fromFile().withOAuth2Credential(oAuth2Credential).build();
    } catch (ConfigurationLoadException cle) {
      System.err.printf(
          "Failed to load configuration from the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, cle);
      return;
    } catch (ValidationException ve) {
      System.err.printf(
          "Invalid configuration in the %s file. Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, ve);
      return;
    } catch (OAuthException oe) {
      System.err.printf(
          "Failed to create OAuth credentials. Check OAuth settings in the %s file. "
              + "Exception: %s%n",
          DEFAULT_CONFIGURATION_FILENAME, oe);
      return;
    }

    AdWordsServicesInterface adWordsServices = AdWordsServices.getInstance();

    try {
      runExample(adWordsServices, session);
    } catch (DetailedReportDownloadResponseException dre) {
      // A DetailedReportDownloadResponseException will be thrown if the HTTP status code in the
      // response indicates an error occurred and the response body contains XML with further
      // information, such as the fieldPath and trigger.
      System.err.printf(
          "Report was not downloaded due to a %s with errorText '%s', trigger '%s' and "
              + "field path '%s'%n",
          dre.getClass().getSimpleName(),
          dre.getErrorText(),
          dre.getTrigger(),
          dre.getFieldPath());
    } catch (ReportDownloadResponseException rde) {
      // A ReportDownloadResponseException will be thrown if the HTTP status code in the response
      // indicates an error occurred, but the response did not contain further details.
      System.err.printf("Report was not downloaded due to: %s%n", rde);
    } catch (ReportException re) {
      // A ReportException will be thrown if the download failed due to a transport layer exception.
      System.err.printf("Report was not downloaded due to transport layer exception: %s%n", re);
    } catch (IOException ioe) {
      // An IOException in this example indicates that the report's contents could not be read from
      // the response.
      System.err.printf(
          "Report was not read due to an IOException: %s%n", ioe);
    }
  }

  /**
   * Runs the example.
   *
   * @param adWordsServices the services factory.
   * @param session the session.
   * @throws DetailedReportDownloadResponseException if the report request failed with a detailed
   *     error from the reporting service.
   * @throws ReportDownloadResponseException if the report request failed with a general error from
   *     the reporting service.
   * @throws ReportException if the report request failed due to a transport layer error.
   * @throws IOException if the report's contents could not be read from the response.
   */
  public static void runExample(AdWordsServicesInterface adWordsServices, AdWordsSession session)
      throws ReportDownloadResponseException, ReportException, IOException  {
    // Create the query.
    ReportQuery query =
        new ReportQuery.Builder()
            .fields("Id", "AdNetworkType1", "Impressions")
            .from(ReportDefinitionReportType.CRITERIA_PERFORMANCE_REPORT)
            .where("Status")
            .in("ENABLED", "PAUSED")
            .during(ReportDefinitionDateRangeType.LAST_7_DAYS)
            .build();

    // Optional: Set the reporting configuration of the session to suppress header, column name, or
    // summary rows in the report output. You can also configure this via your ads.properties
    // configuration file. See AdWordsSession.Builder.from(Configuration) for details.
    // In addition, you can set whether you want to explicitly include or exclude zero impression
    // rows.
    ReportingConfiguration reportingConfiguration =
        new ReportingConfiguration.Builder()
            // Skip all header and summary lines since the loop below expects
            // every field to be present in each line.
            .skipReportHeader(true)
            .skipColumnHeader(true)
            .skipReportSummary(true)
            // Enable to include rows with zero impressions.
            .includeZeroImpressions(false)
            .build();
    session.setReportingConfiguration(reportingConfiguration);

    ReportDownloaderInterface reportDownloader =
        adWordsServices.getUtility(session, ReportDownloaderInterface.class);

    BufferedReader reader = null;
    try {
      // Set the property api.adwords.reportDownloadTimeout or call
      // ReportDownloader.setReportDownloadTimeout to set a timeout (in milliseconds)
      // for CONNECT and READ in report downloads.
      final ReportDownloadResponse response =
          reportDownloader.downloadReport(query.toString(), DownloadFormat.CSV);
      
      // Read the response as a BufferedReader.
      reader = new BufferedReader(new InputStreamReader(response.getInputStream(), UTF_8));

      // Map to store total impressions by ad network type 1.
      Map<String, Long> impressionsByAdNetworkType1 = Maps.newTreeMap();

      // Stream the results one line at a time and perform any line-specific processing.
      String line;
      Splitter splitter = Splitter.on(',');
      while ((line = reader.readLine()) != null) {
        System.out.println(line);

        // Split the line into a list of field values.
        List<String> values = splitter.splitToList(line);

        // Update the total impressions for the ad network type 1 value.
        String adNetworkType1 = values.get(1);
        Long impressions = Longs.tryParse(values.get(2));
        if (impressions != null) {
          Long impressionsTotal = impressionsByAdNetworkType1.get(adNetworkType1);
          impressionsTotal = impressionsTotal == null ? 0L : impressionsTotal;
          impressionsByAdNetworkType1.put(adNetworkType1, impressionsTotal + impressions);
        }
      }

      // Print the impressions totals by ad network type 1.
      System.out.println();
      System.out.printf(
          "Total impressions by ad network type 1:%n%s%n",
          Joiner.on(SystemUtils.LINE_SEPARATOR).join(impressionsByAdNetworkType1.entrySet()));
    } finally {
      if (reader != null) {
        reader.close();
      }
    }
  }
}