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

Optimization Samples

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

Get keyword traffic estimates

// 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.optimization;

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.Criterion;
import com.google.api.ads.adwords.axis.v201809.cm.Keyword;
import com.google.api.ads.adwords.axis.v201809.cm.KeywordMatchType;
import com.google.api.ads.adwords.axis.v201809.cm.Language;
import com.google.api.ads.adwords.axis.v201809.cm.Location;
import com.google.api.ads.adwords.axis.v201809.cm.Money;
import com.google.api.ads.adwords.axis.v201809.o.AdGroupEstimateRequest;
import com.google.api.ads.adwords.axis.v201809.o.CampaignEstimate;
import com.google.api.ads.adwords.axis.v201809.o.CampaignEstimateRequest;
import com.google.api.ads.adwords.axis.v201809.o.KeywordEstimate;
import com.google.api.ads.adwords.axis.v201809.o.KeywordEstimateRequest;
import com.google.api.ads.adwords.axis.v201809.o.PlatformCampaignEstimate;
import com.google.api.ads.adwords.axis.v201809.o.StatsEstimate;
import com.google.api.ads.adwords.axis.v201809.o.TrafficEstimatorResult;
import com.google.api.ads.adwords.axis.v201809.o.TrafficEstimatorSelector;
import com.google.api.ads.adwords.axis.v201809.o.TrafficEstimatorServiceInterface;
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;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

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

  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 TrafficEstimatorService.
    TrafficEstimatorServiceInterface trafficEstimatorService =
        adWordsServices.get(session, TrafficEstimatorServiceInterface.class);

    // Create keywords. Refer to the TrafficEstimatorService documentation for the maximum
    // number of keywords that can be passed in a single request.
    //   https://developers.google.com/adwords/api/docs/reference/latest/TrafficEstimatorService
    List<Keyword> keywords = new ArrayList<Keyword>();
    
    Keyword marsCruiseKeyword = new Keyword();
    marsCruiseKeyword.setText("mars cruise");
    marsCruiseKeyword.setMatchType(KeywordMatchType.BROAD);
    keywords.add(marsCruiseKeyword);
    
    Keyword cheapCruiseKeyword = new Keyword();
    cheapCruiseKeyword.setText("cheap cruise");
    cheapCruiseKeyword.setMatchType(KeywordMatchType.PHRASE);
    keywords.add(cheapCruiseKeyword);
    
    Keyword cruiseKeyword = new Keyword();
    cruiseKeyword.setText("cruise");
    cruiseKeyword.setMatchType(KeywordMatchType.EXACT);
    keywords.add(cruiseKeyword);

    // Create a keyword estimate request for each keyword.
    List<KeywordEstimateRequest> keywordEstimateRequests =
        keywords.stream()
            .map(
                keyword -> {
                  KeywordEstimateRequest keywordEstimateRequest = new KeywordEstimateRequest();
                  keywordEstimateRequest.setKeyword(keyword);
                  return keywordEstimateRequest;
                })
            .collect(Collectors.toList());

    // Add a negative keyword to the traffic estimate.
    KeywordEstimateRequest negativeKeywordEstimateRequest = new KeywordEstimateRequest();
    negativeKeywordEstimateRequest.setKeyword(new Keyword(null, null, null, "hiking tour",
        KeywordMatchType.BROAD));
    negativeKeywordEstimateRequest.setIsNegative(true);
    keywordEstimateRequests.add(negativeKeywordEstimateRequest);

    // Create ad group estimate requests.
    List<AdGroupEstimateRequest> adGroupEstimateRequests = new ArrayList<AdGroupEstimateRequest>();
    AdGroupEstimateRequest adGroupEstimateRequest = new AdGroupEstimateRequest();
    adGroupEstimateRequest.setKeywordEstimateRequests(keywordEstimateRequests
        .toArray(new KeywordEstimateRequest[] {}));
    adGroupEstimateRequest.setMaxCpc(new Money(null, 1000000L));
    adGroupEstimateRequests.add(adGroupEstimateRequest);
    
    // Create campaign estimate requests.
    List<CampaignEstimateRequest> campaignEstimateRequests =
        new ArrayList<CampaignEstimateRequest>();
    CampaignEstimateRequest campaignEstimateRequest = new CampaignEstimateRequest();
    campaignEstimateRequest.setAdGroupEstimateRequests(adGroupEstimateRequests
        .toArray(new AdGroupEstimateRequest[] {}));
    
    Location unitedStates = new Location();
    unitedStates.setId(2840L);
    Language english = new Language();
    english.setId(1000L);
    campaignEstimateRequest.setCriteria(new Criterion[] {unitedStates, english});
    campaignEstimateRequests.add(campaignEstimateRequest);
    
    // Create selector.
    TrafficEstimatorSelector selector = new TrafficEstimatorSelector();
    selector.setCampaignEstimateRequests(campaignEstimateRequests
        .toArray(new CampaignEstimateRequest[] {}));
    
    // Optional: Request a list of campaign level estimates segmented by platform.
    selector.setPlatformEstimateRequested(true);

    // Get traffic estimates.
    TrafficEstimatorResult result = trafficEstimatorService.get(selector);

    // Display traffic estimates.
    if (result != null
        && result.getCampaignEstimates() != null
        && result.getCampaignEstimates().length > 0) {
      CampaignEstimate campaignEstimate = result.getCampaignEstimates()[0];

      // Display the campaign level estimates segmented by platform.
      if (campaignEstimate.getPlatformEstimates() != null) {
        for (PlatformCampaignEstimate platformEstimate : campaignEstimate.getPlatformEstimates()) {
          String platformMessage =
              String.format(
                  "Results for the platform with ID %d and name '%s':%n",
                  platformEstimate.getPlatform().getId(),
                  platformEstimate.getPlatform().getPlatformName());
          displayMeanEstimates(
              platformMessage,
              platformEstimate.getMinEstimate(),
              platformEstimate.getMaxEstimate());
        }
      }

      // Display the keyword estimates.
      KeywordEstimate[] keywordEstimates =
          campaignEstimate.getAdGroupEstimates()[0].getKeywordEstimates();
      for (int i = 0; i < keywordEstimates.length; i++) {
        if (Boolean.TRUE.equals(keywordEstimateRequests.get(i).getIsNegative())) {
          continue;
        }

        Keyword keyword = keywordEstimateRequests.get(i).getKeyword();
        KeywordEstimate keywordEstimate = keywordEstimates[i];
        
        String keywordMessage =
            String.format(
                "Results for the keyword with text '%s' and match type '%s':%n",
                keyword.getText(), keyword.getMatchType());
        displayMeanEstimates(keywordMessage, keywordEstimate.getMin(), keywordEstimate.getMax());
      }
    } else {
      System.out.println("No traffic estimates were returned.");
    }
  }

  /**
   * Writes the mean estimates for the given min and max estimates to {@code System.out},
   * prefixed with {@code message}.
   */
  private static void displayMeanEstimates(String message, StatsEstimate minEstimate,
      StatsEstimate maxEstimate) {
    // Find the mean of the min and max values.
    Double meanAverageCpc = calculateMean(minEstimate.getAverageCpc(),
        maxEstimate.getAverageCpc());
    Double meanAveragePosition = calculateMean(minEstimate.getAveragePosition(),
        maxEstimate.getAveragePosition());
    Double meanClicks = calculateMean(minEstimate.getClicksPerDay(),
        maxEstimate.getClicksPerDay());
    Double meanTotalCost = calculateMean(minEstimate.getTotalCost(),
        maxEstimate.getTotalCost());

    System.out.println(message);
    System.out.printf("\tEstimated average CPC: %s%n", formatMean(meanAverageCpc));
    System.out.printf("\tEstimated ad position: %s%n", formatMean(meanAveragePosition));
    System.out.printf("\tEstimated daily clicks: %s%n", formatMean(meanClicks));
    System.out.printf("\tEstimated daily cost: %s%n%n", formatMean(meanTotalCost));
  }

  /**
   * Returns a formatted version of {@code mean}, handling nulls.
   */
  private static String formatMean(Double mean) {
    if (mean == null) {
      // Handle nulls separately, else the %.2f format below will
      // truncate 'null' to 'nu'.
      return null;
    }
    return String.format("%.2f", mean);
  }

  /**
   * Returns the mean of the {@code microAmount} of the two Money values if neither is null, else
   * returns null.
   */
  private static Double calculateMean(Money minMoney, Money maxMoney) {
    if (minMoney == null || maxMoney == null) {
      return null;
    }
    return calculateMean(minMoney.getMicroAmount(), maxMoney.getMicroAmount());
  }

  /**
   * Returns the mean of the two Number values if neither is null, else returns null.
   */
  private static Double calculateMean(Number min, Number max) {
    if (min == null || max == null) {
      return null;
    }
    return (min.doubleValue() + max.doubleValue()) / 2;
  }
}

