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 through a sales rep.

ID parameters

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. However, this does not occur when using the Google Ads API, so you should enable auto-tagging by updating the auto_tagging_enabled attribute of Customer.

Starting with iOS 14, clicks can contain a wbraid parameter (for clicks associated with web conversions) or a gbraid parameter (for clicks associated with app conversions) instead of the gclid parameter. This feature is currently behind an allowlist, and we expect to eventually roll it out to all users. Version v9 of the Google Ads API adds support to specify wbraid and gbraid instead of gclid when uploading offline conversions.

Keep in mind the following restrictions when specifying combinations of wbraid, gbraid, gclid, and user_identifiers for a given ClickConversion:

  • Providing both wbraid and gbraid results in the error: GBRAID_WBRAID_BOTH_SET.

  • Providing wbraid or gbraid while also including a gclid or user_identifiers results in the error: VALUE_MUST_BE_UNSET.

  • Providing neither wbraid nor gbraid, but providing both gclid and user_identifiers: The gclid takes precedence.

Code example

You need to associate your offline click conversions with a conversion action by passing either the gclid, gbraid, or wbraid identifier. In addition, provide the 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 gbraid,
    String wbraid,
    String conversionDateTime,
    Double conversionValue,
    Long conversionCustomVariableId,
    String conversionCustomVariableValue,
    String orderId) {
  // Verifies that exactly one of gclid, gbraid, and wbraid is specified, as required.
  // See https://developers.google.com/google-ads/api/docs/conversions/upload-clicks for details.
  long numberOfIdsSpecified =
      Arrays.asList(gclid, gbraid, wbraid).stream().filter(idField -> idField != null).count();
  if (numberOfIdsSpecified != 1) {
    throw new IllegalArgumentException(
        "Exactly 1 of gclid, gbraid, or wbraid is required, but "
            + numberOfIdsSpecified
            + " ID values were provided");
  }

  // Constructs the conversion action resource name from the customer and conversion action IDs.
  String conversionActionResourceName =
      ResourceNames.conversionAction(customerId, conversionActionId);

  // Creates the click conversion.
  ClickConversion.Builder clickConversionBuilder =
      ClickConversion.newBuilder()
          .setConversionAction(conversionActionResourceName)
          .setConversionDateTime(conversionDateTime)
          .setConversionValue(conversionValue)
          .setCurrencyCode("USD");

  // Sets the single specified ID field.
  if (gclid != null) {
    clickConversionBuilder.setGclid(gclid);
  } else if (gbraid != null) {
    clickConversionBuilder.setGbraid(gbraid);
  } else {
    clickConversionBuilder.setWbraid(wbraid);
  }

  if (conversionCustomVariableId != null && conversionCustomVariableValue != null) {
    // Sets the custom variable and value, if provided.
    clickConversionBuilder.addCustomVariables(
        CustomVariable.newBuilder()
            .setConversionCustomVariable(
                ResourceNames.conversionCustomVariable(customerId, conversionCustomVariableId))
            .setValue(conversionCustomVariableValue));
  }

  if (orderId != null) {
    // Sets the order ID (unique transaction ID), if provided. An order ID is required in order to
    // upload enhancements as shown in the UploadConversionEnhancement example.
    clickConversionBuilder.setOrderId(orderId);
  }

  ClickConversion clickConversion = clickConversionBuilder.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(
            UploadClickConversionsRequest.newBuilder()
                .setCustomerId(Long.toString(customerId))
                .addConversions(clickConversion)
                // Enables partial failure (must be true).
                .setPartialFailure(true)
                .build());

    // Prints any partial errors returned.
    if (response.hasPartialFailureError()) {
      GoogleAdsFailure googleAdsFailure =
          ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError());
      googleAdsFailure
          .getErrorsList()
          .forEach(e -> System.out.println("Partial failure occurred: " + e.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(), result.getGclid(), result.getConversionAction());
    }
  }
}
      

