Upload Click Conversions

You can use the Google Ads API to upload offline click conversions into Google Ads, mapping to the Uploads conversion source in the Google Ads UI, followed by Conversions from clicks. It gives you more flexibility in associating clicks with conversions. You can track ads that led to sales in the offline world, such as over the phone or via a sales rep.

You must enable your website and lead-tracking system to capture and store the GCLID, the unique ID that Google Ads provides for every impression of a Google ad. If the click conversions are uploaded in the Google Ads UI, auto-tagging is automatically enabled so your website will start receiving the GCLID as a URL parameter. But this does not occur when using the Google Ads API, and you should enable auto-tagging by updating the auto_tagging_enabled attribute of Customer.

Code example

You need to associate your offline click conversions with a conversion action by passing the GCLID, conversion date time, conversion action resource name and optionally the conversion value and currency to ConversionUploadService:

Java

private void runExample(
    GoogleAdsClient googleAdsClient,
    long customerId,
    long conversionActionId,
    String gclid,
    String conversionDateTime,
    Double conversionValue) {
  // Gets the conversion action resource name.
  String conversionActionResourceName =
      ResourceNames.conversionAction(customerId, conversionActionId);

  // Creates the click conversion.
  ClickConversion clickConversion =
      ClickConversion.newBuilder()
          .setConversionAction(StringValue.of(conversionActionResourceName))
          .setConversionDateTime(StringValue.of(conversionDateTime))
          .setConversionValue(DoubleValue.of(conversionValue))
          .setCurrencyCode(StringValue.of("USD"))
          .setGclid(StringValue.of(gclid))
          .build();

  // Creates the conversion upload service client.
  try (ConversionUploadServiceClient conversionUploadServiceClient =
      googleAdsClient.getLatestVersion().createConversionUploadServiceClient()) {
    // Uploads the click conversion. Partial failure should always be set to true.
    UploadClickConversionsResponse response =
        conversionUploadServiceClient.uploadClickConversions(
            Long.toString(customerId),
            ImmutableList.of(clickConversion),
            // 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());
    }

    // Prints the result.
    ClickConversionResult result = response.getResults(0);
    // Only prints valid results.
    if (result.hasGclid()) {
      System.out.printf(
          "Uploaded conversion that occurred at '%s' from Google Click ID '%s' to '%s'.%n",
          result.getConversionDateTime().getValue(),
          result.getGclid().getValue(),
          result.getConversionAction().getValue());
    }
  }
}

C#

