Policy Exemption Requests

Exemption requests allow you to automatically submit review requests for keywords and ads that trigger policy violations.

This feature could be used, for example, when:

  • Your ad contains some punctuation that's generally considered unconventional, but that follows standards within your industry.
  • A keyword in your ad group contains medical terms, but you (or your users) believe your usage of the term adheres to AdWords policies and warrants further review.

In such situations, your initial attempt at creating the ad or keyword will fail with a PolicyViolationError.

If you've populated your ads' and keywords' exemptionRequests collection, they'll be automatically submitted for review. Depending on the outcome of the review, you may be able to successfully resubmit your ads or keywords using the ADD operation.

Important: Do not simply resubmit every ad or keyword that generates a PolicyViolationError after adding exemption requests. Only resubmit those ads or keywords you believe comply with our advertising policies and warrant further review. If your ads or keywords are repeatedly disapproved for violating our advertising policies, your AdWords account could be suspended.

In the event that an ad is submitted successfully without a PolicyViolationError but is later disapproved due to an ads policy violation, further details are provided in the AdGroupAdPolicySummary.

This sections below describe the steps required to submit exemption requests.

Check each failed operation for policy violation errors

Let's say you're creating three ads in a single call to AdGroupAdService.mutate():

  • Ads A and B contain the same medical term in their headlines
  • Ad B also contains some non-standard punctuation in its headline
  • Ad C contains text that isn't in standard sentence form or doesn't reflect the target site's content

When you submit the service call containing three AdGroupAdOperations, the request fails with the following errors:

Error PolicyViolationError.fieldPath PolicyViolationError.isExemptable PolicyViolationKey.policyName
PolicyViolationError 0 (Ad A) true pharma
PolicyViolationError 1 (Ad B) true pharma
PolicyViolationError 1 (Ad B) true nonstandard_punctuation
PolicyViolationError 2 (Ad C) false unclear_or_inaccurate_ad_text

Keep the operations with exemptible policy violations

Since the errors for operations 0 and 1 are exemptible, you can resubmit them with exemption requests. The error for operation 2, however, is not exemptible so there's no point in resubmitting that operation.

The following code snippet iterates over the errors in the response and retains the operation index of each operation that failed with an exemptible policy violation error.

Java

for (ApiError error : e.getErrors()) {
  // Get the index of the failed operation from the error's field path elements.
  FieldPathElement[] fieldPathElements = error.getFieldPathElements();
  FieldPathElement firstFieldPathElement = null;
  if (fieldPathElements != null && fieldPathElements.length > 0) {
    firstFieldPathElement = fieldPathElements[0];
  }
  if (firstFieldPathElement == null
      || !"operations".equals(firstFieldPathElement.getField())
      || firstFieldPathElement.getIndex() == null) {
    // If the operation index is not present on the first error field path element, then
    // there's no way to determine which operation to remove, so simply throw the exception.
    throw e;
  }
  int operationIndex = firstFieldPathElement.getIndex();
  AdGroupAdOperation operation = operations[operationIndex];
  if (handleApiError(error, operationIndex, operation)) {
    operationIndicesToRetry.add(operationIndex);
  } else {
    System.out.printf(
        "Removing operation with non-exemptable error at index %d.%n", operationIndex);
  }
}

C#

ApiException innerException = e.ApiException as ApiException;
if (innerException == null) {
  throw new Exception("Failed to retrieve ApiError. See inner exception for more " +
      "details.", e);
}

// Examine each ApiError received from the server.
foreach (ApiError apiError in innerException.errors) {
  int index = apiError.GetOperationIndex();
  if (index == -1) {
    // This API error is not associated with an operand, so we cannot
    // recover from this error by removing one or more operations.
    // Rethrow the exception for manual inspection.
    throw;
  }

  // Handle policy violation errors.
  if (apiError is PolicyViolationError) {
    PolicyViolationError policyError = (PolicyViolationError) apiError;

    if (policyError.isExemptable) {
      // If the policy violation error is exemptable, add an exemption
      // request.
      List<ExemptionRequest> exemptionRequests = new List<ExemptionRequest>();
      if (allOperations[index].exemptionRequests != null) {
        exemptionRequests.AddRange(allOperations[index].exemptionRequests);
      }

      ExemptionRequest exemptionRequest = new ExemptionRequest();
      exemptionRequest.key = policyError.key;
      exemptionRequests.Add(exemptionRequest);
      allOperations[index].exemptionRequests = exemptionRequests.ToArray();
    } else {
      // Policy violation error is not exemptable, remove this
      // operation from the list of operations.
      operationsToBeRemoved.Add(allOperations[index]);
    }
  } else {
    // This is not a policy violation error, remove this operation
    // from the list of operations.
    operationsToBeRemoved.Add(allOperations[index]);
  }
}