C#

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

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

    // Verifies that exactly one of gclid, gbraid, and wbraid is specified, as required.
    // See https://developers.google.com/google-ads/api/docs/conversions/upload-clicks
    // for details.
    string[] ids = { gclid, gbraid, wbraid };
    int idCount = ids.Where(id => !string.IsNullOrEmpty(id)).Count();

    if (idCount != 1)
    {
        throw new ArgumentException($"Exactly 1 of gclid, gbraid, or wbraid is " +
            $"required, but {idCount} ID values were provided");
    }

    // Sets the single specified ID field.
    if (!string.IsNullOrEmpty(gclid))
    {
        clickConversion.Gclid = gclid;
    }
    else if (!string.IsNullOrEmpty(wbraid))
    {
        clickConversion.Wbraid = wbraid;
    }
    else if (!string.IsNullOrEmpty(gbraid))
    {
        clickConversion.Gbraid = gbraid;
    }

    try
    {
        // Issues a request to upload the click conversion.
        UploadClickConversionsResponse response =
            conversionUploadService.UploadClickConversions(
                new UploadClickConversionsRequest()
                {
                    CustomerId = customerId.ToString(),
                    Conversions = { clickConversion },
                    PartialFailure = true,
                    ValidateOnly = 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 $gbraid,
    ?string $wbraid,
    string $conversionDateTime,
    float $conversionValue,
    ?string $conversionCustomVariableId,
    ?string $conversionCustomVariableValue
) {
    // Verifies that exactly one of gclid, gbraid, and wbraid is specified, as required.
    // See https://developers.google.com/google-ads/api/docs/conversions/upload-clicks for details.
    $nonNullFields = array_filter(
        [$gclid, $gbraid, $wbraid],
        function ($field) {
            return !is_null($field);
        }
    );
    if (count($nonNullFields) !== 1) {
        throw new \UnexpectedValueException(
            sprintf(
                "Exactly 1 of gclid, gbraid or wbraid is required, but %d ID values were "
                . "provided",
                count($nonNullFields)
            )
        );
    }

    // Creates a click conversion by specifying currency as USD.
    $clickConversion = new ClickConversion([
        'conversion_action' =>
            ResourceNames::forConversionAction($customerId, $conversionActionId),
        'conversion_value' => $conversionValue,
        'conversion_date_time' => $conversionDateTime,
        'currency_code' => 'USD'
    ]);
    // Sets the single specified ID field.
    if (!is_null($gclid)) {
        $clickConversion->setGclid($gclid);
    } elseif (!is_null($gbraid)) {
        $clickConversion->setGbraid($gbraid);
    } else {
        $clickConversion->setWbraid($wbraid);
    }


    if (!is_null($conversionCustomVariableId) && !is_null($conversionCustomVariableValue)) {
        $clickConversion->setCustomVariables([new CustomVariable([
            'conversion_custom_variable' => ResourceNames::forConversionCustomVariable(
                $customerId,
                $conversionCustomVariableId
            ),
            'value' => $conversionCustomVariableValue
        ])]);
    }

    // Issues a request to upload the click conversion.
    $conversionUploadServiceClient = $googleAdsClient->getConversionUploadServiceClient();
    /** @var UploadClickConversionsResponse $response */
    $response = $conversionUploadServiceClient->uploadClickConversions(
        $customerId,
        [$clickConversion],
        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 ($response->hasPartialFailureError()) {
        printf(
            "Partial failures occurred: '%s'.%s",
            $response->getPartialFailureError()->getMessage(),
            PHP_EOL
        );
    } else {
        // Prints the result if exists.
        /** @var ClickConversionResult $uploadedClickConversion */
        $uploadedClickConversion = $response->getResults()[0];
        printf(
            "Uploaded click conversion that occurred at '%s' from Google Click ID '%s' " .
            "to '%s'.%s",
            $uploadedClickConversion->getConversionDateTime(),
            $uploadedClickConversion->getGclid(),
            $uploadedClickConversion->getConversionAction(),
            PHP_EOL
        );
    }
}
      

Python

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

    Args:
        client: An initialized GoogleAdsClient instance.
        customer_id: The client customer ID string.
        conversion_action_id: The ID of the conversion action to upload to.
        gclid: The Google Click Identifier ID. If set, the wbraid and gbraid
            parameters must be None.
        conversion_date_time: The the date and time of the conversion (should be
            after the click time). The format is 'yyyy-mm-dd hh:mm:ss+|-hh:mm',
            e.g. '2021-01-01 12:32:45-08:00'.
        conversion_value: The conversion value in the desired currency.
        conversion_custom_variable_id: The ID of the conversion custom
            variable to associate with the upload.
        conversion_custom_variable_value: The str value of the conversion custom
            variable to associate with the upload.
        gbraid: The GBRAID for the iOS app conversion. If set, the gclid and
            wbraid parameters must be None.
        wbraid: The WBRAID for the iOS app conversion. If set, the gclid and
            gbraid parameters must be None.
    """
    click_conversion = client.get_type("ClickConversion")
    conversion_action_service = client.get_service("ConversionActionService")
    click_conversion.conversion_action = conversion_action_service.conversion_action_path(
        customer_id, conversion_action_id
    )

    # Sets the single specified ID field.
    if gclid:
        click_conversion.gclid = gclid
    elif gbraid:
        click_conversion.gbraid = gbraid
    else:
        click_conversion.wbraid = wbraid

    click_conversion.conversion_value = float(conversion_value)
    click_conversion.conversion_date_time = conversion_date_time
    click_conversion.currency_code = "USD"

    if conversion_custom_variable_id and conversion_custom_variable_value:
        conversion_custom_variable = client.get_type("CustomVariable")
        conversion_custom_variable.conversion_custom_variable = (
            conversion_custom_variable_id
        )
        conversion_custom_variable.value = conversion_custom_variable_value
        click_conversion.custom_variables.append(conversion_custom_variable)

    conversion_upload_service = client.get_service("ConversionUploadService")
    request = client.get_type("UploadClickConversionsRequest")
    request.customer_id = customer_id
    request.conversions = [click_conversion]
    request.partial_failure = True
    conversion_upload_response = conversion_upload_service.upload_click_conversions(
        request=request,
    )
    uploaded_click_conversion = conversion_upload_response.results[0]
    print(
        f"Uploaded conversion that occurred at "
        f'"{uploaded_click_conversion.conversion_date_time}" from '
        f'Google Click ID "{uploaded_click_conversion.gclid}" '
        f'to "{uploaded_click_conversion.conversion_action}"'
    )
      

Ruby

def upload_offline_conversion(
  customer_id,
  conversion_action_id,
  gclid,
  gbraid,
  wbraid,
  conversion_date_time,
  conversion_value,
  conversion_custom_variable_id,
  conversion_custom_variable_value)
  # GoogleAdsClient will read a config file from
  # ENV['HOME']/google_ads_config.rb when called without parameters
  client = Google::Ads::GoogleAds::GoogleAdsClient.new

  # Verifies that exactly one of gclid, gbraid, and wbraid is specified, as required.
  # See https://developers.google.com/google-ads/api/docs/conversions/upload-clicks for details.
  identifiers_specified = [gclid, gbraid, wbraid].reject {|v| v.nil?}.count
  if identifiers_specified != 1
    raise "Must specify exactly one of GCLID, GBRAID, and WBRAID. " \
      "#{identifiers_specified} values were provided."
  end

  click_conversion = client.resource.click_conversion do |cc|
    cc.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)
    # Sets the single specified ID field.
    if !gclid.nil?
      cc.gclid = gclid
    elsif !gbraid.nil?
      cc.gbraid = gbraid
    else
      cc.wbraid = wbraid
    end
    cc.conversion_value = conversion_value.to_f
    cc.conversion_date_time = conversion_date_time
    cc.currency_code = 'USD'
    if conversion_custom_variable_id && conversion_custom_variable_value
      cc.custom_variables << client.resource.custom_variable do |cv|
        cv.conversion_custom_variable = client.path.conversion_custom_variable(
          customer_id, conversion_custom_variable_id)
        cv.value = conversion_custom_variable_value
      end
    end
  end

  response = client.service.conversion_upload.upload_click_conversions(
    customer_id: customer_id,
    conversions: [click_conversion],
    partial_failure: true,
  )
  if response.partial_failure_error.nil?
    result = response.results.first
    puts "Uploaded conversion that occurred at #{result.conversion_date_time} " \
      "from Google Click ID #{result.gclid} to #{result.conversion_action}."
  else
    failures = client.decode_partial_failure_error(response.partial_failure_error)
    puts "Request failed. Failure details:"
    failures.each do |failure|
      failure.errors.each do |error|
        puts "\t#{error.error_code.error_code}: #{error.message}"
      end
    end
  end
end
      

Perl

sub upload_offline_conversion {
  my (
    $api_client,                    $customer_id,
    $conversion_action_id,          $gclid,
    $gbraid,                        $wbraid,
    $conversion_date_time,          $conversion_value,
    $conversion_custom_variable_id, $conversion_custom_variable_value,
    $order_id
  ) = @_;

  # Verify that exactly one of gclid, gbraid, and wbraid is specified, as required.
  # See https://developers.google.com/google-ads/api/docs/conversions/upload-clicks for details.
  my $number_of_ids_specified = grep { defined $_ } ($gclid, $gbraid, $wbraid);
  if ($number_of_ids_specified != 1) {
    die sprintf "Exactly 1 of gclid, gbraid, or wbraid is required, " .
      "but %d ID values were provided.\n",
      $number_of_ids_specified;
  }

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

  # Set the single specified ID field.
  if (defined $gclid) {
    $click_conversion->{gclid} = $gclid;
  } elsif (defined $gbraid) {
    $click_conversion->{gbraid} = $gbraid;
  } else {
    $click_conversion->{wbraid} = $wbraid;
  }

  if ($conversion_custom_variable_id && $conversion_custom_variable_value) {
    $click_conversion->{customVariables} = [
      Google::Ads::GoogleAds::V10::Services::ConversionUploadService::CustomVariable
        ->new({
          conversionCustomVariable =>
            Google::Ads::GoogleAds::V10::Utils::ResourceNames::conversion_custom_variable(
            $customer_id, $conversion_custom_variable_id
            ),
          value => $conversion_custom_variable_value
        })];
  }

  if (defined $order_id) {
    # Set the order ID (unique transaction ID), if provided. An order ID is
    # required in order to upload enhancements as shown in the
    # upload_conversion_enhancement.pl example.
    $click_conversion->{orderId} = $order_id;
  }

  # 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 click.

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.

Include cart data in conversions

You can include shopping cart information for a ClickConversion in the cart_data field, which consists of the following attributes:

  • merchant_id: The ID of the associated Merchant Center account.
  • feed_country_code: The ISO 3166 two-character region code of the Merchant Center feed.
  • feed_language_code: The ISO 639-1 language code of the Merchant Center feed.
  • local_transaction_cost: The sum of all transaction-level discounts, in the currency_code of the ClickConversion.
  • items: The items in the shopping cart.

Each item in items consists of the following attributes:

  • product_id: The ID of the product, sometimes referred to as the offer ID or item ID.
  • quantity: The quantity of the item.
  • unit_price: The unit price of the item.

Upload 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 ConversionActionType is UPLOAD_CLICKS.
  • The status of the ConversionAction is ENABLED.
  • The ConversionAction exists in the effective conversion account of the click's Google Ads account.
  • At the time of the click, conversion tracking was enabled in the effective conversion account of the click's Google Ads account.
  • Starting with v8 of the Google Ads API, the customer_id of the UploadClickConversionsRequest must be the customer ID of the owner_customer of each click conversion's conversion_action.

In addition, the following conditions must be met:

  • Starting with v8 of the Google Ads API, the customer_id of the UploadClickConversionsRequest must be the customer ID of the effective conversion tracking account of the click's Google Ads account. Otherwise, the conversion upload will result in a ConversionUploadError.INVALID_CUSTOMER_FOR_CLICK error.

  • 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, for example: 2022-01-01 19:32:45-05:00 (ignoring daylight saving time) . The timezone can be for any valid value: it does not have to match the account's timezone.

Create 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.

  • Starting from version v9 of the Google Ads API, if you upload duplicate conversions (that is, same GCLID, name and time), then you will get a CLICK_CONVERSION_ALREADY_EXISTS error. In addition, if a single request contains multiple operations for the same conversion, you will get a DUPLICATE_CLICK_CONVERSION_IN_REQUEST error. In older versions of the API, duplicate uploads of a conversion are permitted, but 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.

  • If you encounter a TOO_RECENT_CONVERSION_ACTION or TOO_RECENT_EVENT response message to an upload conversion request, wait at least 6 hours after the action creation or event before retrying the failed rows.

  • 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.

    Starting with v8, this approach will succeed only if both of the following conditions are met:

    1. The effective conversion account of the client account of the GCLID is set to the manager specified in the request's customer_id.
    2. The conversion_action of the ClickConversion is owned by the manager specified in the request's customer_id.
  • 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.

  • If you plan to upload first-party data adjustments for enhanced conversions, you must provide an order_id for each ClickConversion.