Get all mobile bid modifier landscapes for a campaign

// 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.optimization;

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

import com.beust.jcommander.Parameter;
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.cm.BidLandscapeLandscapePoint;
import com.google.api.ads.adwords.axis.v201809.cm.CriterionBidLandscape;
import com.google.api.ads.adwords.axis.v201809.cm.CriterionBidLandscapePage;
import com.google.api.ads.adwords.axis.v201809.cm.DataServiceInterface;
import com.google.api.ads.adwords.axis.v201809.cm.Selector;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.adwords.lib.factory.AdWordsServicesInterface;
import com.google.api.ads.adwords.lib.selectorfields.v201809.cm.DataField;
import com.google.api.ads.adwords.lib.utils.examples.ArgumentNames;
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.ads.common.lib.utils.examples.CodeSampleParams;
import com.google.api.client.auth.oauth2.Credential;
import java.rmi.RemoteException;

/**
 * This example gets all available campaign mobile bid modifier landscapes
 * for a given campaign. To get campaigns, run basicoperations.GetCampaigns.
 *
 * <p>Credentials and properties in {@code fromFile()} are pulled from the
 * "ads.properties" file. See README for more info.
 */
public class GetCampaignCriterionBidModifierSimulations {

  private static final int PAGE_SIZE = 100;

