Upload Conversion Adjustments

Once a conversion has already been reported to Google Ads, you can adjust the conversion at a later point in time in the Google Ads API.

In order to adjust a conversion, you must first have a conversion action set up, and you must also have reported on the conversions that you are about to adjust.

Code example

There are two types of adjustments that can be made:

  • When you have a Google click ID (GCLID) and conversion date time available, you can adjust a conversion by uploading a ConversionAdjustment.

  • You can also adjust a conversion when you have an order ID available instead of a Google click ID and conversion date time.

Both these adjustment types would be uploaded through the ConversionAdjustmentUploadService.

Java

private void runExample(
    GoogleAdsClient googleAdsClient,
    long customerId,
    long conversionActionId,
    String gclid,
    String adjustmentType,
    String conversionDateTime,
    String adjustmentDateTime,
    @Nullable Float restatementValue) {
  // Gets the conversion adjustment enum value from the adjustmentType String.
  ConversionAdjustmentType conversionAdjustmentType =
      ConversionAdjustmentType.valueOf(adjustmentType);

  // Associates conversion adjustments with the existing conversion action.
  // The GCLID should have been uploaded before with a conversion.
  ConversionAdjustment conversionAdjustment =
      ConversionAdjustment.newBuilder()
          .setConversionAction(
              StringValue.of(ResourceNames.conversionAction(customerId, conversionActionId)))
          .setAdjustmentType(conversionAdjustmentType)
          .setGclidDateTimePair(
              GclidDateTimePair.newBuilder()
                  .setGclid(StringValue.of(gclid))
                  .setConversionDateTime(StringValue.of(conversionDateTime))
                  .build())
          .setAdjustmentDateTime(StringValue.of(adjustmentDateTime))
          .build();

  // Sets adjusted value for adjustment type RESTATEMENT.
  if (restatementValue != null
      && conversionAdjustmentType == ConversionAdjustmentType.RESTATEMENT) {
    conversionAdjustment =
        conversionAdjustment.toBuilder()
            .setRestatementValue(
                RestatementValue.newBuilder()
                    .setAdjustedValue(DoubleValue.of(restatementValue))
                    .build())
            .build();
  }

  // Creates the conversion upload service client.
  try (ConversionAdjustmentUploadServiceClient conversionUploadServiceClient =
      googleAdsClient.getLatestVersion().createConversionAdjustmentUploadServiceClient()) {
    // Uploads the click conversion. Partial failure should always be set to true.
    UploadConversionAdjustmentsResponse response =
        conversionUploadServiceClient.uploadConversionAdjustments(
            Long.toString(customerId),
            ImmutableList.of(conversionAdjustment),
            // Enables partial failure (must be true).
            true,
            // Disables validate only.
            false);

    // Prints any partial errors returned.
    if (response.hasPartialFailureError()) {
      System.out.printf(
          "Partial error encountered: '%s'.%n", response.getPartialFailureError().getMessage());
    } else {
      // Prints the result.
      ConversionAdjustmentResult result = response.getResults(0);
      System.out.printf(
          "Uploaded conversion adjustment of '%s' for Google Click ID '%s'.%n",
          result.getConversionAction().getValue(),
          result.getGclidDateTimePair().getGclid().getValue());
    }
  }
}

C#

public void Run(GoogleAdsClient client, long customerId, long conversionActionId,
    string gclid, string conversionDateTime, string adjustmentDateTime,
    ConversionAdjustmentType adjustmentType,
    double? restatementValue)
{
    // Get the ConversionAdjustmentUploadService.
    ConversionAdjustmentUploadServiceClient conversionAdjustmentUploadService =
        client.GetService(Services.V3.ConversionAdjustmentUploadService);

    // Associate conversion adjustments with the existing conversion action.
    // The GCLID should have been uploaded before with a conversion.
    ConversionAdjustment conversionAdjustment = new ConversionAdjustment()
    {
        ConversionAction = ResourceNames.ConversionAction(customerId, conversionActionId),
        AdjustmentType = adjustmentType,
        GclidDateTimePair = new GclidDateTimePair()
        {
            Gclid = gclid,
            ConversionDateTime = conversionDateTime,
        },
        AdjustmentDateTime = adjustmentDateTime,
    };

    // Set adjusted value for adjustment type RESTATEMENT.
    if (adjustmentType == ConversionAdjustmentType.Restatement)
    {
        conversionAdjustment.RestatementValue = new RestatementValue()
        {
            AdjustedValue = restatementValue
        };
    }

    try
    {
        // Issue a request to upload the conversion adjustment.
        UploadConversionAdjustmentsResponse response =
            conversionAdjustmentUploadService.UploadConversionAdjustments(
                new UploadConversionAdjustmentsRequest()
                {
                    CustomerId = customerId.ToString(),
                    ConversionAdjustments = { conversionAdjustment },
                    PartialFailure = true,
                    ValidateOnly = false
                });

        ConversionAdjustmentResult result = response.Results[0];
        // Print the result.
        Console.WriteLine($"Uploaded conversion adjustment value of" +
            $" '{result.ConversionAction}' for Google Click ID " +
            $"'{result.GclidDateTimePair.Gclid}'");
    }
    catch (GoogleAdsException e)
    {
        Console.WriteLine("Failure:");
        Console.WriteLine($"Message: {e.Message}");
        Console.WriteLine($"Failure: {e.Failure}");
        Console.WriteLine($"Request ID: {e.RequestId}");
        throw;
    }
}