public void Run(GoogleAdsClient client, long customerId, long conversionActionId,
    string gclid, string conversionTime, double conversionValue)
{
    // Get the ConversionActionService.
    ConversionUploadServiceClient conversionUploadService =
        client.GetService(Services.V3.ConversionUploadService);

    // Creates a click conversion by specifying currency as USD.
    ClickConversion clickConversion = new ClickConversion()
    {
        ConversionAction = ResourceNames.ConversionAction(customerId, conversionActionId),
        Gclid = gclid,
        ConversionValue = conversionValue,
        ConversionDateTime = conversionTime,
        CurrencyCode = "USD"
    };

    try
    {
        // Issues a request to upload the click conversion.
        UploadClickConversionsResponse response =
            conversionUploadService.UploadClickConversions(
            customerId.ToString(), new[] { clickConversion }, true, false);

        // Prints the result.
        ClickConversionResult uploadedClickConversion = response.Results[0];
        Console.WriteLine($"Uploaded conversion that occurred at " +
            $"'{uploadedClickConversion.ConversionDateTime}' from Google " +
            $"Click ID '{uploadedClickConversion.Gclid}' to " +
            $"'{uploadedClickConversion.ConversionAction}'.");
    }
    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 $conversionDateTime,
    float $conversionValue
) {
    // Creates a click conversion by specifying currency as USD.
    $clickConversion = new ClickConversion([
        'conversion_action' => new StringValue([
            'value' => ResourceNames::forConversionAction($customerId, $conversionActionId)
        ]),
        'gclid' => new StringValue(['value' => $gclid]),
        'conversion_value' => new DoubleValue(['value' => $conversionValue]),
        'conversion_date_time' => new StringValue(['value' => $conversionDateTime]),
        'currency_code' => new StringValue(['value' => 'USD']),
    ]);

    // Issues a request to upload the click conversion.
    $conversionUploadServiceClient = $googleAdsClient->getConversionUploadServiceClient();
    /** @var UploadClickConversionsResponse $response */
    $response = $conversionUploadServiceClient->uploadClickConversions(
        $customerId,
        [$clickConversion],
        ['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 ClickConversion $uploadedClickConversion */
        $uploadedClickConversion = $response->getResults()[0];
        printf(
            "Uploaded click conversion that occurred at '%s' from Google Click ID '%s' " .
            "to '%s'.%s",
            $uploadedClickConversion->getConversionDateTimeUnwrapped(),
            $uploadedClickConversion->getGclidUnwrapped(),
            $uploadedClickConversion->getConversionActionUnwrapped(),
            PHP_EOL
        );
    }
}

Python

def main(client, customer_id, conversion_action_id, gclid, 
         conversion_date_time, conversion_value):
    """Creates a click conversion with a default currency of USD."""

    click_conversion = client.get_type('ClickConversion', version='v3')
    conversion_action_service = client.get_service('ConversionActionService',
                                                   version='v3')
    click_conversion.conversion_action.value = (
        conversion_action_service.conversion_action_path(
            customer_id, conversion_action_id))
    click_conversion.gclid.value = gclid
    click_conversion.conversion_value.value = float(conversion_value)
    click_conversion.conversion_date_time.value = conversion_date_time
    click_conversion.currency_code.value = 'USD'

    conversion_upload_service = client.get_service('ConversionUploadService',
                                                   version='v3')

    try:
        conversion_upload_response = (
            conversion_upload_service.upload_click_conversions(customer_id,
                [click_conversion], partial_failure=True))
        uploaded_click_conversion = conversion_upload_response.results[0]
        print(f'Uploaded conversion that occurred at '
              f'"{uploaded_click_conversion.conversion_date_time.value}" from '
              f'Google Click ID "{uploaded_click_conversion.gclid.value}" '
              f'to "{uploaded_click_conversion.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_offline_conversion {
  my ($api_client, $customer_id, $conversion_action_id, $gclid,
    $conversion_date_time, $conversion_value)
    = @_;

  # Create a click conversion by specifying currency as USD.
  my $click_conversion =
    Google::Ads::GoogleAds::V3::Services::ConversionUploadService::ClickConversion
    ->new({
      conversionAction =>
        Google::Ads::GoogleAds::V3::Utils::ResourceNames::conversion_action(
        $customer_id, $conversion_action_id
        ),
      gclid              => $gclid,
      conversionDateTime => $conversion_date_time,
      conversionValue    => $conversion_value,
      currencyCode       => "USD"
    });

  # Issue a request to upload the click conversion.
  my $upload_click_conversions_response =
    $api_client->ConversionUploadService()->upload_click_conversions({
      customerId     => $customer_id,
      conversions    => [$click_conversion],
      partialFailure => "true"
    });

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

  # Print the result if valid.
  my $uploaded_click_conversion =
    $upload_click_conversions_response->{results}[0];
  if (%$uploaded_click_conversion) {
    printf
      "Uploaded conversion that occurred at '%s' from Google Click ID '%s' " .
      "to the conversion action with resource name '%s'.\n",
      $uploaded_click_conversion->{conversionDateTime},
      $uploaded_click_conversion->{gclid},
      $uploaded_click_conversion->{conversionAction};
  }

  return 1;
}

Import externally attributed conversions

If you use third-party tools or homegrown solutions to track conversions, then you may want to give Google Ads only part of the credit for the conversion. Sometimes, you may also want to distribute a conversion's credit across multiple clicks. Externally attributed conversion imports allow you to upload conversions with fractional credit assigned to each GCLID.

To upload fractional credits, you need to follow the upload_offline_conversion code example, then specify the external_attribution_model and external_attribution_credit attributes for the ExternalAttributionData when creating the ClickConversion.

Uploading ClickConversion

There are several requirements that must be met when uploading a ClickConversion.

To avoid a ConversionUploadError.INVALID_CONVERSION_ACTION error, the conversion_action attribute must refer to a ConversionAction where:

  • The ConversionAction had a status of ENABLED at the time of the impression.

  • The ConversionAction existed in the effective conversion account of the click's Google Ads account at the time of the impression. If the account was not using cross-account conversion tracking at the time of the impression, Google Ads will look for the ConversionAction in the account used to upload conversions. You can find your account's effective conversion tracking account under TOOLS & SETTINGS > Conversions in the Google Ads UI, or the conversion_tracking_setting attribute of Customer in the Google Ads API, but keep in mind that this will show you the current effective conversion tracking account, which may differ from the effective conversion tracking account in place at the time of the impression.

In addition, the following conditions must be met:

  • The conversion_date_time must be after the impression happened, to avoid a ConversionUploadError.CONVERSION_PRECEDES_GCLID error.

  • The conversion_date_time must be before the click_through_lookback_window_days you specified for the ConversionAction, to avoid a ConversionUploadError.EXPIRED_GCLID error.

  • The conversion_value must be greater than or equal to zero.

  • The conversion_date_time must have a timezone specified, and the format is as yyyy-mm-dd hh:mm:ss+|-hh:mm, e.g., 2019-01-01 12:32:45-08:00. The timezone can be for any valid value: it does not have to match the account's timezone.

Creating ClickConversion

A few things to keep in mind when creating a ClickConversion:

  • The partial_failure attribute of the UploadClickConversionsRequest should always be set to true. Follow the partial failures guidelines when handlling valid and failed operations simultaneously.

  • Although duplicate uploads of a conversion (same GCLID, name and time) are permitted, only the first instance is recorded.

  • Uploaded conversions will be reflected in reports for the impression date of the original click, not the date of the upload request or the date of the conversion_date_time of the ClickConversion.

  • We recommend you wait 6 hours after creating the ConversionAction before uploading.

  • It takes up to 3 hours for imported conversion statistics to appear in your Google Ads account for last-click attribution. For other search attribution models, it can take longer than 3 hours.

  • When uploading click conversions for multiple accounts, you can specify the customer_id of a common manager account and include conversions with GCLIDs from across multiple accounts. A conversion is attributed to the proper account based on the origin of its GCLID.

  • When uploading click conversions with cross-account conversion tracking enabled, the conversion action must be in the manager account, rather than in the account associated with the GCLID.