  private static class GetCampaignCriterionBidModifierSimulationsParams extends CodeSampleParams {
    @Parameter(names = ArgumentNames.CAMPAIGN_ID, required = true)
    private Long campaignId;
  }

  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();

    GetCampaignCriterionBidModifierSimulationsParams params =
        new GetCampaignCriterionBidModifierSimulationsParams();
    if (!params.parseArguments(args)) {
      // Either pass the required parameters for this example on the command line, or insert them
      // into the code here. See the parameter class definition above for descriptions.
      params.campaignId = Long.parseLong("INSERT_CAMPAIGN_ID_HERE");
    }

    try {
      runExample(adWordsServices, session, params.campaignId);
    } 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.
   * @param campaignId the ID of the campaign.
   * @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, Long campaignId)
      throws RemoteException {
    // Get the DataService.
    DataServiceInterface dataService = adWordsServices.get(session, DataServiceInterface.class);

    // Create selector.
    Selector selector =
        new SelectorBuilder()
            .fields(
                DataField.BidModifier,
                DataField.CampaignId,
                DataField.CriterionId,
                DataField.StartDate,
                DataField.EndDate,
                DataField.LocalClicks,
                DataField.LocalCost,
                DataField.LocalImpressions,
                DataField.TotalLocalClicks,
                DataField.TotalLocalCost,
                DataField.TotalLocalImpressions,
                DataField.RequiredBudget)
            .equals(DataField.CampaignId, campaignId.toString())
            .limit(PAGE_SIZE)
            .build();

    // Display bid landscapes.
    int landscapePointsInPreviousPage = 0;
    int startIndex = 0;
    do {
      // Offset the start index by the number of landscape points in the last retrieved page,
      // NOT the number of entries (bid landscapes) in the page.
      startIndex += landscapePointsInPreviousPage;
      selector.getPaging().setStartIndex(startIndex);

      // Reset the count of landscape points in preparation for processing the next page.
      landscapePointsInPreviousPage = 0;

      // Request the next page of bid landscapes.
      CriterionBidLandscapePage page = dataService.getCampaignCriterionBidLandscape(selector);

      if (page.getEntries() != null) {
        for (CriterionBidLandscape criterionBidLandscape : page.getEntries()) {
          System.out.printf(
              "Found campaign-level criterion bid modifier landscape for"
                  + " criterion with ID %d, start date '%s', end date '%s', and"
                  + " landscape points:%n",
              criterionBidLandscape.getCriterionId(),
              criterionBidLandscape.getStartDate(),
              criterionBidLandscape.getEndDate());

          for (BidLandscapeLandscapePoint bidLandscapePoint :
              criterionBidLandscape.getLandscapePoints()) {
            landscapePointsInPreviousPage++;
            System.out.printf(
                "  {bid modifier: %.2f => clicks: %d, cost: %d, impressions: %d, "
                    + "total clicks: %d, total cost: %d, total impressions: %d, and "
                    + "required budget: %d%n",
                bidLandscapePoint.getBidModifier(),
                bidLandscapePoint.getClicks(),
                bidLandscapePoint.getCost().getMicroAmount(),
                bidLandscapePoint.getImpressions(),
                bidLandscapePoint.getTotalLocalClicks(),
                bidLandscapePoint.getTotalLocalCost().getMicroAmount(),
                bidLandscapePoint.getTotalLocalImpressions(),
                bidLandscapePoint.getRequiredBudget().getMicroAmount());
          }
        }
      }
    } while (landscapePointsInPreviousPage >= PAGE_SIZE);
  }
}