Python

for error in e.errors:
  # Get the index of the failed operation from the error's field path
  # elements.
  field_path_elements = error['fieldPathElements']
  first_field_path_element = None

  if field_path_elements:
    first_field_path_element = field_path_elements[0]

  # If the operation index is not present on the first error field path
  # element, then there's no way to determine which operation to remove,
  # so simply throw the exception.
  if (not (first_field_path_element
           and first_field_path_element['field'] == 'operations'
           and 'index' in first_field_path_element)):
    raise e

  index = long(first_field_path_element['index'])
  operation = operations[index]
  if not HandleAPIError(error, operation):
    # Set non-exemptable operation to None to mark for deletion.
    print ('Removing operation with non-exemptable error at index %s.'
           % index)
    operations[index] = None

PHP

foreach ($apiException->getErrors() as $error) {
    // Get the index of the failed operation from the error's field path
    // elements.
    $fieldPathElements = $error->getFieldPathElements();
    $firstFieldPathElement = null;
    if ($fieldPathElements !== null && count($fieldPathElements) > 0) {
        $firstFieldPathElement = $fieldPathElements[0];
    }
    if ($firstFieldPathElement === null
        || $firstFieldPathElement->getField() !== 'operations'
        || $firstFieldPathElement->getIndex() === null) {
        // If the operation index is not present on the first error field
        // path element, then there's no way to determine which operation to
        // remove, so simply throw the exception.
        throw $apiException;
    }
    $operationIndex = $firstFieldPathElement->getIndex();
    $operation = $operations[$operationIndex];
    if (self::handleApiError($error, $operationIndex, $operation)) {
        // Store the index of operations we want to retry as indices of the
        // bucket.
        $operationIndicesToRetryBucket[$operationIndex] = 1;
    } else {
        printf(
            "Removing operation with non-exemptable error at index %d.\n",
            $operationIndex
        );
    }
}

Perl

foreach
  my $error (@{$result->get_detail()->get_ApiExceptionFault()->get_errors()})
{
  # Get the index of the failed operation from the error's field path
  # elements.
  my $field_path_elements = $error->get_fieldPathElements();
  my $first_field_path_element =
    ($field_path_elements && (scalar $field_path_elements > 0))
    ? $field_path_elements->[0]
    : undef;
  if ( $first_field_path_element
    && $first_field_path_element->get_field() eq "operations"
    && defined $first_field_path_element->get_index())
  {
    my $operation_index = $first_field_path_element->get_index();
    my $operation       = $operations[$operation_index];
    if ($error->get_ApiError__Type() =~ "PolicyViolationError") {
      printf "Ad with headline '%s' violated '%s' policy '%s'.\n",
        $operation->get_operand()->get_ad()->get_headlinePart1(),
        $error->get_isExemptable ? 'exemptable' : 'non-exemptable',
        $error->get_externalPolicyName();

      if ($error->get_isExemptable()) {
        # Add exemption request to the operation.
        printf(
          "Adding exemption request for policy name '%s' on text " .
            "'%s'.\n",
          $error->get_key()->get_policyName(),
          $error->get_key()->get_violatingText());
        $operation->set_exemptionRequests([
            new Google::Ads::AdWords::v201802::ExemptionRequest(
              {key => $error->get_key()})]);
      } else {
        # Remove non-exemptable operation.
        print "Removing from the request.\n";
        push @operation_indicies_to_remove, $operation_index;
      }
    } else {
      # Non-policy error returned, remove ad.
      print "Removing from the request.\n";
      push @operation_indicies_to_remove, $operation_index;
    }
  }
}

Ruby

