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

Campaign Management Samples

The code samples below provide examples for managing campaigns using the AdWords API. Client Library.

Add a campaign group and set its performance target

#!/usr/bin/perl -w
#
# 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.
#
# This code example adds a campaign group and sets a performance target for
# that group. To get campaigns, run get_campaigns.pl. To download reports, run
# download_criteria_report_with_awql.pl.

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::Campaign;
use Google::Ads::AdWords::v201809::CampaignGroup;
use Google::Ads::AdWords::v201809::CampaignGroupOperation;
use Google::Ads::AdWords::v201809::CampaignGroupPerformanceTarget;
use Google::Ads::AdWords::v201809::CampaignGroupPerformanceTargetOperation;
use Google::Ads::AdWords::v201809::CampaignOperation;
use Google::Ads::AdWords::v201809::Money;
use Google::Ads::AdWords::v201809::PerformanceTarget;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Replace with valid values of your account.
my $campaign_ids = ["INSERT_CAMPAIGN_ID_1_HERE", "INSERT_CAMPAIGN_ID_2_HERE"];

# Example main subroutine.
sub add_campaign_groups_and_performance_targets {
  my $client       = shift;
  my $campaign_ids = shift;

  my $campaign_group = _create_campaign_group($client);
  _add_campaigns_to_group($client, $campaign_group->get_id(), $campaign_ids);
  _create_performance_target($client, $campaign_group->get_id());
  printf(
    "Campaign group and its performance target were setup successfully.\n");
  return 1;
}

# Create a campaign group.
sub _create_campaign_group {
  my ($client) = @_;

  # Create the campaign group.
  my $campaign_group = Google::Ads::AdWords::v201809::CampaignGroup->new({
    name => sprintf("Mars campaign group - #%s", uniqid()),
  });
  # Create the operation.
  my $operation = Google::Ads::AdWords::v201809::CampaignGroupOperation->new({
    operator => "ADD",
    operand  => $campaign_group
  });

  my $result =
    $client->CampaignGroupService()->mutate({operations => [$operation]});

  $campaign_group = $result->get_value()->[0];

  printf(
    "Campaign group with ID %d and name '%s' was created.\n",
    $campaign_group->get_id(),
    $campaign_group->get_name());
  return $campaign_group;
}

# Add multiple campaigns to the campaign group.
sub _add_campaigns_to_group {
  my ($client, $campaign_group_id, $campaign_ids) = @_;

  my @operations = ();
  foreach my $campaign_id (@{$campaign_ids}) {
    my $campaign = Google::Ads::AdWords::v201809::Campaign->new({
      id              => $campaign_id,
      campaignGroupId => $campaign_group_id
    });

    # Create operation.
    my $campaign_operation =
      Google::Ads::AdWords::v201809::CampaignOperation->new({
        operator => "SET",
        operand  => $campaign
      });
    push @operations, $campaign_operation;
  }

  # Add campaigns.
  my $result = $client->CampaignService()->mutate({operations => \@operations});

  # Display campaigns.
  foreach my $campaign (@{$result->get_value()}) {
    printf "Campaign with ID %d was added to campaign group with ID %d.\n",
      $campaign->get_id(), $campaign->get_campaignGroupId();
  }
}