Get a bid landscape for an ad group and criterion

// 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.optimization;

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

import com.beust.jcommander.Parameter;
import com.google.api.ads.adwords.axis.factory.AdWordsServices;
import com.google.api.ads.adwords.axis.utils.v201809.ServiceQuery;
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.BidLandscapeLandscapePoint;
import com.google.api.ads.adwords.axis.v201809.cm.CriterionBidLandscape;
import com.google.api.ads.adwords.axis.v201809.cm.CriterionBidLandscapePage;
import com.google.api.ads.adwords.axis.v201809.cm.DataServiceInterface;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.adwords.lib.factory.AdWordsServicesInterface;
import com.google.api.ads.adwords.lib.selectorfields.v201809.cm.DataField;
import com.google.api.ads.adwords.lib.utils.examples.ArgumentNames;
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.ads.common.lib.utils.examples.CodeSampleParams;
import com.google.api.client.auth.oauth2.Credential;
import java.rmi.RemoteException;

/**
 * This example gets a bid landscape for an ad group and a criterion. To get ad
 * groups, run GetAdGroups.java. To get criteria, run GetKeywords.java.
 *
 * <p>Credentials and properties in {@code fromFile()} are pulled from the
 * "ads.properties" file. See README for more info.
 */
public class GetKeywordBidSimulations {

  private static final int PAGE_SIZE = 100;

  private static class GetKeywordBidSimulationsParams extends CodeSampleParams {
    @Parameter(names = ArgumentNames.AD_GROUP_ID, required = true)
    private Long adGroupId;

    @Parameter(names = ArgumentNames.CRITERION_ID, required = true)
    private Long criterionId;
  }

  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();

    GetKeywordBidSimulationsParams params = new GetKeywordBidSimulationsParams();
    if (!params.parseArguments(args)) {
      // Either pass the required parameters for this example on the command line, or insert them
      // into the code here. See the parameter class definition above for descriptions.
      params.adGroupId = Long.parseLong("INSERT_AD_GROUP_ID_HERE");
      params.criterionId = Long.parseLong("INSERT_CRITERION_ID_HERE");
    }

    try {
      runExample(adWordsServices, session, params.adGroupId, params.criterionId);
    } 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.
   * @param adGroupId the ID of the ad group.
   * @param criterionId the ID of the criterion.
   * @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, Long adGroupId,
      Long criterionId) throws RemoteException {
    // Get the DataService.
    DataServiceInterface dataService = adWordsServices.get(session, DataServiceInterface.class);

    // Create a query to select all keyword bid simulations for the ad group.
    ServiceQuery query =
        new ServiceQuery.Builder()
            .fields(
                DataField.AdGroupId,
                DataField.CriterionId,
                DataField.StartDate,
                DataField.EndDate,
                DataField.Bid,
                DataField.BiddableConversions,
                DataField.BiddableConversionsValue,
                DataField.LocalClicks,
                DataField.LocalCost,
                DataField.LocalImpressions)
            .where(DataField.AdGroupId)
            .equalTo(adGroupId)
            .where(DataField.CriterionId)
            .equalTo(criterionId)
            .limit(0, PAGE_SIZE)
            .build();

    // Display bid landscapes.
    CriterionBidLandscapePage page = null;
    do {
      query.nextPage(page);
      // Retrieve keyword bid simulations one page at a time, continuing to request pages until all
      // of them have been retrieved.
      page = dataService.queryCriterionBidLandscape(query.toString());

      if (page.getEntries() != null) {
        for (CriterionBidLandscape criterionBidLandscape : page.getEntries()) {
          System.out.printf(
              "Criterion bid landscape with ad group ID %d, criterion ID %d, "
                  + "start date %s, end date %s, with landscape points:%n",
              criterionBidLandscape.getAdGroupId(),
              criterionBidLandscape.getCriterionId(),
              criterionBidLandscape.getStartDate(),
              criterionBidLandscape.getEndDate());

          for (BidLandscapeLandscapePoint bidLanscapePoint :
              criterionBidLandscape.getLandscapePoints()) {
            System.out.printf(
                "\t{bid: %d clicks: %d cost: %d impressions: %d, biddable conversions: %.2f, "
                    + "biddable conversions value: %.2f}%n",
                bidLanscapePoint.getBid().getMicroAmount(),
                bidLanscapePoint.getClicks(),
                bidLanscapePoint.getCost().getMicroAmount(),
                bidLanscapePoint.getImpressions(),
                bidLanscapePoint.getBiddableConversions(),
                bidLanscapePoint.getBiddableConversionsValue());
          }
          System.out.println(" was found.");
        }
      }
    } while (query.hasNext(page));
  }
}