e.errors.each do |error|
  if error[:xsi_type] == 'PolicyViolationError'
    field_path_elements = error[:field_path_elements]
    first_field_path_element = nil
    unless field_path_elements.nil? || field_path_elements.length <= 0
      first_field_path_element = field_path_elements.first
    end
    if first_field_path_element.nil? ||
        'operations' != first_field_path_element[:field] ||
        first_field_path_element[:index].nil?
      # If the operation index is not present on the first error field path
      # element, then there's no way to determine which operation to
      # remove, so simply throw the exception.
      raise e
    end

    operation_index = first_field_path_element[:index]
    operation = operations[operation_index]
    process_api_error(error, operation)
    unless error[:is_exemptable]
      # Remove non-exemptable operation
      puts "Removing the operation from the request."
      operations.delete(operation)
    end
  else
    # Non-policy error returned, re-throw exception.
    raise e
  end
end

VB.NET

Dim innerException As ApiException = TryCast(e.ApiException, ApiException)
If (innerException Is Nothing) Then
  Throw New Exception("Failed to retrieve ApiError. See inner exception for more " &
      "details.", e)
End If

' Examine each ApiError received from the server.
For Each apiError As ApiError In innerException.errors
  Dim index As Integer = apiError.GetOperationIndex()
  If (index = -1) Then
    ' This API error is not associated with an operand, so we cannot
    ' recover from this error by removing one or more operations.
    ' Rethrow the exception for manual inspection.
    Throw
  End If

  ' Handle policy violation errors.
  If TypeOf apiError Is PolicyViolationError Then
    Dim policyError As PolicyViolationError = CType(apiError, PolicyViolationError)

    If policyError.isExemptable Then
      ' If the policy violation error is exemptable, add an exemption
      ' request.
      Dim exemptionRequests As New List(Of ExemptionRequest)
      If (Not allOperations.Item(index).exemptionRequests Is Nothing) Then
        exemptionRequests.AddRange(allOperations.Item(index).exemptionRequests)
      End If

      Dim exemptionRequest As New ExemptionRequest
      exemptionRequest.key = policyError.key
      exemptionRequests.Add(exemptionRequest)
      allOperations.Item(index).exemptionRequests = exemptionRequests.ToArray
    Else
      ' Policy violation error is not exemptable, remove this
      ' operation from the list of operations.
      operationsToBeRemoved.Add(allOperations.Item(index))
    End If
  Else
    ' This is not a policy violation error, remove this operation
    ' from the list of operations.
    operationsToBeRemoved.Add(allOperations.Item(index))
  End If
Next

Update the operations with exemption requests

In order to resubmit the operations that failed due to exemptible policy violation errors, you need to add one or more ExemptionRequests to the operation's exemptionRequests collection.

An ExemptionRequest has just one attribute, a PolicyViolationKey, that consists of:

  • a string identifying the policy name
  • a string identifying the violating text

The good news is that each PolicyViolationError you encounter provides this information. As shown in the following code snippet, you can use the key attribute of each PolicyViolationError for the key attribute of the corresponding ExemptionRequest.

Java

private static boolean handleApiError(
    ApiError error, int operationIndex, AdGroupAdOperation operation) {
  // Determine if the operation can be resubmitted with an exemption request.
  boolean isExemptableError = false;
  PolicyViolationError policyViolationError = null;
  if (error instanceof PolicyViolationError) {
    policyViolationError = (PolicyViolationError) error;
    ExpandedTextAd expandedTextAd = (ExpandedTextAd) operation.getOperand().getAd();
    System.out.printf(
        "Ad with headline '%s - %s' violated %s policy '%s'.%n",
        expandedTextAd.getHeadlinePart1(),
        expandedTextAd.getHeadlinePart2(),
        policyViolationError.getIsExemptable() ? "exemptable" : "non-exemptable",
        policyViolationError.getExternalPolicyName());
    isExemptableError = policyViolationError.getIsExemptable();
  }

  if (isExemptableError) {
    // Add exemption request to the operation.
    System.out.printf(
        "Adding exemption request for policy name '%s' on text '%s' to operation at index %d.%n",
        policyViolationError.getKey().getPolicyName(),
        policyViolationError.getKey().getViolatingText(),
        operationIndex);
    ExemptionRequest exemptionRequest = new ExemptionRequest();
    exemptionRequest.setKey(policyViolationError.getKey());

    List<ExemptionRequest> exemptionRequests =
        (operation.getExemptionRequests() == null)
            ? new ArrayList<ExemptionRequest>()
            : new ArrayList<>(Arrays.asList(operation.getExemptionRequests()));
    exemptionRequests.add(exemptionRequest);
    operation.setExemptionRequests(
        exemptionRequests.toArray(new ExemptionRequest[exemptionRequests.size()]));
  }
  return isExemptableError;
}