# Creates a performance target for the campaign group.
sub _create_performance_target {
  my ($client, $campaign_group_id) = @_;

  # Start the performance target today, and run it for the next 90 days.
  my (undef, undef, undef, $mday, $mon, $year) = localtime(time);
  my $start_date = sprintf("%d%02d%02d", ($year + 1900), ($mon + 1), $mday);
  (undef, undef, undef, $mday, $mon, $year) =
    localtime(time + 60 * 60 * 24 * 90);
  my $end_date = sprintf("%d%02d%02d", ($year + 1900), ($mon + 1), $mday);

  my $performance_target =
    Google::Ads::AdWords::v201809::PerformanceTarget->new({
      # Keep the CPC for the campaigns < $3.
      efficiencyTargetType  => "CPC_LESS_THAN_OR_EQUAL_TO",
      efficiencyTargetValue => 3000000,
      # Keep the maximum spend under $50.
      spendTargetType => "MAXIMUM",
      spendTarget =>
        Google::Ads::AdWords::v201809::Money->new({microAmount => 500000000}),
      # Aim for at least 3000 clicks.
      volumeTargetValue => 3000,
      volumeGoalType    => "MAXIMIZE_CLICKS",
      startDate         => $start_date,
      endDate           => $end_date
    });

  # Create the performance target.
  my $campaign_group_performance_target =
    Google::Ads::AdWords::v201809::CampaignGroupPerformanceTarget->new({
      campaignGroupId   => $campaign_group_id,
      performanceTarget => $performance_target
    });
  # Create the operation.
  my $operation =
    Google::Ads::AdWords::v201809::CampaignGroupPerformanceTargetOperation->new(
    {
      operator => "ADD",
      operand  => $campaign_group_performance_target
    });

  my $result =
    $client->CampaignGroupPerformanceTargetService()
    ->mutate({operations => [$operation]});

  $campaign_group_performance_target = $result->get_value()->[0];

  printf(
    "Campaign performance target with ID %d was added for campaign group ID " .
    "%d.\n",
    $campaign_group_performance_target->get_id(),
    $campaign_group_performance_target->get_campaignGroupId());
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
add_campaign_groups_and_performance_targets($client, $campaign_ids);

Add a label to multiple campaigns

#!/usr/bin/perl -w
#
# 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.
#
# This example adds a label to multiple campaigns.

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::Campaign;
use Google::Ads::AdWords::v201809::CampaignLabel;
use Google::Ads::AdWords::v201809::CampaignLabelOperation;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Replace with valid values of your account.
my $campaign_ids = ["INSERT_CAMPAIGN_ID_1_HERE", "INSERT_CAMPAIGN_ID_2_HERE"];
my $label_id = "INSERT_LABEL_ID_HERE";

# Example main subroutine.
sub add_campaign_labels {
  my $client       = shift;
  my $campaign_ids = shift;
  my $label_id     = shift;

  my @operations = ();

  # Create label operations.
  foreach my $campaign_id (@{$campaign_ids}) {
    my $campaign_label = Google::Ads::AdWords::v201809::CampaignLabel->new({
        campaignId => $campaign_id,
        labelId    => $label_id
    });
    my $campaign_label_operation =
      Google::Ads::AdWords::v201809::CampaignLabelOperation->new({
        operator => "ADD",
        operand  => $campaign_label
      });
    push @operations, $campaign_label_operation;
  }

  # Add campaign labels.
  my $result =
    $client->CampaignService()->mutateLabel({operations => \@operations});

  # Display campaign labels.
  foreach my $campaign_label (@{$result->get_value()}) {
    printf "Campaign label for campaign ID %d and label ID %d was added.\n",
      $campaign_label->get_campaignId(), $campaign_label->get_labelId();
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
add_campaign_labels($client, $campaign_ids, $label_id);

Add a campaign using BatchJobService

#!/usr/bin/perl -w
#
# 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.
#
# This example illustrates how to perform multiple requests using the
# BatchJobService.

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::AdGroup;
use Google::Ads::AdWords::v201809::AdGroupAd;
use Google::Ads::AdWords::v201809::AdGroupAdOperation;
use Google::Ads::AdWords::v201809::AdGroupCriterionOperation;
use Google::Ads::AdWords::v201809::AdGroupOperation;
use Google::Ads::AdWords::v201809::BatchJob;
use Google::Ads::AdWords::v201809::BatchJobOperation;
use Google::Ads::AdWords::v201809::BiddableAdGroupCriterion;
use Google::Ads::AdWords::v201809::Budget;
use Google::Ads::AdWords::v201809::BudgetOperation;
use Google::Ads::AdWords::v201809::CampaignCriterionOperation;
use Google::Ads::AdWords::v201809::CampaignOperation;
use Google::Ads::AdWords::v201809::CpcBid;
use Google::Ads::AdWords::v201809::ExpandedTextAd;
use Google::Ads::AdWords::v201809::Keyword;
use Google::Ads::AdWords::v201809::Money;
use Google::Ads::AdWords::v201809::NegativeCampaignCriterion;
use Google::Ads::AdWords::v201809::Predicate;
use Google::Ads::AdWords::v201809::Paging;
use Google::Ads::AdWords::v201809::Selector;
use Google::Ads::AdWords::Utilities::BatchJobHandler;
use Google::Ads::AdWords::Utilities::BatchJobHandlerError;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Set a timeout to fail if the job does not complete in a specified amount
# of time.
use constant JOB_TIMEOUT_IN_MILLISECONDS => 180000;
# The time to sleep in between polls will start at this time. Then an
# exponential back-off will be instituted.
use constant JOB_BASE_WAITTIME_IN_MILLISECONDS => 30000;
use constant NUMBER_OF_CAMPAIGNS_TO_ADD        => 2;
use constant NUMBER_OF_ADGROUPS_TO_ADD         => 2;
use constant NUMBER_OF_KEYWORDS_TO_ADD         => 5;
# Temporary IDs are negative. Keep decrementing the number in order to always
# have a unique temporary ID.
my $temporary_id = -1;

# Example main subroutine.
sub add_complete_campaign_using_batch_job {
  my $client = shift;

  # Create a batch job, which returns an upload URL to upload the batch
  # operations and a job ID to track the job.
  my $operation = Google::Ads::AdWords::v201809::BatchJobOperation->new({
      operator => 'ADD',
      operand  => Google::Ads::AdWords::v201809::BatchJob->new({})});

  my $batch_job_result =
    $client->BatchJobService()->mutate({operations => [$operation]});

  if (!$batch_job_result->get_value()) {
    print "A batch job could not be created. No operations were uploaded.\n";
    return 1;
  }

  my $batch_job = $batch_job_result->get_value()->[0];

  printf("Batch job with ID %d was created.\n\tInitial upload URL: '%s'\n",
    $batch_job->get_id(), $batch_job->get_uploadUrl()->get_url());

  my @operations = ();
  # Create and add an operation to create a new budget.
  my $budget_operation = _create_budget_operation();
  push @operations, $budget_operation;

  # Create and add operations to create new campaigns.
  my @campaign_operations = _create_campaign_operations($budget_operation);
  push @operations, @campaign_operations;

  # Create and add operations to create new negative keyword criteria
  # for each campaign.
  my @campaign_criterion_operations =
    _create_campaign_criterion_operations(@campaign_operations);
  push @operations, @campaign_criterion_operations;

  # Create and add operations to create new ad groups.
  my @ad_group_operations = _create_ad_group_operations(@campaign_operations);
  push @operations, @ad_group_operations;

  # Create and add operations to create new ad group criteria (keywords).
  my @ad_group_criterion_operations =
    _create_ad_group_criterion_operations(@ad_group_operations);
  push @operations, @ad_group_criterion_operations;

  # Create and add operations to create new ad group ads (text ads).
  my @ad_group_ad_operations =
    _create_ad_group_ad_operations(@ad_group_operations);
  push @operations, @ad_group_ad_operations;

  # Get an instance of a utility that helps with uploading batch operations.
  my $batch_job_handler =
    Google::Ads::AdWords::Utilities::BatchJobHandler->new({client => $client});

  # Convert the list of operations into XML.
  # POST the XML to the upload URL.
  my $upload_result =
    $batch_job_handler->upload_operations(\@operations,
    $batch_job->get_uploadUrl()->get_url());
  if (!$upload_result) {
    printf("%s Error. %s\n",
      $upload_result->get_type(),
      $upload_result->get_description());
    if ($upload_result->get_type() eq "HTTP") {
      printf(
        "Type: %s\nCode: %s\nMessage: %s\nTrigger: %s\nField Path: %s\n",
        $upload_result->get_http_type(),
        $upload_result->get_http_response_code(),
        $upload_result->get_http_response_message(),
        $upload_result->get_http_trigger(),
        $upload_result->get_http_field_path());
    }
    return 1;
  }

  printf("\tOperations uploaded to upload URL: '%s'\n",
    $upload_result->get_resumable_upload_uri());

  # Verify that the operations completed.
  # This will poll until the job has completed or the timeout has expired.
  my $verify_result =
    _verify_operations($client, $batch_job_handler, $batch_job);

  # Display the results.
  my $error             = $verify_result->{error};
  my $job_result        = $verify_result->{batch_job};
  my $operations_result = $verify_result->{result};

  printf("Job with ID %d has completed with a status of '%s'.\n",
    $job_result->get_id(), $job_result->get_status());
  if (defined $error) {
    printf("%s Error. %s\n", $error->get_type(), $error->get_description());
    if ($error->get_type() eq "PROCESSING") {
      printf("%s\n", $error->get_processing_errors());
    }
    if ($error->get_type() eq "HTTP") {
      printf(
        "Type: %s\nCode: %s\nMessage: %s\nTrigger: %s\nField Path: %s\n",
        $error->get_http_type(),             $error->get_http_response_code(),
        $error->get_http_response_message(), $error->get_http_trigger(),
        $error->get_http_field_path());
    }
  }
  if ($operations_result) {
    for my $item (@{$operations_result->get_rval()}) {
      if ($item->get_errorList()) {
        printf("Error on index %d: %s\n",
          $item->get_index(), $item->get_errorList()->get_errors());
      } else {
        # Print the XML output.
        printf("Successful on index %d\n%s\n", $item->get_index(), $item);
      }
    }
  }

  return 1;
}

# Create a BudgetOperation for the Campaign.
sub _create_budget_operation {
  my $budget = Google::Ads::AdWords::v201809::Budget->new({
    # Required attributes.
    budgetId => _next_id(),
    name     => "Interplanetary budget #" . uniqid(),
    amount =>
      Google::Ads::AdWords::v201809::Money->new({microAmount => 5000000}),
    deliveryMethod => "STANDARD"
  });

  my $budget_operation = Google::Ads::AdWords::v201809::BudgetOperation->new({
    operator => "ADD",
    operand  => $budget
  });
  return $budget_operation;
}

# Create a CampaignOperations to add Campaigns.
sub _create_campaign_operations {
  my ($budget_operation) = @_;

  my @campaign_operations = ();

  for (my $i = 1 ; $i < NUMBER_OF_CAMPAIGNS_TO_ADD ; $i++) {
    my $campaign = Google::Ads::AdWords::v201809::Campaign->new({
        id                     => _next_id(),
        name                   => "Batch Campaign #" . uniqid(),
        advertisingChannelType => "SEARCH",
        # Recommendation: Set the campaign to PAUSED when creating it to stop
        # the ads from immediately serving. Set to ENABLED once you've added
        # targeting and the ads are ready to serve.
        status                 => "PAUSED",
        # Bidding strategy (required).
        biddingStrategyConfiguration =>
          Google::Ads::AdWords::v201809::BiddingStrategyConfiguration->new({
            biddingStrategyType => "MANUAL_CPC",
            # You can optionally provide a bidding scheme in place of the type.
          }
          ),
        budget => Google::Ads::AdWords::v201809::Budget->new(
          {budgetId => $budget_operation->get_operand()->get_budgetId()})});
    my $campaign_operation =
      Google::Ads::AdWords::v201809::CampaignOperation->new({
        operator => "ADD",
        operand  => $campaign
      });
    push @campaign_operations, $campaign_operation;
  }
  return @campaign_operations;
}

# Create CampaignCriterionOperations.
sub _create_campaign_criterion_operations() {
  my (@campaign_operations) = @_;

  my @campaign_criterion_operations = ();

  for my $campaign_operation (@campaign_operations) {
    # Create keyword.
    my $keyword = Google::Ads::AdWords::v201809::Keyword->new(
      {matchType => "BROAD", text => "venus"});

    my $negative_criterion =
      Google::Ads::AdWords::v201809::NegativeCampaignCriterion->new({
        campaignId => $campaign_operation->get_operand()->get_id(),
        criterion  => $keyword
      });

    # Create operation.
    my $negative_criterion_operation =
      Google::Ads::AdWords::v201809::CampaignCriterionOperation->new({
        operator => "ADD",
        operand  => $negative_criterion
      });

    push @campaign_criterion_operations, $negative_criterion_operation;
  }
  return @campaign_criterion_operations;
}

# Create AdGroupOperations to add AdGroups.
sub _create_ad_group_operations {
  my (@campaign_operations) = @_;

  my @ad_group_operations = ();
  for my $campaign_operation (@campaign_operations) {
    for (my $i = 1 ; $i < NUMBER_OF_ADGROUPS_TO_ADD ; $i++) {
      my $ad_group = Google::Ads::AdWords::v201809::AdGroup->new({
          id         => _next_id(),
          campaignId => $campaign_operation->get_operand()->get_id(),
          name       => "Batch Ad Group #" . uniqid(),
          biddingStrategyConfiguration =>
            Google::Ads::AdWords::v201809::BiddingStrategyConfiguration->new({
              bids => [
                Google::Ads::AdWords::v201809::CpcBid->new({
                    bid => Google::Ads::AdWords::v201809::Money->new(
                      {microAmount => 10000000})})]})});
      my $ad_group_operation =
        Google::Ads::AdWords::v201809::AdGroupOperation->new({
          operator => "ADD",
          operand  => $ad_group
        });
      push @ad_group_operations, $ad_group_operation;
    }
  }
  return @ad_group_operations;
}

# Create AdGroupCriterionOperations.
sub _create_ad_group_criterion_operations {
  my (@ad_group_operations) = @_;

  my @ad_group_criterion_operations = ();

  for my $ad_group_operation (@ad_group_operations) {
    for (my $i = 1 ; $i < NUMBER_OF_KEYWORDS_TO_ADD ; $i++) {
      # Create keyword.
      my $keyword = Google::Ads::AdWords::v201809::Keyword->new({
          matchType => "BROAD",
          # Make 50% of keywords invalid to demonstrate error handling.
          text => sprintf("mars%d%s", $i, (($i % 2 == 0) ? "!!!" : ""))});

      # Create biddable ad group criterion.
      my $ad_group_criterion =
        Google::Ads::AdWords::v201809::BiddableAdGroupCriterion->new({
          adGroupId => $ad_group_operation->get_operand()->get_id(),
          criterion => $keyword
        });

      # Create operation.
      my $ad_group_criterion_operation =
        Google::Ads::AdWords::v201809::AdGroupCriterionOperation->new({
          operator => "ADD",
          operand  => $ad_group_criterion
        });
      push @ad_group_criterion_operations, $ad_group_criterion_operation;
    }
  }
  return @ad_group_criterion_operations;
}

# Create AdGroupAdOperations.
sub _create_ad_group_ad_operations() {
  my (@ad_group_operations) = @_;

  my @ad_group_ad_operations = ();
  for my $ad_group_operation (@ad_group_operations) {
    my $ad_group_id = $ad_group_operation->get_operand()->get_id();

    my $text_ad = Google::Ads::AdWords::v201809::ExpandedTextAd->new({
        headlinePart1 => "Cruise to Mars",
        headlinePart2 => "Visit the Red Planet in style.",
        description   => "Low-gravity for everyone!",
        finalUrls     => ["http://www.example.com/1"]});

    # Create ad group ad for the text ad.
    my $text_ad_group_ad = Google::Ads::AdWords::v201809::AdGroupAd->new({
      adGroupId => $ad_group_id,
      ad        => $text_ad,
      # Additional properties (non-required).
      status => "PAUSED"
    });

    # Create operation.
    my $text_ad_group_ad_operation =
      Google::Ads::AdWords::v201809::AdGroupAdOperation->new({
        operator => "ADD",
        operand  => $text_ad_group_ad
      });

    push @ad_group_ad_operations, $text_ad_group_ad_operation;
  }
  return @ad_group_ad_operations;
}

# Return the next available temporary ID.
sub _next_id() {
  return $temporary_id--;
}

# Poll for completion of the batch job using an exponential back off
# waiting until the progress of the job is DONE or CANCELED.
# This returns a hash of return values:
#   error => undef if no error and BatchJobHandlerError if an error occurred
#   batch_job => the batch job with the id, status, progressStats,
#                processingErrors, and downloadUrl
#   result => BatchJobOpsService::mutateResponse object with contents from the
#             job's download URL
sub _verify_operations {
  my ($client, $batch_job_handler, $batch_job) = @_;

  my $job_id = sprintf("%d", $batch_job->get_id());
  my $predicate = Google::Ads::AdWords::v201809::Predicate->new({
      field    => "Id",
      operator => "IN",
      values   => [$job_id]});
  my $paging = Google::Ads::AdWords::v201809::Paging->new({
    startIndex    => 0,
    numberResults => 1
  });
  my $selector = Google::Ads::AdWords::v201809::Selector->new({
    fields =>
      ["Id", "Status", "ProgressStats", "ProcessingErrors", "DownloadUrl"],
    predicates => [$predicate],
    paging     => $paging
  });

  # Loop while waiting for the job to complete.
  my $job;
  my $poll_attempts = 0;
  my $end_time      = time + JOB_TIMEOUT_IN_MILLISECONDS;
  do {
    my $batch_job_page = $client->BatchJobService->get({selector => $selector});
    $job = $batch_job_page->get_entries()->[0];

    printf("Job with ID %d has a status of: %s.\n",
      $job->get_id(), $job->get_status());

    my $waittime_in_milliseconds =
      JOB_BASE_WAITTIME_IN_MILLISECONDS * (2**$poll_attempts);
    if (
      (time + $waittime_in_milliseconds) < $end_time
      and ($job->get_status() eq "ACTIVE"
        or $job->get_status() eq "AWAITING_FILE"))
    {
      printf("Sleeping %d milliseconds...\n", $waittime_in_milliseconds);
      sleep($waittime_in_milliseconds / 1000);    # Convert to seconds.
      $poll_attempts++;
    } else {
      # If there isn't enough time to sleep and do another loop, then get out
      # of the loop.
      $end_time = time;
    }
    } while (
    time < $end_time
    and !(
         $job->get_status() eq "DONE"
      or $job->get_status() eq "CANCELED"
    ));

  # If the tiemout was exceeded, then return an error.
  my $error;
  if (!($job->get_status() eq "DONE" or $job->get_status() eq "CANCELED")) {
    $error = Google::Ads::AdWords::Utilities::BatchJobHandlerError->new({
        type        => "UPLOAD",
        description => sprintf(
          "Job with ID %d did not complete" .
            "within a timeout of %d milliseconds.",
          $job->get_id(), JOB_TIMEOUT_IN_MILLISECONDS
        )});
  }

  # Check for processing errors.
  if ($job->get_processingErrors()) {
    $error = Google::Ads::AdWords::Utilities::BatchJobHandlerError->new({
        type => "PROCESSING",
        description =>
          sprintf("Job ID %d had processing errors.", $job->get_id()),
        processing_errors => $job->get_processingErrors()});
  }

  my $download_url = $job->get_downloadUrl()->get_url();
  printf("Batch job with ID %d.\n\tDownload URL: '%s'\n",
    $job->get_id(), $download_url);
  my $download_url_result =
    $batch_job_handler->download_response($download_url);
  if (!$download_url_result) {
    $error               = $download_url_result;
    $download_url_result = undef;
  }
  return {
    error     => $error,
    batch_job => $job,
    result    => $download_url_result
  };
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
add_complete_campaign_using_batch_job($client);

Create a draft and access its campaign

#!/usr/bin/perl -w
#
# 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.
#
# This example illustrates how to create a draft and access its associated
# draft campaign.
#
# See the Campaign Drafts and Experiments guide for more information:
# https://developers.google.com/adwords/api/docs/guides/campaign-drafts-experiments

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::CampaignCriterion;
use Google::Ads::AdWords::v201809::CampaignCriterionOperation;
use Google::Ads::AdWords::v201809::Draft;
use Google::Ads::AdWords::v201809::DraftOperation;
use Google::Ads::AdWords::v201809::Language;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Replace with a valid value from your account.
my $base_campaign_id = "INSERT_BASE_CAMPAIGN_ID_HERE";

# Example main subroutine.
sub add_draft {
  my ($client, $base_campaign_id) = @_;

  my $draft = Google::Ads::AdWords::v201809::Draft->new({
      baseCampaignId => $base_campaign_id,
      draftName      => sprintf("Test Draft #%s", uniqid())});

  # Create operation.
  my $draft_operation = Google::Ads::AdWords::v201809::DraftOperation->new({
      operator => "ADD",
      operand  => $draft
  });

  # Add draft.
  my $result =
    $client->DraftService()->mutate({operations => [$draft_operation]});

  if ($result) {
    $draft = $result->get_value()->[0];
    my $draft_id          = $draft->get_draftId();
    my $draft_campaign_id = $draft->get_draftCampaignId();

    printf(
      "Draft with ID %d and base campaign ID %d" .
        " and draft campaign ID %d created.\n",
      $draft_id, $draft->get_baseCampaignId(),
      $draft_campaign_id
    );

    # Once the draft is created, you can modify the draft campaign as if it
    # were a real campaign. For example, you may add criteria, adjust bids,
    # or even include additional ads. Adding a criterion is shown here.
    my $criterion = Google::Ads::AdWords::v201809::Language->new({
        id => 1003    # Spanish
    });

    my $operation =
      Google::Ads::AdWords::v201809::CampaignCriterionOperation->new({
        operator => "ADD",
        operand  => Google::Ads::AdWords::v201809::CampaignCriterion->new({
            campaignId => $draft_campaign_id,
            criterion  => $criterion
          })});

    $result =
      $client->CampaignCriterionService()->mutate({operations => [$operation]});

    $criterion = $result->get_value()->[0];

    printf("Draft updated to include criteria in campaign %d.\n",
      $criterion->get_campaignId());
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
add_draft($client, $base_campaign_id);

Upload keywords incrementally using BatchJobService

#!/usr/bin/perl -w
#
# 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.
#
# This example illustrates how to perform multiple requests using the
# BatchJobService using incremental uploads.

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::AdGroupCriterion;
use Google::Ads::AdWords::v201809::AdGroupCriterionOperation;
use Google::Ads::AdWords::v201809::BatchJob;
use Google::Ads::AdWords::v201809::BatchJobOperation;
use Google::Ads::AdWords::v201809::BiddableAdGroupCriterion;
use Google::Ads::AdWords::v201809::Keyword;
use Google::Ads::AdWords::Utilities::BatchJobHandler;
use Google::Ads::AdWords::Utilities::BatchJobHandlerError;
use Google::Ads::AdWords::Utilities::BatchJobHandlerStatus;

use Cwd qw(abs_path);

use constant KEYWORD_COUNT => 100;
# Set a timeout to fail if the job does not complete in a specified amount
# of time.
use constant JOB_TIMEOUT_IN_MILLISECONDS => 600000;
# The time to sleep in between polls will start at this time. Then an
# exponential back-off will be instituted.
use constant JOB_BASE_WAITTIME_IN_MILLISECONDS => 30000;

# Replace with valid values of your account.
my $ad_group_id = "INSERT_AD_GROUP_ID_HERE";

# Example main subroutine.
sub add_keywords_using_incremental_batch_job {
  my $client      = shift;
  my $ad_group_id = shift;

  # Create a batch job, which returns an upload URL to upload the batch
  # operations and a job ID to track the job.
  my $operation = Google::Ads::AdWords::v201809::BatchJobOperation->new({
      operator => 'ADD',
      operand  => Google::Ads::AdWords::v201809::BatchJob->new({})});

  my $batch_job_result =
    $client->BatchJobService()->mutate({operations => [$operation]});

  if (!$batch_job_result->get_value()) {
    print "A batch job could not be created. No operations were uploaded.\n";
    return 1;
  }

  my $batch_job = $batch_job_result->get_value()->[0];

  printf("Batch job with ID %d was created.\n\tInitial upload URL: '%s'\n",
    $batch_job->get_id(), $batch_job->get_uploadUrl()->get_url());

  # Get an instance of a utility that helps with uploading batch operations.
  my $batch_job_handler =
    Google::Ads::AdWords::Utilities::BatchJobHandler->new({client => $client});

  #
  # Upload the keywords in three batches to the upload URL.
  # The process that adds the keywords to the ad group will not start
  # executing until the last request has been sent.
  #

  # Upload #1: This is the first upload.
  my @operations = _create_keyword_operations($ad_group_id);

  # Create an initial batch job status.
  # Convert the list of operations into XML.
  # POST the XML to the upload URL.
  my $batch_job_status =
    Google::Ads::AdWords::Utilities::BatchJobHandlerStatus->new({
      total_content_length => 0,
      resumable_upload_uri => $batch_job->get_uploadUrl()->get_url()});
  $batch_job_status =
    $batch_job_handler->upload_incremental_operations(\@operations,
    $batch_job_status);
  if (!_check_status($batch_job_status)) {
    return 1;
  }

  printf("\tResumable upload URL: '%s'\n",
    $batch_job_status->get_resumable_upload_uri());

  # Upload #2: Make sure to pass in the status returned in the last call
  # into 'upload_incremental_operations'.
  @operations = _create_keyword_operations($ad_group_id);

  # Convert the list of operations into XML.
  # POST the XML to the upload URL.
  $batch_job_status =
    $batch_job_handler->upload_incremental_operations(\@operations,
    $batch_job_status);
  if (!_check_status($batch_job_status)) {
    return 1;
  }

  # Upload #3: Make sure to pass in the status returned in the last request
  # and a true value for $is_last_request indicating that the uploads have
  # finished into 'upload_incremental_operations'.
  @operations = _create_keyword_operations($ad_group_id);

  # Convert the list of operations into XML.
  # POST the XML to the upload URL.
  $batch_job_status =
    $batch_job_handler->upload_incremental_operations(\@operations,
    $batch_job_status, 1);
  if (!_check_status($batch_job_status)) {
    return 1;
  }

  # Verify that the operations completed.
  # This will poll until the job has completed or the timeout has expired.
  my $verify_result =
    _verify_operations($client, $batch_job_handler, $batch_job);

  # Display the results.
  my $error             = $verify_result->{error};
  my $job_result        = $verify_result->{batch_job};
  my $operations_result = $verify_result->{result};

  printf("Job with ID %d has completed with a status of '%s'.\n",
    $job_result->get_id(), $job_result->get_status());
  if (defined $error) {
    printf("%s Error. %s\n", $error->get_type(), $error->get_description());
    if ($error->get_type() eq "PROCESSING") {
      printf("%s\n", $error->get_processing_errors());
    }
    if ($error->get_type() eq "HTTP") {
      printf(
        "Type: %s\nCode: %s\nMessage: %s\nTrigger: %s\nField Path: %s\n",
        $error->get_http_type(),             $error->get_http_response_code(),
        $error->get_http_response_message(), $error->get_http_trigger(),
        $error->get_http_field_path());
    }
  }
  if ($operations_result and $operations_result->get_rval()) {
    for my $item (@{$operations_result->get_rval()}) {
      if ($item->get_errorList()) {
        printf("Error on index %d: %s\n",
          $item->get_index(), $item->get_errorList()->get_errors());
      } else {
        # Print the XML output.
        printf("Successful on index %d\n%s\n",
          $item->get_index(),
          $item->get_result()->get_AdGroupCriterion()->get_criterion()
            ->get_text());
      }
    }
  }

  return 1;
}

# Create AdGroupCriterion to add keywords.
sub _create_keyword_operations {
  my ($ad_group_id) = @_;

  my @operations = ();

  for (my $i = 1 ; $i < KEYWORD_COUNT ; $i++) {
    # Create keyword.
    my $keyword =
      Google::Ads::AdWords::v201809::Keyword->new({matchType => "BROAD"});
    # Randomly add invalid characters to keyword.
    if (int(rand(10)) == 0) {
      $keyword->set_text(sprintf("keyword %d!!!", $i));
    } else {
      $keyword->set_text(sprintf("keyword %d", $i));
    }

    # Create biddable ad group criterion.
    my $ad_group_criterion =
      Google::Ads::AdWords::v201809::BiddableAdGroupCriterion->new({
        adGroupId => $ad_group_id,
        criterion => $keyword
      });

    # Create operation.
    my $ad_group_criterion_operation =
      Google::Ads::AdWords::v201809::AdGroupCriterionOperation->new({
        operator => "ADD",
        operand  => $ad_group_criterion
      });

    push(@operations, $ad_group_criterion_operation);
  }
  return @operations;
}

# Check the status of uploading incremental operations. Print details if an
# error is found.
sub _check_status {
  my ($batch_job_status) = @_;
  if (!$batch_job_status) {
    # If not given a status back, then this object is an error.
    my $error = $batch_job_status;
    printf("%s Error. %s\n", $error->get_type(), $error->get_description());
    if ($error->get_type() eq "HTTP") {
      printf(
        "Type: %s\nCode: %s\nMessage: %s\nTrigger: %s\nField Path: %s\n",
        $error->get_http_type(),             $error->get_http_response_code(),
        $error->get_http_response_message(), $error->get_http_trigger(),
        $error->get_http_field_path());
    }
  }
  return $batch_job_status;
}

# Poll for completion of the batch job using an exponential back off
# waiting until the progress of the job is DONE or CANCELED.
# If the job does not finish in the alloted time, attempt to cancel it once.
# This returns a hash of return values:
#   error => undef if no error and BatchJobHandlerError if an error occurred
#   batch_job => the batch job with the id, status, progressStats,
#                processingErrors, and downloadUrl
#   result => BatchJobOpsService::mutateResponse object with contents from the
#             job's download URL
sub _verify_operations {
  my ($client, $batch_job_handler, $batch_job) = @_;

  my $job_id = sprintf("%d", $batch_job->get_id());
  my $predicate = Google::Ads::AdWords::v201809::Predicate->new({
      field    => "Id",
      operator => "IN",
      values   => [$job_id]});
  my $paging = Google::Ads::AdWords::v201809::Paging->new({
      startIndex    => 0,
      numberResults => 1
  });
  my $selector = Google::Ads::AdWords::v201809::Selector->new({
      fields =>
        ["Id", "Status", "ProgressStats", "ProcessingErrors", "DownloadUrl"],
      predicates => [$predicate],
      paging     => $paging
  });

  # Loop while waiting for the job to complete.
  my $job;
  my $poll_attempts        = 0;
  my $end_time             = time + JOB_TIMEOUT_IN_MILLISECONDS;
  my $was_cancel_requested = 0;
  do {
    my $batch_job_page = $client->BatchJobService->get({selector => $selector});
    $job = $batch_job_page->get_entries()->[0];

    printf("Job with ID %d has a status of: %s.\n",
      $job->get_id(), $job->get_status());

    my $waittime_in_milliseconds =
      JOB_BASE_WAITTIME_IN_MILLISECONDS * (2**$poll_attempts);
    if (
      (time + $waittime_in_milliseconds) < $end_time
      and ($job->get_status() eq "ACTIVE"
        or $job->get_status() eq "AWAITING_FILE"
        or $job->get_status() eq "CANCELING")
      )
    {
      printf("Sleeping %d milliseconds...\n", $waittime_in_milliseconds);
      sleep($waittime_in_milliseconds / 1000);    # Convert to seconds.
      $poll_attempts++;
    } else {
      # Optional:
      # If there isn't enough time to sleep and do another loop, then cancel.
      # If a cancel was already unsuccessful, get out of the loop.
      $end_time = time;
      if ( !($job->get_status() eq "DONE" or $job->get_status() eq "CANCELED")
        && !$was_cancel_requested)
      {
        $was_cancel_requested = 1;
        $job->set_status("CANCELING");
        my $operation = Google::Ads::AdWords::v201809::BatchJobOperation->new({
            operator => 'SET',
            operand  => $job
        });
        my $job_result =
          $client->BatchJobService()->mutate({operations => [$operation]});
        if (!$job_result->get_value()) {
          sprint("Unable to cancel job with ID %d.", $job->get_id());
        } else {
          $job = $job_result->get_value()->[0];
          # Reset the timer to give the job time to cancel.
          $poll_attempts = 0;
          $end_time      = time + JOB_TIMEOUT_IN_MILLISECONDS;
          printf("Job with ID %d did not complete within a timeout of %d " .
              "milliseconds. Requested cancellation of batch job.\n",
            $job->get_id(), JOB_TIMEOUT_IN_MILLISECONDS);
        }
      }
    }
    } while (
    time < $end_time
    and !(
         $job->get_status() eq "DONE"
      or $job->get_status() eq "CANCELED"
    ));

  # If the timeout was exceeded, then return an error.
  my $error;
  if (!($job->get_status() eq "DONE" or $job->get_status() eq "CANCELED")) {
    $error = Google::Ads::AdWords::Utilities::BatchJobHandlerError->new({
        type        => "UPLOAD",
        description => sprintf(
          "Job with ID %d did not complete" .
            "within a timeout of %d milliseconds.",
          $job->get_id(), JOB_TIMEOUT_IN_MILLISECONDS
        )});
  }

  # Check for processing errors.
  if ($job->get_processingErrors()) {
    $error = Google::Ads::AdWords::Utilities::BatchJobHandlerError->new({
        type => "PROCESSING",
        description =>
          sprintf("Job ID %d had processing errors.", $job->get_id()),
        processing_errors => $job->get_processingErrors()});
  }

  my $download_url_result;
  if ($job->get_downloadUrl()) {
    my $download_url = $job->get_downloadUrl()->get_url();
    printf("Batch job with ID %d.\n\tDownload URL: '%s'\n",
      $job->get_id(), $download_url);
    $download_url_result = $batch_job_handler->download_response($download_url);
    if (!$download_url_result) {
      $error               = $download_url_result;
      $download_url_result = undef;
    }
  }
  return {
    error     => $error,
    batch_job => $job,
    result    => $download_url_result
  };
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
add_keywords_using_incremental_batch_job($client, $ad_group_id);

Create a trial

#!/usr/bin/perl -w
#
# 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.
#
# This example illustrates how to create a trial and wait for it to complete.
#
# See the Campaign Drafts and Experiments guide for more information:
# https://developers.google.com/adwords/api/docs/guides/campaign-drafts-experiments

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::Predicate;
use Google::Ads::AdWords::v201809::Selector;
use Google::Ads::AdWords::v201809::Trial;
use Google::Ads::AdWords::v201809::TrialOperation;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Replace with valid values of your account.
my $base_campaign_id = "INSERT_BASE_CAMPAIGN_ID_HERE";
my $draft_id         = "INSERT_DRAFT_ID_HERE";

# Set a timeout to fail if the trial is not created in a specified amount
# of time.
use constant JOB_TIMEOUT_IN_MILLISECONDS => 180000;
# The time to sleep in between polls will start at this time. Then an
# exponential back-off will be instituted.
use constant JOB_BASE_WAITTIME_IN_MILLISECONDS => 10000;

# Example main subroutine.
sub add_trial {
  my ($client, $base_campaign_id, $draft_id) = @_;

  my $trial = Google::Ads::AdWords::v201809::Trial->new({
      draftId             => $draft_id,
      baseCampaignId      => $base_campaign_id,
      name                => sprintf("Test Trial #%s", uniqid()),
      trafficSplitPercent => 50,
      trafficSplitType    => "RANDOM_QUERY"
  });

  # Create operation.
  my $trial_operation = Google::Ads::AdWords::v201809::TrialOperation->new({
      operator => "ADD",
      operand  => $trial
  });

  # Add trial.
  my $result =
    $client->TrialService()->mutate({operations => [$trial_operation]});

  if ($result) {
    my $trial_id = $result->get_value()->[0]->get_id()->get_value();

    my $predicate = Google::Ads::AdWords::v201809::Predicate->new({
        field    => "Id",
        operator => "IN",
        values   => [$trial_id]});
    my $paging = Google::Ads::AdWords::v201809::Paging->new({
        startIndex    => 0,
        numberResults => 1
    });
    my $selector = Google::Ads::AdWords::v201809::Selector->new({
        fields => ["Id", "Status", "BaseCampaignId", "TrialCampaignId"],
        predicates => [$predicate],
        paging     => $paging
    });

    # Since creating a trial is asynchronous, we have to poll it to wait for
    # it to finish.
    my $poll_attempts = 0;
    my $is_pending    = 1;
    my $end_time      = time + JOB_TIMEOUT_IN_MILLISECONDS;
    do {
      # Check to see if the trial is still in the process of being created.
      my $result = $client->TrialService()->get({selector => $selector});
      $trial = $result->get_entries()->[0];
      my $waittime_in_milliseconds =
        JOB_BASE_WAITTIME_IN_MILLISECONDS * (2**$poll_attempts);
      if (((time + $waittime_in_milliseconds) < $end_time)
        and $trial->get_status() eq 'CREATING')
      {
        printf("Sleeping %d milliseconds...\n", $waittime_in_milliseconds);
        sleep($waittime_in_milliseconds / 1000);    # Convert to seconds.
        $poll_attempts++;
      }
    } while (time < $end_time
      and $trial->get_status() eq 'CREATING');

    if ($trial->get_status() eq 'ACTIVE') {
      # The trial creation was successful.
      printf("Trial created with ID %d and trial campaign ID %d.\n",
        $trial->get_id(), $trial->get_trialCampaignId());
    } elsif ($trial->get_status() eq 'CREATION_FAILED') {
      # The trial creation failed, and errors can be fetched from the
      # TrialAsyncErrorService.
      my $error_selector = Google::Ads::AdWords::v201809::Selector->new({
          fields     => ["TrialId", "AsyncError"],
          predicates => [
            Google::Ads::AdWords::v201809::Predicate->new({
                field    => "TrialId",
                operator => "IN",
                values   => [$trial_id]})]});

      my $errors =
        $client->TrialAsyncErrorService->get({selector => $error_selector})
        ->get_entries();
      if (!$errors) {
        printf("Could not retrieve errors for trial %d", $trial->get_id());
      } else {
        printf("Could not create trial due to the following errors:");
        my $index = 0;
        for my $error ($errors) {
          printf("Error %d: %s", $index, $error->get_asyncError()
          ->get_errorString());
          $index++;
        }
      }
    } else {
      # Most likely, the trial is still being created. You can continue polling,
      # but we have limited the number of attempts in the example.
      printf("Timed out waiting to create trial from draft %d with base " .
          "campaign %d.\n",
        $draft_id, $base_campaign_id);
    }
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
add_trial($client, $base_campaign_id, $draft_id);

Get all disapproved ads in an ad group

#!/usr/bin/perl -w
#
# 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.
#
# This example gets all disapproved ads in an ad group. To add ads, run
# basic_operations/add_text_ads.pl. To get ad groups, run
# basic_operations/get_ad_groups.pl.

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::OrderBy;
use Google::Ads::AdWords::v201809::Paging;
use Google::Ads::AdWords::v201809::Predicate;
use Google::Ads::AdWords::v201809::Selector;
use Google::Ads::AdWords::Utilities::PageProcessor;

use Cwd qw(abs_path);

use constant PAGE_SIZE => 100;

# Replace with valid values of your account.
my $ad_group_id = "INSERT_AD_GROUP_ID_HERE";

# Example main subroutine.
sub get_all_disapproved_ads {
  my $client      = shift;
  my $ad_group_id = shift;

  # Create predicates (Filters).
  my $ad_group_predicate = Google::Ads::AdWords::v201809::Predicate->new({
      field    => "AdGroupId",
      operator => "IN",
      values   => [$ad_group_id]});

  my $disapproved_predicate = Google::Ads::AdWords::v201809::Predicate->new({
      field    => "CombinedApprovalStatus",
      operator => "EQUALS",
      values   => ["DISAPPROVED"]});

  # Create selector.
  my $paging = Google::Ads::AdWords::v201809::Paging->new({
    startIndex    => 0,
    numberResults => PAGE_SIZE
  });
  my $selector = Google::Ads::AdWords::v201809::Selector->new({
      fields     => ["Id", "PolicySummary"],
      predicates => [$ad_group_predicate, $disapproved_predicate],
      ordering   => [
        Google::Ads::AdWords::v201809::OrderBy->new({
            field     => "Id",
            sortOrder => "ASCENDING"
          })
      ],
      paging => $paging
    });

  # Paginate through results.
  # The contents of the subroutine will be executed for each ad.
  my $disapproved_ad_count = 0;
  Google::Ads::AdWords::Utilities::PageProcessor->new({
      client   => $client,
      service  => $client->AdGroupAdService(),
      selector => $selector
    }
    )->process_entries(
    sub {
      my ($ad_group_ad) = @_;
      $disapproved_ad_count++;
      my $policy_summary = $ad_group_ad->get_policySummary();
      printf "Ad with ID %d and type '%s' was disapproved with the " .
        "following policy topic entries:\n", $ad_group_ad->get_ad()->get_id(),
        $ad_group_ad->get_ad()->get_Ad__Type();
      foreach
        my $policy_topic_entry (@{$policy_summary->get_policyTopicEntries()}) {
        printf "  topic id: %s, topic name: '%s', Help Center URL: '%s'\n",
          $policy_topic_entry->get_policyTopicId(),
          $policy_topic_entry->get_policyTopicName(),
          $policy_topic_entry->get_policyTopicHelpCenterUrl();
        # Display the attributes and values that triggered the policy topic.
        if ($policy_topic_entry->get_policyTopicEvidences()) {
          foreach
            my $evidence (@{$policy_topic_entry->get_policyTopicEvidences()}) {
            printf("    evidence type: '%s'\n",
              $evidence->get_policyTopicEvidenceType());
            if ($evidence->get_evidenceTextList()) {
              for (
                my $i = 0;
                $i < scalar(@{$evidence->get_evidenceTextList()}) ;
                $i++
                )
              {
                printf("      evidence text[%d]: '%s'\n",
                  $i, $evidence->get_evidenceTextList()->[$i]);
              }
            }
          }
        }

      }
    });

  if ($disapproved_ad_count == 0) {
    print("No disapproved ads were found.\n");
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
get_all_disapproved_ads($client, $ad_group_id);

Get all disapproved ads in an ad group using AWQL

#!/usr/bin/perl -w
#
# 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.
#
# This example gets all disapproved ads in an ad group using AWQL.
# To add ads, run basic_operations/add_text_ads.pl. To get ad groups, run
# basic_operations/get_ad_groups.pl.

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::OrderBy;
use Google::Ads::AdWords::v201809::Paging;
use Google::Ads::AdWords::v201809::Predicate;
use Google::Ads::AdWords::v201809::Selector;
use Google::Ads::AdWords::Utilities::PageProcessor;
use Google::Ads::AdWords::Utilities::ServiceQueryBuilder;

use Cwd qw(abs_path);

use constant PAGE_SIZE => 100;

# Replace with valid values of your account.
my $ad_group_id = "INSERT_AD_GROUP_ID_HERE";

# Example main subroutine.
sub get_all_disapproved_ads_with_awql {
  my $client      = shift;
  my $ad_group_id = shift;

  # Get all the disapproved ads for the given ad group.
  my $query = Google::Ads::AdWords::Utilities::ServiceQueryBuilder->new(
      {client => $client})
      ->select(["Id", "PolicySummary"])
      ->where("AdGroupId")->equal_to($ad_group_id)
      ->where("CombinedApprovalStatus")->equal_to("DISAPPROVED")
      ->order_by("Id")
      ->build();

  # Paginate through results.
  # The contents of the subroutine will be executed for each disapproved ad.
  my $disapproved_ad_count = 0;
  Google::Ads::AdWords::Utilities::PageProcessor->new({
      client    => $client,
      service   => $client->AdGroupAdService(),
      query     => $query,
      page_size => PAGE_SIZE
    }
    )->process_entries(
    sub {
      my ($ad_group_ad) = @_;
      $disapproved_ad_count++;
      my $policy_summary = $ad_group_ad->get_policySummary();
      printf "Ad with ID %d and type '%s' was disapproved with the " .
        "following policy topic entries:\n", $ad_group_ad->get_ad()->get_id(),
        $ad_group_ad->get_ad()->get_Ad__Type();
      #  Display the policy topic entries related to the ad disapproval.
      foreach
        my $policy_topic_entry (@{$policy_summary->get_policyTopicEntries()})
      {
        printf "  topic id: %s, topic name: '%s'\n",
          $policy_topic_entry->get_policyTopicId(),
          $policy_topic_entry->get_policyTopicName();
        # Display the attributes and values that triggered the policy topic.
        if ($policy_topic_entry->get_policyTopicEvidences()) {
          foreach
            my $evidence (@{$policy_topic_entry->get_policyTopicEvidences()})
          {
            printf("    evidence type: '%s'\n",
              $evidence->get_policyTopicEvidenceType());
            if ($evidence->get_evidenceTextList()) {
              for (
                my $i = 0 ;
                $i < scalar(@{$evidence->get_evidenceTextList()}) ;
                $i++
                )
              {
                printf("      evidence text[%d]: '%s'\n",
                  $i, $evidence->get_evidenceTextList()->[$i]);
              }
            }
          }
        }
      }
    });

  if ($disapproved_ad_count == 0) {
    print("No disapproved ads were found.\n");
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
get_all_disapproved_ads_with_awql($client, $ad_group_id);

Get all campaigns with a specific label

#!/usr/bin/perl -w
#
# 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.
#
# This example gets all campaigns with a specific label. To add a label to
# campaigns, run add_campaign_labels.pl.

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::OrderBy;
use Google::Ads::AdWords::v201809::Paging;
use Google::Ads::AdWords::v201809::Predicate;
use Google::Ads::AdWords::v201809::Selector;
use Google::Ads::AdWords::Utilities::PageProcessor;

use constant PAGE_SIZE => 500;

use Cwd qw(abs_path);

# Replace with valid values of your account.
my $label_id = "INSERT_LABEL_ID_HERE";

# Example main subroutine.
sub get_campaigns_by_label {
  my $client   = shift;
  my $label_id = shift;

  # Create predicate.
  # Labels filtering is performed by ID. You can use CONTAINS_ANY to select
  # campaigns with any of the label IDs, CONTAINS_ALL to select campaigns with
  # all of the label IDs, or CONTAINS_NONE to select campaigns with none of the
  # label IDs.
  my $labels_predicate = Google::Ads::AdWords::v201809::Predicate->new({
      field    => "Labels",
      operator => "CONTAINS_ANY",
      values   => [$label_id]});
  # Create selector.
  my $paging = Google::Ads::AdWords::v201809::Paging->new({
      startIndex    => 0,
      numberResults => PAGE_SIZE
  });
  my $selector = Google::Ads::AdWords::v201809::Selector->new({
      fields     => ["Id", "Name", "Labels"],
      predicates => [$labels_predicate],
      ordering   => [
        Google::Ads::AdWords::v201809::OrderBy->new({
            field     => "Name",
            sortOrder => "ASCENDING"
          })
      ],
      paging => $paging
    });

  # Paginate through results.
  # The contents of the subroutine will be executed for each campaign.
  Google::Ads::AdWords::Utilities::PageProcessor->new({
      client   => $client,
      service  => $client->CampaignService(),
      selector => $selector
    }
    )->process_entries(
    sub {
      my ($campaign) = @_;
      my @label_strings =
        map { $_->get_id() . '/' . $_->get_name() } @{$campaign->get_labels()};
      printf "Campaign found with name \"%s\" and ID %d and labels: %s.\n",
        $campaign->get_name(), $campaign->get_id(),
        join(', ', @label_strings);
    });

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
get_campaigns_by_label($client, $label_id);

Graduate a trial

#!/usr/bin/perl -w
#
# 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.
#
# This example illustrates how to graduate a trial.
#
# See the Campaign Drafts and Experiments guide for more information:
# https://developers.google.com/adwords/api/docs/guides/campaign-drafts-experiments

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::Budget;
use Google::Ads::AdWords::v201809::BudgetOperation;
use Google::Ads::AdWords::v201809::Trial;
use Google::Ads::AdWords::v201809::TrialOperation;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Replace with a valid value from your account.
my $trial_id = "INSERT_TRIAL_ID_HERE";

# Example main subroutine.
sub graduate_trial {
  my ($client, $trial_id) = @_;

  # To graduate a trial, you must specify a different budget from the base
  # campaign. The base campaign (in order to have had a trial based on it)
  # must have a non-shared budget, so it cannot be shared with the new
  # independent campaign created by graduation.
  my $budget = Google::Ads::AdWords::v201809::Budget->new({
      name => sprintf("Budget #%s", uniqid()),
      amount =>
        Google::Ads::AdWords::v201809::Money->new({microAmount => 5000000}),
      deliveryMethod => "STANDARD"
  });

  my $budget_operation = Google::Ads::AdWords::v201809::BudgetOperation->new({
      operator => "ADD",
      operand  => $budget
  });

  # Add budget.
  my $budget_id =
    $client->BudgetService()->mutate({operations => ($budget_operation)})
    ->get_value()->get_budgetId()->get_value();

  my $trial = Google::Ads::AdWords::v201809::Trial->new({
      id       => $trial_id,
      budgetId => $budget_id,
      status   => "GRADUATED"
  });

  # Create operation.
  my $trial_operation = Google::Ads::AdWords::v201809::TrialOperation->new({
      operator => "SET",
      operand  => $trial
  });

  # Graduate trial.
  my $result =
    $client->TrialService()->mutate({operations => [$trial_operation]});

  # Update the trial.
  $trial = $result->get_value()->[0];

  # Graduation is a synchronous operation, so the campaign is already ready.
  # If you promote instead, make sure to see the polling scheme demonstrated
  # in add_trial.pl to wait for the asynchronous operation to finish.
  printf("Trial ID %d graduated. Campaign %d was given a new budget ID %d " .
      "and is no longer dependent on this trial.\n",
    $trial->get_id(), $trial->get_trialCampaignId(), $budget_id);

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
graduate_trial($client, $trial_id);

Set ad parameters for a keyword ad group criterion

#!/usr/bin/perl -w
#
# 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.
#
# This example sets ad parameters for a keyword in an ad group. To get keywords,
# run basic_operations/get_keywords.pl.

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::AdParam;
use Google::Ads::AdWords::v201809::AdParamOperation;

use Cwd qw(abs_path);

# Replace with valid values of your account.
my $ad_group_id = "INSERT_AD_GROUP_ID_HERE";
my $keyword_id  = "INSERT_KEYWORD_ID_HERE";

# Example main subroutine.
sub set_ad_parameters {
  my $client      = shift;
  my $ad_group_id = shift;
  my $keyword_id  = shift;

  # Create ad parameters.
  my $ad_param1 = Google::Ads::AdWords::v201809::AdParam->new({
      adGroupId     => $ad_group_id,
      criterionId   => $keyword_id,
      insertionText => "100",
      paramIndex    => "1",
  });

  my $ad_param2 = Google::Ads::AdWords::v201809::AdParam->new({
      adGroupId     => $ad_group_id,
      criterionId   => $keyword_id,
      insertionText => "\$40",
      paramIndex    => "2",
  });

  # Create operations.
  my $ad_param_operation1 =
    Google::Ads::AdWords::v201809::AdParamOperation->new({
      operator => "SET",
      operand  => $ad_param1
    });

  my $ad_param_operation2 =
    Google::Ads::AdWords::v201809::AdParamOperation->new({
      operator => "SET",
      operand  => $ad_param2
    });

  # Set ad parameters.
  my $ad_params =
    $client->AdParamService()
    ->mutate({operations => [$ad_param_operation1, $ad_param_operation2]});

  # Display ad parameters.
  if ($ad_params) {
    foreach my $ad_param (@{$ad_params}) {
      printf "Ad parameter with ad group id \"%d\", criterion id \"%d\", " .
        "insertion text \"%s\", and parameter index \"%d\" was set.\n",
        $ad_param->get_adGroupId(),     $ad_param->get_criterionId(),
        $ad_param->get_insertionText(), $ad_param->get_paramIndex();
    }
  } else {
    print "No ad parameters were set.\n";
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
set_ad_parameters($client, $ad_group_id, $keyword_id);

Set a bid modifier on a campaign

#!/usr/bin/perl -w
#
# 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.
#
# This example sets a bid modifier for the mobile platform on given campaign.
# To get campaigns, run basic_operations/get_campaigns.pl.

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::CampaignCriterionOperation;
use Google::Ads::AdWords::v201809::Platform;

use Cwd qw(abs_path);

use constant BID_MODIFIER => 1.5;

# Replace with valid values of your account.
my $campaign_id = "INSERT_CAMPAIGN_ID_HERE";

# Example main subroutine.
sub set_bid_modifier {
  my $client      = shift;
  my $campaign_id = shift;

  # Create mobile platform. The ID can be found in the documentation.
  # https://developers.google.com/adwords/api/docs/appendix/platforms
  my $mobile = Google::Ads::AdWords::v201809::Platform->new({id => 30001});

  # Create criterion with modified bid.
  my $criterion = Google::Ads::AdWords::v201809::CampaignCriterion->new({
      campaignId  => $campaign_id,
      criterion   => $mobile,
      bidModifier => BID_MODIFIER
  });

  # Create SET operation.
  my $operation =
    Google::Ads::AdWords::v201809::CampaignCriterionOperation->new({
      operator => "SET",
      operand  => $criterion
    });

  # Update campaign criteria.
  my $result =
    $client->CampaignCriterionService()->mutate({operations => [$operation]});

  # Display campaign criteria.
  if ($result->get_value()) {
    foreach my $campaign_criterion (@{$result->get_value()}) {
      printf "Campaign criterion with campaign id '%s', criterion id '%s', " .
        "and type '%s' was modified with bid %.2f.\n",
        $campaign_criterion->get_campaignId(),
        $campaign_criterion->get_criterion()->get_id(),
        $campaign_criterion->get_criterion()->get_type(),
        $campaign_criterion->get_bidModifier();
    }
  } else {
    print "No campaign criteria were modified.\n";
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
set_bid_modifier($client, $campaign_id);

Validate text ad through setValidateOnly header

#!/usr/bin/perl -w
#
# 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.
#
# This example shows how to use the validate only header to check for errors.
# No objects will be created, but exceptions will still be returned.

use strict;
use lib "../../../lib";

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::AdGroupAd;
use Google::Ads::AdWords::v201809::AdGroupAdOperation;
use Google::Ads::AdWords::v201809::ExpandedTextAd;

use Cwd qw(abs_path);

# Replace with valid values of your account.
my $ad_group_id = "INSERT_AD_GROUP_ID_HERE";

# Example main subroutine.
sub validate_text_ad {
  my $client      = shift;
  my $ad_group_id = shift;

  # Don't die on fault it will be handled in code.
  $client->set_die_on_faults(0);

  # Set validate only.
  $client->set_validate_only(1);

  # Create invalid expanded text ad
  my $text_ad = Google::Ads::AdWords::v201809::ExpandedTextAd->new({
      headlinePart1 => "Luxury Cruise to Mars",
      headlinePart2 => "Visit the Red Planet in style.",
      description   => "Low-gravity fun for all astronauts in orbit.",
      finalUrls     => ["http://www.example.com"]});
  my $text_ad_group_ad = Google::Ads::AdWords::v201809::AdGroupAd->new({
    adGroupId => $ad_group_id,
    ad        => $text_ad
  });

  # Create operations.
  my $operation = Google::Ads::AdWords::v201809::AdGroupAdOperation->new({
    operand  => $text_ad_group_ad,
    operator => "ADD"
  });

  # Validate text ad operation.
  my $result =
    $client->AdGroupAdService()->mutate({operations => [$operation]});
  if (defined($result) and $result->isa("SOAP::WSDL::SOAP::Typelib::Fault11")) {
    printf "Validation failed for reason: %s\n", $result->get_faultstring();
  } else {
    print "The ad is valid!\n";
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
validate_text_ad($client, $ad_group_id);