// 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.optimization;

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

import com.beust.jcommander.Parameter;
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.Language;
import com.google.api.ads.adwords.axis.v201809.cm.Money;
import com.google.api.ads.adwords.axis.v201809.cm.NetworkSetting;
import com.google.api.ads.adwords.axis.v201809.cm.Paging;
import com.google.api.ads.adwords.axis.v201809.o.Attribute;
import com.google.api.ads.adwords.axis.v201809.o.AttributeType;
import com.google.api.ads.adwords.axis.v201809.o.DoubleAttribute;
import com.google.api.ads.adwords.axis.v201809.o.IdeaType;
import com.google.api.ads.adwords.axis.v201809.o.IntegerSetAttribute;
import com.google.api.ads.adwords.axis.v201809.o.LanguageSearchParameter;
import com.google.api.ads.adwords.axis.v201809.o.LongAttribute;
import com.google.api.ads.adwords.axis.v201809.o.MoneyAttribute;
import com.google.api.ads.adwords.axis.v201809.o.NetworkSearchParameter;
import com.google.api.ads.adwords.axis.v201809.o.RelatedToQuerySearchParameter;
import com.google.api.ads.adwords.axis.v201809.o.RequestType;
import com.google.api.ads.adwords.axis.v201809.o.SearchParameter;
import com.google.api.ads.adwords.axis.v201809.o.SeedAdGroupIdSearchParameter;
import com.google.api.ads.adwords.axis.v201809.o.StringAttribute;
import com.google.api.ads.adwords.axis.v201809.o.TargetingIdea;
import com.google.api.ads.adwords.axis.v201809.o.TargetingIdeaPage;
import com.google.api.ads.adwords.axis.v201809.o.TargetingIdeaSelector;
import com.google.api.ads.adwords.axis.v201809.o.TargetingIdeaServiceInterface;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.adwords.lib.factory.AdWordsServicesInterface;
import com.google.api.ads.adwords.lib.utils.examples.ArgumentNames;
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.ads.common.lib.utils.Maps;
import com.google.api.ads.common.lib.utils.examples.CodeSampleParams;
import com.google.api.client.auth.oauth2.Credential;
import com.google.common.base.Joiner;
import com.google.common.primitives.Ints;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
 * This example gets keywords related to a seed keyword.
 *
 * <p>Credentials and properties in {@code fromFile()} are pulled from the
 * "ads.properties" file. See README for more info.
 */
public class GetKeywordIdeas {

  private static class GetKeywordIdeasParams extends CodeSampleParams {
    @Parameter(names = ArgumentNames.AD_GROUP_ID)
    private Long adGroupId;
  }

  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();