C#

// Handle policy violation errors.
if (apiError is PolicyViolationError) {
  PolicyViolationError policyError = (PolicyViolationError) apiError;

  if (policyError.isExemptable) {
    // If the policy violation error is exemptable, add an exemption
    // request.
    List<ExemptionRequest> exemptionRequests = new List<ExemptionRequest>();
    if (allOperations[index].exemptionRequests != null) {
      exemptionRequests.AddRange(allOperations[index].exemptionRequests);
    }

    ExemptionRequest exemptionRequest = new ExemptionRequest();
    exemptionRequest.key = policyError.key;
    exemptionRequests.Add(exemptionRequest);
    allOperations[index].exemptionRequests = exemptionRequests.ToArray();
  } else {
    // Policy violation error is not exemptable, remove this
    // operation from the list of operations.
    operationsToBeRemoved.Add(allOperations[index]);
  }
} else {
  // This is not a policy violation error, remove this operation
  // from the list of operations.
  operationsToBeRemoved.Add(allOperations[index]);
}

Python

def HandleAPIError(error, operation):
  """Makes an exemption for exemptable PolicyViolationErrors.

  Args:
    error: the error associated with the given operation.
    operation: the operation associated with the given error.

  Returns:
    A boolean that is True if the given error was an exemptable
    PolicyViolationError; otherwise, returns False.
  """
  is_exemptable = False

  # Determine if the operation can be resubmitted with an exemption request.
  if error['ApiError.Type'] == 'PolicyViolationError':
    expanded_text_ad = operation['operand']['ad']
    is_exemptable = (error['isExemptable'] if 'isExemptable' in error else
                     False)
    print ('Ad with headline "%s - %s" violated %s policy "%s".' %
           (expanded_text_ad['headlinePart1'],
            expanded_text_ad['headlinePart2'],
            'exemptable' if is_exemptable else 'non-exemptable',
            error['externalPolicyName']))

  if is_exemptable:
    # Add exemption request to the operation.
    print ('Adding exemption request for policy name "%s" on text "%s".'
           % (error['key']['policyName'], error['key']['violatingText']))
    if 'exemptionRequests' not in operation:
      operation['exemptionRequests'] = []
    operation['exemptionRequests'].append({'key': error['key']})

  return is_exemptable

PHP

private static function handleApiError(
    ApiError $apiError,
    $operationIndex,
    AdGroupAdOperation $operation
) {
    $isExemptableError = false;
    $policyViolationError = null;

    $expandedTextAd = $operation->getOperand()->getAd();
    if ($apiError instanceof PolicyViolationError) {
        printf(
            "Ad with headline '%s - %s' violated %s policy '%s'.\n",
            $expandedTextAd->getHeadlinePart1(),
            $expandedTextAd->getHeadlinePart2(),
            $apiError->getIsExemptable() ? 'exemptable' : 'non-exemptable',
            $apiError->getExternalPolicyName()
        );
        $isExemptableError = $apiError->getIsExemptable();
    }

    if ($isExemptableError) {
        // Add exemption request to the operation.
        printf(
            "Adding exemption request for policy name '%s' on text '%s' to operation at index %d.\n",
            $apiError->getKey()->getPolicyName(),
            $apiError->getKey()->getViolatingText(),
            $operationIndex
        );
        if ($operation->getExemptionRequests() === null) {
            $exemptionRequests = [];
        } else {
            $exemptionRequests = $operation->getExemptionRequests();
        }
        $exemptionRequests[] = new ExemptionRequest($apiError->getKey());
        $operation->setExemptionRequests($exemptionRequests);
    }

    return $isExemptableError;
}