PHP

public static function runExample(
    GoogleAdsClient $googleAdsClient,
    int $customerId,
    int $conversionActionId,
    string $gclid,
    string $adjustmentType,
    string $conversionDateTime,
    string $adjustmentDateTime,
    float $restatementValue
) {
    $conversionAdjustmentType = ConversionAdjustmentType::value($adjustmentType);

    // Associates conversion adjustments with the existing conversion action.
    // The GCLID should have been uploaded before with a conversion.
    $conversionAdjustment = new ConversionAdjustment([
        'conversion_action' => new StringValue([
            'value' => ResourceNames::forConversionAction($customerId, $conversionActionId)
        ]),
        'adjustment_type' => $conversionAdjustmentType,
        'gclid_date_time_pair' => new GclidDateTimePair([
            'gclid' => new StringValue(['value' => $gclid]),
            'conversion_date_time' => new StringValue(['value' => $conversionDateTime])
        ]),
        'adjustment_date_time' => new StringValue(['value' => $adjustmentDateTime])
    ]);

    // Sets adjusted value for adjustment type RESTATEMENT.
    if (
        $restatementValue !== null
        && $conversionAdjustmentType === ConversionAdjustmentType::RESTATEMENT
    ) {
        $conversionAdjustment->setRestatementValue(new RestatementValue([
            'adjusted_value' => new DoubleValue(['value' => $restatementValue])
        ]));
    }

    // Issues a request to upload the conversion adjustment.
    $conversionAdjustmentUploadServiceClient =
        $googleAdsClient->getConversionAdjustmentUploadServiceClient();
    $response = $conversionAdjustmentUploadServiceClient->uploadConversionAdjustments(
        $customerId,
        [$conversionAdjustment],
        ['partialFailure' => true]
    );

    // Prints the status message if any partial failure error is returned.
    // Note: The details of each partial failure error are not printed here, you can refer to
    // the example HandlePartialFailure.php to learn more.
    if (!is_null($response->getPartialFailureError())) {
        printf(
            "Partial failures occurred: '%s'.%s",
            $response->getPartialFailureError()->getMessage(),
            PHP_EOL
        );
    } else {
        // Prints the result if exists.
        /** @var ConversionAdjustment $uploadedConversionAdjustment */
        $uploadedConversionAdjustment = $response->getResults()[0];
        printf(
            "Uploaded conversion adjustment of '%s' for Google Click ID '%s'.%s",
            $uploadedConversionAdjustment->getConversionActionUnwrapped(),
            $uploadedConversionAdjustment->getGclidDateTimePair()->getGclidUnwrapped(),
            PHP_EOL
        );
    }
}

Python