    GetKeywordIdeasParams params = new GetKeywordIdeasParams();
    if (!params.parseArguments(args)) {
      // Either pass the parameters for this example on the command line, or insert them
      // into the code here. See the parameter class definition above for descriptions.
      // If you do not want to use an existing ad group to seed your request, you can
      // leave this set to null.
      params.adGroupId = null;
    }
    try {
      runExample(adWordsServices, session, params.adGroupId);
    } 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.
   * @param adGroupId the optional ID of the seed ad group.
   * @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, @Nullable Long adGroupId)
      throws RemoteException {
    // Get the TargetingIdeaService.
    TargetingIdeaServiceInterface targetingIdeaService =
        adWordsServices.get(session, TargetingIdeaServiceInterface.class);

    // Create selector.
    TargetingIdeaSelector selector = new TargetingIdeaSelector();
    selector.setRequestType(RequestType.IDEAS);
    selector.setIdeaType(IdeaType.KEYWORD);
    selector.setRequestedAttributeTypes(new AttributeType[] {
        AttributeType.KEYWORD_TEXT,
        AttributeType.SEARCH_VOLUME,
        AttributeType.AVERAGE_CPC,
        AttributeType.COMPETITION,
        AttributeType.CATEGORY_PRODUCTS_AND_SERVICES});

    // Set selector paging (required for targeting idea service).
    Paging paging = new Paging();
    paging.setStartIndex(0);
    paging.setNumberResults(10);
    selector.setPaging(paging);

    List<SearchParameter> searchParameters = new ArrayList<>();
    // Create related to query search parameter.
    RelatedToQuerySearchParameter relatedToQuerySearchParameter =
        new RelatedToQuerySearchParameter();
    relatedToQuerySearchParameter.setQueries(new String[] {"bakery", "pastries", "birthday cake"});
    searchParameters.add(relatedToQuerySearchParameter);

    // Language setting (optional).
    // The ID can be found in the documentation:
    //   https://developers.google.com/adwords/api/docs/appendix/languagecodes
    // See the documentation for limits on the number of allowed language parameters:
    //   https://developers.google.com/adwords/api/docs/reference/latest/TargetingIdeaService.LanguageSearchParameter
    LanguageSearchParameter languageParameter = new LanguageSearchParameter();
    Language english = new Language();
    english.setId(1000L);
    languageParameter.setLanguages(new Language[] {english});
    searchParameters.add(languageParameter);

    // Create network search parameter (optional).
    NetworkSetting networkSetting = new NetworkSetting();
    networkSetting.setTargetGoogleSearch(true);
    networkSetting.setTargetSearchNetwork(false);
    networkSetting.setTargetContentNetwork(false);
    networkSetting.setTargetPartnerSearchNetwork(false);

    NetworkSearchParameter networkSearchParameter = new NetworkSearchParameter();
    networkSearchParameter.setNetworkSetting(networkSetting);
    searchParameters.add(networkSearchParameter);

    // Optional: Use an existing ad group to generate ideas.
    if (adGroupId != null) {
      SeedAdGroupIdSearchParameter seedAdGroupIdSearchParameter =
          new SeedAdGroupIdSearchParameter();
      seedAdGroupIdSearchParameter.setAdGroupId(adGroupId);
      searchParameters.add(seedAdGroupIdSearchParameter);
    }
    selector.setSearchParameters(
        searchParameters.toArray(new SearchParameter[searchParameters.size()]));

    // Get keyword ideas.
    TargetingIdeaPage page = targetingIdeaService.get(selector);

    // Display keyword ideas.
    for (TargetingIdea targetingIdea : page.getEntries()) {
      Map<AttributeType, Attribute> data = Maps.toMap(targetingIdea.getData());
      StringAttribute keyword = (StringAttribute) data.get(AttributeType.KEYWORD_TEXT);

      IntegerSetAttribute categories =
          (IntegerSetAttribute) data.get(AttributeType.CATEGORY_PRODUCTS_AND_SERVICES);
      String categoriesString = "(none)";
      if (categories != null && categories.getValue() != null) {
        categoriesString = Joiner.on(", ").join(Ints.asList(categories.getValue()));
      }
      Long averageMonthlySearches =
          ((LongAttribute) data.get(AttributeType.SEARCH_VOLUME))
              .getValue();
      Money averageCpc =
          ((MoneyAttribute) data.get(AttributeType.AVERAGE_CPC)).getValue();
      Double competition =
          ((DoubleAttribute) data.get(AttributeType.COMPETITION)).getValue();
      System.out.printf("Keyword with text '%s', average monthly search volume %d, "
          + "average CPC %d, and competition %.2f "
          + "was found with categories: %s%n", keyword.getValue(), averageMonthlySearches,
          averageCpc.getMicroAmount(), competition,
          categoriesString);
    }

    if (page.getEntries() == null) {
      System.out.println("No related keywords were found.");
    }
  }
}