Perl

if ($error->get_ApiError__Type() =~ "PolicyViolationError") {
  printf "Ad with headline '%s' violated '%s' policy '%s'.\n",
    $operation->get_operand()->get_ad()->get_headlinePart1(),
    $error->get_isExemptable ? 'exemptable' : 'non-exemptable',
    $error->get_externalPolicyName();

  if ($error->get_isExemptable()) {
    # Add exemption request to the operation.
    printf(
      "Adding exemption request for policy name '%s' on text " .
        "'%s'.\n",
      $error->get_key()->get_policyName(),
      $error->get_key()->get_violatingText());
    $operation->set_exemptionRequests([
        new Google::Ads::AdWords::v201802::ExemptionRequest(
          {key => $error->get_key()})]);
  } else {
    # Remove non-exemptable operation.
    print "Removing from the request.\n";
    push @operation_indicies_to_remove, $operation_index;
  }
} else {
  # Non-policy error returned, remove ad.
  print "Removing from the request.\n";
  push @operation_indicies_to_remove, $operation_index;
}

Ruby

def process_api_error(error, operation)
  is_exemptable = error[:is_exemptable]

  puts "Ad with headline '%s - %s' violated %s policy '%s'." %
      [operation[:operand][:ad][:headline_part1],
      operation[:operand][:ad][:headline_part2],
      is_exemptable ? 'exemptable' : 'non-exemptable',
      error[:external_policy_name]]

  if is_exemptable
    # Add exemption request to the operation.
    puts "Adding exemption request for policy name '%s' on text '%s'." %
        [error[:key][:policy_name], error[:key][:violating_text]]
    unless operation[:exemption_requests]
      operation[:exemption_requests] = []
    end
    operation[:exemption_requests] << {
      :key => error[:key]
    }
  end
end

VB.NET

' Handle policy violation errors.
If TypeOf apiError Is PolicyViolationError Then
  Dim policyError As PolicyViolationError = CType(apiError, PolicyViolationError)

  If policyError.isExemptable Then
    ' If the policy violation error is exemptable, add an exemption
    ' request.
    Dim exemptionRequests As New List(Of ExemptionRequest)
    If (Not allOperations.Item(index).exemptionRequests Is Nothing) Then
      exemptionRequests.AddRange(allOperations.Item(index).exemptionRequests)
    End If

    Dim exemptionRequest As New ExemptionRequest
    exemptionRequest.key = policyError.key
    exemptionRequests.Add(exemptionRequest)
    allOperations.Item(index).exemptionRequests = exemptionRequests.ToArray
  Else
    ' Policy violation error is not exemptable, remove this
    ' operation from the list of operations.
    operationsToBeRemoved.Add(allOperations.Item(index))
  End If
Else
  ' This is not a policy violation error, remove this operation
  ' from the list of operations.
  operationsToBeRemoved.Add(allOperations.Item(index))
End If

Resubmit the modified operations

Using the example, at this point you'll have the following AdGroupAdOperations:

  • An AdGroupAdOperation for ad A with a single exemption request with policy name "pharma" and violating text taken from the first PolicyViolationError.
  • An AdGroupAdOperation for ad B with two exemption requests: one with policy name "pharma" with violating text from the second PolicyViolationError, and one with policy name "nonstandard_punctuation" with violating text from the third PolicyViolationError.

The AdGroupAdOperation for ad C has been discarded since its PolicyViolationError was not exemptible.

With all of this in place, you can submit the modified AdGroupAdOperations for ads A and B. These operations will succeed and result in two new ads that are pending review.

Monitor the approval status of each ad or keyword

As with any other new ads, you can periodically submit AdGroupAdService.get() or AdGroupAdService.query() requests that include the AdGroupCreativeApprovalStatus field to monitor the ad and determine if it's ultimately approved or rejected.

Code examples

Each client library contains two code examples related to policy violations:

  • Handle Policy Violation Errors - This example illustrates how to identify and resubmit AdGroupAdOperations that failed due to exemptible policy violations.
  • Validate Text Ad - This example illustrates how to submit ads for validation using the validateOnly SOAP header, described in the API Call Structure guide.

Send feedback about...

AdWords API
AdWords API
Need help? Visit our support page.