def main(client, customer_id, conversion_action_id, gclid, adjustment_type,
         conversion_date_time, adjustment_date_time, restatement_value):
    # Determine the adjustment type.
    conversion_adjustment_type_enum = (
        client.get_type('ConversionAdjustmentTypeEnum'))
    if adjustment_type.lower() == 'retraction':
        conversion_adjustment_type = conversion_adjustment_type_enum.RETRACTION
    elif adjustment_type.lower() == 'restatement':
        conversion_adjustment_type = (
            conversion_adjustment_type_enum.RESTATEMENT)
    else:
        raise ValueError('Invalid adjustment type specified.')

    # Associates conversion adjustments with the existing conversion action.
    # The GCLID should have been uploaded before with a conversion
    conversion_adjustment = (client.get_type('ConversionAdjustment',
                                             version='v3'))
    conversion_action_service = (client.get_service('ConversionActionService',
                                                    version='v3'))
    conversion_adjustment.conversion_action.value = (
        conversion_action_service.conversion_action_path(
            customer_id, conversion_action_id))
    conversion_adjustment.adjustment_type = conversion_adjustment_type
    conversion_adjustment.adjustment_date_time.value = adjustment_date_time

    # Set the Gclid Date
    conversion_adjustment.gclid_date_time_pair.gclid.value = gclid
    conversion_adjustment.gclid_date_time_pair.conversion_date_time.value = (
        conversion_date_time)

    # Sets adjusted value for adjustment type RESTATEMENT.
    if (restatement_value and
        conversion_adjustment_type ==
        conversion_adjustment_type_enum.RESTATEMENT):
        conversion_adjustment.restatement_value.adjusted_value.value = (
            float(restatement_value))

    conversion_adjustment_upload_service = (
        client.get_service('ConversionAdjustmentUploadService', version='v3'))
    try:
        response = (
            conversion_adjustment_upload_service.
            upload_conversion_adjustments(customer_id,
                                          [conversion_adjustment],
                                          partial_failure=True))
        conversion_adjustment_result = response.results[0]
        print(f'Uploaded conversion that occurred at '
              f'"{conversion_adjustment_result.adjustment_date_time.value}" '
              f'from Gclid '
              f'"{conversion_adjustment_result.gclid_date_time_pair.gclid.value}"'
              f' to "{conversion_adjustment_result.conversion_action.value}"')

    except GoogleAdsException as ex:
        print(f'Request with ID "{ex.request_id}" failed with status '
              f'"{ex.error.code().name}" and includes the following errors:')
        for error in ex.failure.errors:
            print(f'\tError with message "{error.message}".')
            if error.location:
                for field_path_element in error.location.field_path_elements:
                    print(f'\t\tOn field: {field_path_element.field_name}')
        sys.exit(1)

Perl

sub upload_conversion_adjustment {
  my ($api_client, $customer_id, $conversion_action_id, $gclid,
    $adjustment_type, $conversion_date_time, $adjustment_date_time,
    $restatement_value)
    = @_;

  # Associate conversion adjustments with the existing conversion action.
  # The GCLID should have been uploaded before with a conversion.
  my $conversion_adjustment =
    Google::Ads::GoogleAds::V3::Services::ConversionAdjustmentUploadService::ConversionAdjustment
    ->new({
      conversionAction =>
        Google::Ads::GoogleAds::V3::Utils::ResourceNames::conversion_action(
        $customer_id, $conversion_action_id
        ),
      adjustmentType => $adjustment_type,
      gclidDateTimePair =>
        Google::Ads::GoogleAds::V3::Services::ConversionAdjustmentUploadService::GclidDateTimePair
        ->new({
          gclid              => $gclid,
          conversionDateTime => $conversion_date_time
        }
        ),
      adjustmentDateTime => $adjustment_date_time,
    });

  # Set adjusted value for adjustment type RESTATEMENT.
  $conversion_adjustment->{restatementValue} =
    Google::Ads::GoogleAds::V3::Services::ConversionAdjustmentUploadService::RestatementValue
    ->new({
      adjustedValue => $restatement_value
    }) if defined $restatement_value && $adjustment_type eq RESTATEMENT;

  # Issue a request to upload the conversion adjustment.
  my $upload_conversion_adjustments_response =
    $api_client->ConversionAdjustmentUploadService()
    ->upload_conversion_adjustments({
      customerId            => $customer_id,
      conversionAdjustments => [$conversion_adjustment],
      partialFailure        => "true"
    });

  # Print any partial errors returned.
  if ($upload_conversion_adjustments_response->{partialFailureError}) {
    printf "Partial error encountered: '%s'.\n",
      $upload_conversion_adjustments_response->{partialFailureError}{message};
  }

  # Print the result if valid.
  my $uploaded_conversion_adjustment =
    $upload_conversion_adjustments_response->{results}[0];
  if (%$uploaded_conversion_adjustment) {
    printf "Uploaded conversion adjustment of the conversion action " .
      "with resource name '%s' for Google Click ID '%s'.\n",
      $uploaded_conversion_adjustment->{conversionAction},
      $uploaded_conversion_adjustment->{gclidDateTimePair}{gclid};
  }

  return 1;
}

Validation rules

Here are some things to keep in mind while adjusting conversions in the API:

  • Only the account that manages conversion actions will be able to upload adjustments.

  • The fields specifying date time require a timezone which does not need to be that of the account. The format of these fields is as yyyy-mm-dd hh:mm:ss+|-hh:mm, e.g., 2019-01-01 12:32:45-08:00.

  • The conversion has to already be reported in order to adjust it and to avoid a ConversionAdjustmentUploadError.CONVERSION_NOT_FOUND error.

  • When creating a ConversionAdjustment, the partial_failure attribute of the UploadConversionAdjustmentsRequest should always be set to true. Follow the partial failures guidelines when handling valid and failed operations simultaneously.