Как управлять конверсиями по кликам в интернете

Вы можете использовать Google Ads API, чтобы управлять конверсиями по кликам в Google Рекламе. Конверсии по кликам онлайн помогают отслеживать объявления, которые привели к продажам в интернете, например на сайте.

Конверсии на сайте отслеживаются с помощью тега Google, но вы можете корректировать или улучшать их с помощью Google Ads API.

Если вы уже настроили тег Google и хотите изменить онлайн-конверсии, ознакомьтесь со статьей Как корректировать конверсии. Если у вас настроен тег Google и вы хотите улучшить отслеживание конверсий на сайте с помощью данных, предоставленных пользователями (расширенное отслеживание конверсий на сайтах), продолжайте чтение.

Расширенное отслеживание конверсий на сайтах

В следующем разделе рассказывается, как улучшить отслеживание конверсий по кликам, также известное как расширенное отслеживание конверсий на сайтах.

Для расширенного отслеживания конверсий на сайтах необходимо настроить тег, который автоматически отправляет в Google Рекламу идентификатор клика, например GCLID, и идентификатор заказа, когда пользователь совершает конверсию. Вы можете настроить расширенное отслеживание конверсий с помощью Google Менеджера тегов, тега Google или Google Ads API. Google Ads API позволяет отправлять собственные данные о конверсиях в течение 24 часов после события-конверсии, а не в тот же момент. Это позволяет вам передавать собственные данные из других источников, таких как база данных клиентов или CRM-система.

Как работает расширенное отслеживание конверсий на сайтах

Расширенное отслеживание конверсий на сайтах в Google Ads API дополняет шаг 3 в следующем процессе.

Вместо того чтобы отправлять хешированную информацию о пользователе, когда он совершает конверсию, тег отправляет только GCLID и идентификатор заказа. Хешированную информацию о пользователе вы отправляете позже, импортируя идентификатор заказа вместе с хешированными данными. Подробнее о расширенном отслеживании конверсий на сайтах…

Расширенное отслеживание конверсий на сайтах

Требования

Чтобы использовать расширенное отслеживание конверсий на сайтах, необходимо принять условия использования данных клиентов. Чтобы проверить, приняты ли условия использования данных клиентов, отправьте в аккаунт Google Рекламы, связанный с конверсиями, следующий запрос:

SELECT
  customer.id,
  customer.conversion_tracking_setting.accepted_customer_data_terms
FROM customer

Если accepted_customer_data_terms имеет значение false, выполните инструкции из статьи Как настроить расширенное отслеживание конверсий на сайтах.

Настройка тегов

Вы можете настроить теги для своего сайта, следуя инструкциям в статье Как настроить расширенное отслеживание конверсий на сайтах с помощью Google Ads API. Мы также рекомендуем добавить идентификаторы транзакций (идентификаторы заказов) в тег отслеживания конверсий, чтобы повысить точность сопоставления конверсий.

Реализация API

Ниже описан общий процесс импорта расширенных конверсий на сайтах в Google Ads API.

  1. Нормализуйте и хешируйте данные, предоставленные пользователями, например адрес электронной почты, номер телефона и почтовый адрес.

  2. Заполните объекты ConversionAdjustment нормализованными и хешированными данными, предоставленными пользователями.

  3. Импортируйте объекты ConversionAdjustment в ConversionAdjustmentUploadService.

  4. Проверьте импортированные данные.

Как нормализовать и хешировать данные, предоставленные пользователями

Для защиты конфиденциальности пользователей следующие данные будут хешированы по алгоритму SHA-256 перед импортом:

  • Адрес электронной почты
  • номер телефона;
  • имя;
  • фамилия;
  • почтовый адрес.

Не хешируйте следующие данные:

  • страна;
  • штат;
  • город;
  • Индекс

Чтобы стандартизировать результаты работы функции хеширования, перед ее запуском выполните следующие действия:

  • Удалите начальные и конечные пробельные символы.
  • Конвертируйте текст в нижний регистр.
  • Отформатируйте номера телефонов по стандарту E.164.

Форматирование адресов электронной почты для расширенного отслеживания конверсий

Чтобы обеспечить максимальный коэффициент соответствия при расширенном отслеживании конверсий, необходимо правильно отформатировать и хешировать адреса электронной почты. Для этого выполните следующие действия:

  1. Применить стандартную нормализацию (для всех адресов электронной почты)

    • Удалите начальные и конечные пробельные символы ASCII.
    • Все символы в адресе электронной почты приведены в нижнем регистре.
  2. Примените нормализацию для домена (только для gmail.com и googlemail.com):

    • После того как вы переведете адрес электронной почты в нижний регистр, проверьте, является ли домен (после символа @) gmail.com или googlemail.com.
    • Удалите точки (.). В части имени пользователя (до символа @) удалите все точки.
    • Удалите суффиксы со знаком плюса (+). В части имени пользователя удалите первый знак плюса (+) и все последующие символы до знака @.
      • Пример: Jane.Doe+Shopping@googlemail.com сначала преобразуется в jane.doe+shopping@googlemail.com (нижний регистр), а затем в janedoe@googlemail.com (правила Gmail и Googlemail).
    • Другие домены. Для любого домена, кроме gmail.com или googlemail.com, не удаляйте точки и суффиксы с плюсом из имени пользователя. Применяйте только стандартную нормализацию (удаление пробелов и преобразование в нижний регистр).
      • Пример: user.name+NYC@Example.com нормализуется в user.name+nyc@example.com.
  3. Хеширование нормализованного адреса электронной почты

    • Закодируйте нормализованную строку адреса электронной почты в байты UTF-8.
    • Захешируйте байты UTF-8 с помощью алгоритма SHA-256.
    • Полученный дайджест должен быть отформатирован как шестнадцатеричная строка в нижнем регистре (64 символа).
import hashlib
import re


def normalize_and_hash_email(email: str) -> str:
    """Normalizes and SHA-256 hashes an email address for enhanced conversions."""
    normalized = email.strip().lower()
    parts = normalized.split("@")
    if len(parts) != 2:
        raise ValueError("Invalid email address format.")
    username, domain = parts
    if domain in ("gmail.com", "googlemail.com"):
        username = username.replace(".", "")
        username = re.sub(r"\+.*$", "", username)
    normalized = f"{username}@{domain}"
    return hashlib.sha256(normalized.encode("utf-8")).hexdigest()

Пример кода

В этом примере показано, как нормализовать и хешировать данные, предоставленные пользователями.

Java

private String normalizeAndHash(MessageDigest digest, String s, boolean trimIntermediateSpaces)
    throws UnsupportedEncodingException {
  // Normalizes by first converting all characters to lowercase, then trimming spaces.
  String normalized = s.toLowerCase();
  if (trimIntermediateSpaces) {
    // Removes leading, trailing, and intermediate spaces.
    normalized = normalized.replaceAll("\\s+", "");
  } else {
    // Removes only leading and trailing spaces.
    normalized = normalized.trim();
  }
  // Hashes the normalized string using the hashing algorithm.
  byte[] hash = digest.digest(normalized.getBytes("UTF-8"));
  StringBuilder result = new StringBuilder();
  for (byte b : hash) {
    result.append(String.format("%02x", b));
  }

  return result.toString();
}

/**
 * Returns the result of normalizing and hashing an email address. For this use case, Google Ads
 * requires removal of any '.' characters or trailing '+' and characters that follow it from the
 * username portion of the email address if the domain is {@code gmail.com} or {@code
 * googlemail.com}.
 *
 * @param digest the digest to use to hash the normalized string.
 * @param emailAddress the email address to normalize and hash.
 */
private String normalizeAndHashEmailAddress(MessageDigest digest, String emailAddress)
    throws UnsupportedEncodingException {
  // Removes all whitespace (leading, trailing, and intermediate) from the email address.
  String normalizedEmail = emailAddress.toLowerCase().replaceAll("\\s+", "");
  String[] emailParts = normalizedEmail.split("@", 2);
  if (emailParts.length == 2 && emailParts[1].matches("^(gmail|googlemail)\\.com$")) {
    // Removes any '.' characters from the portion of the email address before the domain if the
    // domain is gmail.com or googlemail.com.
    emailParts[0] = emailParts[0].replaceAll("\\.", "");
    // Removes any '+' and all characters that follow it from the portion of the email address
    // before the domain if the domain is gmail.com or googlemail.com.
    emailParts[0] = emailParts[0].replaceAll("\\+.*", "");
    normalizedEmail = String.format("%s@%s", emailParts[0], emailParts[1]);
  }
  return normalizeAndHash(digest, normalizedEmail, true);
}

/**
 * Returns the result of normalizing and hashing a phone number. For this use case, Google Ads
 * requires phone numbers to be in E.164 format.
 *
 * @param digest the digest to use to hash the normalized string.
 * @param phoneNumber the phone number to normalize and hash.
 */
private String normalizeAndHashPhoneNumber(MessageDigest digest, String phoneNumber)
    throws UnsupportedEncodingException {
  // Removes non-digit characters and prepends a leading '+' sign.
  String digitsOnly = phoneNumber.replaceAll("[^0-9]", "");
  String formattedPhone = "+" + digitsOnly;
  if (!formattedPhone.matches("^\\+[1-9]\\d{6,14}$")) {
    throw new IllegalArgumentException("Phone number must be in E.164 format: " + phoneNumber);
  }
  return normalizeAndHash(digest, formattedPhone, true);
}
      

C#

/// <summary>
/// Normalizes the email address and hashes it. For this use case, Google Ads requires
/// removal of any '.' characters preceding <code>gmail.com</code> or
/// <code>googlemail.com</code>.
/// </summary>
/// <param name="emailAddress">The email address.</param>
/// <returns>The hash code.</returns>
private string NormalizeAndHashEmailAddress(string emailAddress)
{
    string normalizedEmail = emailAddress.ToLower();
    string[] emailParts = normalizedEmail.Split('@');
    if (emailParts.Length > 1 && (emailParts[1] == "gmail.com" ||
        emailParts[1] == "googlemail.com"))
    {
        // Removes any '.' characters from the portion of the email address before
        // the domain if the domain is gmail.com or googlemail.com.
        emailParts[0] = emailParts[0].Replace(".", "");
        normalizedEmail = $"{emailParts[0]}@{emailParts[1]}";
    }
    return NormalizeAndHash(normalizedEmail);
}

/// <summary>
/// Normalizes and hashes a string value.
/// </summary>
/// <param name="value">The value to normalize and hash.</param>
/// <returns>The normalized and hashed value.</returns>
private static string NormalizeAndHash(string value)
{
    return ToSha256String(digest, ToNormalizedValue(value));
}

/// <summary>
/// Hash a string value using SHA-256 hashing algorithm.
/// </summary>
/// <param name="digest">Provides the algorithm for SHA-256.</param>
/// <param name="value">The string value (e.g. an email address) to hash.</param>
/// <returns>The hashed value.</returns>
private static string ToSha256String(SHA256 digest, string value)
{
    byte[] digestBytes = digest.ComputeHash(Encoding.UTF8.GetBytes(value));
    // Convert the byte array into an unhyphenated hexadecimal string.
    return BitConverter.ToString(digestBytes).Replace("-", string.Empty);
}

/// <summary>
/// Removes leading and trailing whitespace and converts all characters to
/// lower case.
/// </summary>
/// <param name="value">The value to normalize.</param>
/// <returns>The normalized value.</returns>
private static string ToNormalizedValue(string value)
{
    return value.Trim().ToLower();
}
      

PHP

private static function normalizeAndHash(
    string $hashAlgorithm,
    string $value,
    bool $trimIntermediateSpaces
): string {
    // Normalizes by first converting all characters to lowercase, then trimming spaces.
    $normalized = strtolower($value);
    if ($trimIntermediateSpaces === true) {
        // Removes leading, trailing, and intermediate spaces.
        $normalized = str_replace(' ', '', $normalized);
    } else {
        // Removes only leading and trailing spaces.
        $normalized = trim($normalized);
    }
    return hash($hashAlgorithm, strtolower(trim($normalized)));
}

/**
 * Returns the result of normalizing and hashing an email address. For this use case, Google
 * Ads requires removal of any '.' characters preceding "gmail.com" or "googlemail.com".
 *
 * @param  string $hashAlgorithm the hash algorithm to use
 * @param  string $emailAddress  the email address to normalize and hash
 * @return string the normalized and hashed email address
 */
private static function normalizeAndHashEmailAddress(
    string $hashAlgorithm,
    string $emailAddress
): string {
    $normalizedEmail = strtolower($emailAddress);
    $emailParts = explode("@", $normalizedEmail);
    if (
        count($emailParts) > 1
        && preg_match('/^(gmail|googlemail)\.com\s*/', $emailParts[1])
    ) {
        // Removes any '.' characters from the portion of the email address before the domain
        // if the domain is gmail.com or googlemail.com.
        $emailParts[0] = str_replace(".", "", $emailParts[0]);
        $normalizedEmail = sprintf('%s@%s', $emailParts[0], $emailParts[1]);
    }
    return self::normalizeAndHash($hashAlgorithm, $normalizedEmail, true);
}
      

Python

def normalize_and_hash_email_address(email_address):
    """Returns the result of normalizing and hashing an email address.

    For this use case, Google Ads requires removal of any '.' characters
    preceding "gmail.com" or "googlemail.com"

    Args:
        email_address: An email address to normalize.

    Returns:
        A normalized (lowercase, removed whitespace) and SHA-265 hashed string.
    """
    normalized_email = email_address.strip().lower()
    email_parts = normalized_email.split("@")

    # Check that there are at least two segments
    if len(email_parts) > 1:
        # Removes any '.' and '+' characters from the portion of the email address
        # before the domain
        chars_to_remove = ".+"
        translation_table = str.maketrans("", "", chars_to_remove)
        email_parts[0] = email_parts[0].translate(translation_table)
        normalized_email = "@".join(email_parts)

    return normalize_and_hash(normalized_email)


def normalize_and_hash(s):
    """Normalizes and hashes a string with SHA-256.

    Private customer data must be hashed during upload, as described at:
    https://support.google.com/google-ads/answer/9888656

    Args:
        s: The string to perform this operation on.

    Returns:
        A normalized (lowercase, removed whitespace) and SHA-256 hashed string.
    """
    return hashlib.sha256(s.strip().lower().encode()).hexdigest()
      

Ruby

# Returns the result of normalizing and then hashing the string using the
# provided digest.  Private customer data must be hashed during upload, as
# described at https://support.google.com/google-ads/answer/9888656.
def normalize_and_hash(str)
  # Remove leading and trailing whitespace and ensure all letters are lowercase
  # before hasing.
  Digest::SHA256.hexdigest(str.strip.downcase)
end

# Returns the result of normalizing and hashing an email address. For this use
# case, Google Ads requires removal of any '.' characters preceding 'gmail.com'
# or 'googlemail.com'.
def normalize_and_hash_email(email)
  email_parts = email.downcase.split("@")
  # Removes any '.' characters from the portion of the email address before the
  # domain if the domain is gmail.com or googlemail.com.
  if email_parts.last =~ /^(gmail|googlemail)\.com\s*/
    email_parts[0] = email_parts[0].gsub('.', '')
  end
  normalize_and_hash(email_parts.join('@'))
end
      

Perl

sub normalize_and_hash {
  my $value                    = shift;
  my $trim_intermediate_spaces = shift;

  if ($trim_intermediate_spaces) {
    $value =~ s/\s+//g;
  } else {
    $value =~ s/^\s+|\s+$//g;
  }
  return sha256_hex(lc $value);
}

# Returns the result of normalizing and hashing an email address. For this use
# case, Google Ads requires removal of any '.' characters preceding 'gmail.com'
# or 'googlemail.com'.
sub normalize_and_hash_email_address {
  my $email_address = shift;

  my $normalized_email = lc $email_address;
  my @email_parts      = split('@', $normalized_email);
  if (scalar @email_parts > 1
    && $email_parts[1] =~ /^(gmail|googlemail)\.com\s*/)
  {
    # Remove any '.' characters from the portion of the email address before the
    # domain if the domain is 'gmail.com' or 'googlemail.com'.
    $email_parts[0] =~ s/\.//g;
    $normalized_email = sprintf '%s@%s', $email_parts[0], $email_parts[1];
  }
  return normalize_and_hash($normalized_email, 1);
}
      

curl

Как заполнять объекты ConversionAdjustment

В следующем разделе показано, как создать корректировку для улучшения, содержащую идентификатор заказа, а также идентификаторы адреса электронной почты, номера телефона и физического адреса, с применением стандартизации и хеширования, если это необходимо. Рекомендуем включать в корректировку значение gclid, если оно доступно.

Этот шаг почти полностью совпадает с инструкциями из руководства по импорту корректировок конверсий, но есть несколько нюансов:

  • Заполните user_identifiers нормализованными и хешированными пользовательскими данными. Если у вас есть несколько идентификаторов пользователей, создайте отдельный тег UserIdentifier для каждого из них (не более пяти).

Java

// Creates a builder for constructing the enhancement adjustment.
ConversionAdjustment.Builder enhancementBuilder =
    ConversionAdjustment.newBuilder().setAdjustmentType(ConversionAdjustmentType.ENHANCEMENT);

// Extracts user email, phone, and address info from the raw data, normalizes and hashes it,
// then wraps it in UserIdentifier objects.
// Creates a separate UserIdentifier object for each. The data in this example is hardcoded, but
// in your application you might read the raw data from an input file.

// IMPORTANT: Since the identifier attribute of UserIdentifier
// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a
// oneof
// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE of
// hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting more
// than one of these attributes on the same UserIdentifier will clear all the other members
// of the oneof. For example, the following code is INCORRECT and will result in a
// UserIdentifier with ONLY a hashedPhoneNumber.
//
// UserIdentifier incorrectlyPopulatedUserIdentifier =
//     UserIdentifier.newBuilder()
//         .setHashedEmail("...")
//         .setHashedPhoneNumber("...")
//         .build();

ImmutableMap.Builder<String, String> rawRecordBuilder =
    ImmutableMap.<String, String>builder()
        // Email address that includes a period (.) and plus (+) suffix before the Gmail domain.
        .put("email", "alex.2+myalias@gmail.com")
        // Address that includes all four required elements: first name, last name, country
        // code, and postal code.
        .put("firstName", "Alex")
        .put("lastName", "Quinn")
        .put("countryCode", "US")
        .put("postalCode", "94045")
        // Phone number to be converted to E.164 format, with a leading '+' as required.
        .put("phone", "+1 800-555-0102")
        // This example lets you put conversion details as arguments, but in reality you might
        // store this data alongside other user data, so we include it in this sample user
        // record.
        .put("orderId", orderId)
        .put("conversionActionId", Long.toString(conversionActionId))
        .put("currencyCode", "USD");

// Adds entries for the optional fields.
if (conversionDateTime != null) {
  rawRecordBuilder.put("conversionDateTime", conversionDateTime);
}
if (userAgent != null) {
  rawRecordBuilder.put("userAgent", userAgent);
}

// Builds the map representing the record.
Map<String, String> rawRecord = rawRecordBuilder.build();

// Creates a SHA256 message digest for hashing user identifiers in a privacy-safe way, as
// described at https://support.google.com/google-ads/answer/9888656.
MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256");

// Creates a list for the user identifiers.
List<UserIdentifier> userIdentifiers = new ArrayList<>();

// Creates a user identifier using the hashed email address, using the normalize and hash method
// specifically for email addresses.
UserIdentifier emailIdentifier =
    UserIdentifier.newBuilder()
        // Optional: specify the user identifier source.
        .setUserIdentifierSource(UserIdentifierSource.FIRST_PARTY)
        // Uses the normalize and hash method specifically for email addresses.
        .setHashedEmail(normalizeAndHashEmailAddress(sha256Digest, rawRecord.get("email")))
        .build();
userIdentifiers.add(emailIdentifier);

// Checks if the record has a phone number, and if so, adds a UserIdentifier for it.
if (rawRecord.containsKey("phone")) {
  UserIdentifier hashedPhoneNumberIdentifier =
      UserIdentifier.newBuilder()
          .setHashedPhoneNumber(
              normalizeAndHashPhoneNumber(sha256Digest, rawRecord.get("phone")))
          .build();
  // Adds the hashed phone number identifier to the UserData object's list.
  userIdentifiers.add(hashedPhoneNumberIdentifier);
}

// Checks if the record has all the required mailing address elements, and if so, adds a
// UserIdentifier for the mailing address.
if (rawRecord.containsKey("firstName")) {
  // Checks if the record contains all the other required elements of a mailing address.
  Set<String> missingAddressKeys = new HashSet<>();
  for (String addressKey : new String[] {"lastName", "countryCode", "postalCode"}) {
    if (!rawRecord.containsKey(addressKey)) {
      missingAddressKeys.add(addressKey);
    }
  }

  if (!missingAddressKeys.isEmpty()) {
    System.out.printf(
        "Skipping addition of mailing address information because the following required keys"
            + " are missing: %s%n",
        missingAddressKeys);
  } else {
    // Creates an OfflineUserAddressInfo object that contains all the required elements of a
    // mailing address.
    OfflineUserAddressInfo addressInfo =
        OfflineUserAddressInfo.newBuilder()
            .setHashedFirstName(
                normalizeAndHash(sha256Digest, rawRecord.get("firstName"), false))
            .setHashedLastName(normalizeAndHash(sha256Digest, rawRecord.get("lastName"), false))
            .setCountryCode(rawRecord.get("countryCode"))
            .setPostalCode(rawRecord.get("postalCode"))
            .build();
    UserIdentifier addressIdentifier =
        UserIdentifier.newBuilder().setAddressInfo(addressInfo).build();
    // Adds the address identifier to the UserData object's list.
    userIdentifiers.add(addressIdentifier);
  }
}

// Adds the user identifiers to the enhancement adjustment.
enhancementBuilder.addAllUserIdentifiers(userIdentifiers);
      

C#

// Normalize and hash the raw data, then wrap it in UserIdentifier objects.
// Create a separate UserIdentifier object for each. The data in this example is
// hardcoded, but in your application you might read the raw data from an input file.
//
// IMPORTANT: Since the identifier attribute of UserIdentifier
// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
// is a oneof
// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set
// only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,
// or address-info. Setting more than one of these attributes on the same UserIdentifier
// will clear all the other members of the oneof. For example, the following code is
// INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:
// UserIdentifier incorrectlyPopulatedUserIdentifier = new UserIdentifier()
// {
//         HashedEmail = "..."
//         HashedPhoneNumber = "..."
// }
UserIdentifier addressIdentifier = new UserIdentifier()
{
    AddressInfo = new OfflineUserAddressInfo()
    {
        HashedFirstName = NormalizeAndHash("Dana"),
        HashedLastName = NormalizeAndHash("Quinn"),
        HashedStreetAddress = NormalizeAndHash("1600 Amphitheatre Pkwy"),
        City = "Mountain View",
        State = "CA",
        PostalCode = "94043",
        CountryCode = "US"
    },
    // Optional: Specifies the user identifier source.
    UserIdentifierSource = UserIdentifierSource.FirstParty
};

// Creates a user identifier using the hashed email address.
UserIdentifier emailIdentifier = new UserIdentifier()
{
    UserIdentifierSource = UserIdentifierSource.FirstParty,
    // Uses the normalize and hash method specifically for email addresses.
    HashedEmail = NormalizeAndHashEmailAddress("dana@example.com")
};

// Adds the user identifiers to the enhancement adjustment.
enhancement.UserIdentifiers.AddRange(new[] { addressIdentifier, emailIdentifier });
      

PHP

// Creates the conversion enhancement.
$enhancement =
    new ConversionAdjustment(['adjustment_type' => ConversionAdjustmentType::ENHANCEMENT]);

// Extracts user email, phone, and address info from the raw data, normalizes and hashes it,
// then wraps it in UserIdentifier objects.
// Creates a separate UserIdentifier object for each. The data in this example is hardcoded,
// but in your application you might read the raw data from an input file.

// IMPORTANT: Since the identifier attribute of UserIdentifier
// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a
// oneof
// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE
// of hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting
// more than one of these attributes on the same UserIdentifier will clear all the other
// members of the oneof. For example, the following code is INCORRECT and will result in a
// UserIdentifier with ONLY a hashedPhoneNumber.
//
// $incorrectlyPopulatedUserIdentifier = new UserIdentifier([
//    'hashed_email' => '...',
//    'hashed_phone_number' => '...'
// ]);

$rawRecord = [
    // Email address that includes a period (.) before the Gmail domain.
    'email' => 'alex.2@example.com',
    // Address that includes all four required elements: first name, last name, country
    // code, and postal code.
    'firstName' => 'Alex',
    'lastName' => 'Quinn',
    'countryCode' => 'US',
    'postalCode' => '94045',
    // Phone number to be converted to E.164 format, with a leading '+' as required.
    'phone' => '+1 800 5550102',
    // This example lets you input conversion details as arguments, but in reality you might
    // store this data alongside other user data, so we include it in this sample user
    // record.
    'orderId' => $orderId,
    'conversionActionId' => $conversionActionId,
    'conversionDateTime' => $conversionDateTime,
    'currencyCode' => 'USD'
];

// Creates a list for the user identifiers.
$userIdentifiers = [];

// Uses the SHA-256 hash algorithm for hashing user identifiers in a privacy-safe way, as
// described at https://support.google.com/google-ads/answer/9888656.
$hashAlgorithm = "sha256";

// Creates a user identifier using the hashed email address, using the normalize and hash
// method specifically for email addresses.
$emailIdentifier = new UserIdentifier(
    [
    // Uses the normalize and hash method specifically for email addresses.
    'hashed_email' => self::normalizeAndHashEmailAddress(
        $hashAlgorithm,
        $rawRecord['email']
    ),
    // Optional: Specifies the user identifier source.
    'user_identifier_source' => UserIdentifierSource::FIRST_PARTY
    ]
);
$userIdentifiers[] = $emailIdentifier;

// Checks if the record has a phone number, and if so, adds a UserIdentifier for it.
if (array_key_exists('phone', $rawRecord)) {
    $hashedPhoneNumberIdentifier = new UserIdentifier(
        [
        'hashed_phone_number' => self::normalizeAndHash(
            $hashAlgorithm,
            $rawRecord['phone'],
            true
        )
        ]
    );
    // Adds the hashed email identifier to the user identifiers list.
    $userIdentifiers[] = $hashedPhoneNumberIdentifier;
}

// Checks if the record has all the required mailing address elements, and if so, adds a
// UserIdentifier for the mailing address.
if (array_key_exists('firstName', $rawRecord)) {
    // Checks if the record contains all the other required elements of a mailing
    // address.
    $missingAddressKeys = [];
    foreach (['lastName', 'countryCode', 'postalCode'] as $addressKey) {
        if (!array_key_exists($addressKey, $rawRecord)) {
            $missingAddressKeys[] = $addressKey;
        }
    }
    if (!empty($missingAddressKeys)) {
        printf(
            "Skipping addition of mailing address information because the "
            . "following required keys are missing: %s%s",
            json_encode($missingAddressKeys),
            PHP_EOL
        );
    } else {
        // Creates an OfflineUserAddressInfo object that contains all the required
        // elements of a mailing address.
        $addressIdentifier = new UserIdentifier(
            [
            'address_info' => new OfflineUserAddressInfo(
                [
                'hashed_first_name' => self::normalizeAndHash(
                    $hashAlgorithm,
                    $rawRecord['firstName'],
                    false
                ),
                'hashed_last_name' => self::normalizeAndHash(
                    $hashAlgorithm,
                    $rawRecord['lastName'],
                    false
                ),
                'country_code' => $rawRecord['countryCode'],
                'postal_code' => $rawRecord['postalCode']
                ]
            )
            ]
        );
        // Adds the address identifier to the user identifiers list.
        $userIdentifiers[] = $addressIdentifier;
    }
}

// Adds the user identifiers to the conversion.
$enhancement->setUserIdentifiers($userIdentifiers);
      

Python

# Extracts user email, phone, and address info from the raw data, normalizes
# and hashes it, then wraps it in UserIdentifier objects. Creates a separate
# UserIdentifier object for each. The data in this example is hardcoded, but
# in your application you might read the raw data from an input file.

# IMPORTANT: Since the identifier attribute of UserIdentifier
# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
# is a oneof
# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must
# set only ONE of hashed_email, hashed_phone_number, mobile_id,
# third_party_user_id, or address_info. Setting more than one of these
# attributes on the same UserIdentifier will clear all the other members of
# the oneof. For example, the following code is INCORRECT and will result in
# a UserIdentifier with ONLY a hashed_phone_number:
#
# incorrectly_populated_user_identifier = client.get_type("UserIdentifier")
# incorrectly_populated_user_identifier.hashed_email = "...""
# incorrectly_populated_user_identifier.hashed_phone_number = "...""

raw_record = {
    # Email address that includes a period (.) before the Gmail domain.
    "email": "alex.2@example.com",
    # Address that includes all four required elements: first name, last
    # name, country code, and postal code.
    "first_name": "Alex",
    "last_name": "Quinn",
    "country_code": "US",
    "postal_code": "94045",
    # Phone number to be converted to E.164 format, with a leading '+' as
    # required.
    "phone": "+1 800 5550102",
    # This example lets you input conversion details as arguments, but in
    # reality you might store this data alongside other user data, so we
    # include it in this sample user record.
    "order_id": order_id,
    "conversion_action_id": conversion_action_id,
    "conversion_date_time": conversion_date_time,
    "currency_code": "USD",
    "user_agent": user_agent,
}

# Constructs the enhancement adjustment.
conversion_adjustment = client.get_type("ConversionAdjustment")
conversion_adjustment.adjustment_type = (
    client.enums.ConversionAdjustmentTypeEnum.ENHANCEMENT
)

# Creates a user identifier using the hashed email address, using the
# normalize and hash method specifically for email addresses.
email_identifier = client.get_type("UserIdentifier")
# Optional: Specifies the user identifier source.
email_identifier.user_identifier_source = (
    client.enums.UserIdentifierSourceEnum.FIRST_PARTY
)
# Uses the normalize and hash method specifically for email addresses.
email_identifier.hashed_email = normalize_and_hash_email_address(
    raw_record["email"]
)
# Adds the email identifier to the conversion adjustment.
conversion_adjustment.user_identifiers.append(email_identifier)

# Checks if the record has a phone number, and if so, adds a UserIdentifier
# for it.
if raw_record.get("phone") is not None:
    phone_identifier = client.get_type("UserIdentifier")
    phone_identifier.hashed_phone_number = normalize_and_hash(
        raw_record["phone"]
    )
    # Adds the phone identifier to the conversion adjustment.
    conversion_adjustment.user_identifiers.append(phone_identifier)

# Checks if the record has all the required mailing address elements, and if
# so, adds a UserIdentifier for the mailing address.
if raw_record.get("first_name") is not None:
    # Checks if the record contains all the other required elements of a
    # mailing address.
    required_keys = ["last_name", "country_code", "postal_code"]
    # Builds a new list of the required keys that are missing from
    # raw_record.
    missing_keys = [
        key for key in required_keys if key not in raw_record.keys()
    ]
    if len(missing_keys) > 0:
        print(
            "Skipping addition of mailing address information because the"
            f"following required keys are missing: {missing_keys}"
        )
    else:
        # Creates a user identifier using sample values for the user address,
        # hashing where required.
        address_identifier = client.get_type("UserIdentifier")
        address_info = address_identifier.address_info
        address_info.hashed_first_name = normalize_and_hash(
            raw_record["first_name"]
        )
        address_info.hashed_last_name = normalize_and_hash(
            raw_record["last_name"]
        )
        address_info.country_code = raw_record["country_code"]
        address_info.postal_code = raw_record["postal_code"]
        # Adds the address identifier to the conversion adjustment.
        conversion_adjustment.user_identifiers.append(address_identifier)
      

Ruby

# Extracts user email, phone, and address info from the raw data, normalizes
# and hashes it, then wraps it in UserIdentifier objects. Creates a separate
# UserIdentifier object for each. The data in this example is hardcoded, but
# in your application you might read the raw data from an input file.

# IMPORTANT: Since the identifier attribute of UserIdentifier
# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
# is a oneof
# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must
# set only ONE of hashed_email, hashed_phone_number, mobile_id,
# third_party_user_id, or address_info. Setting more than one of these
# attributes on the same UserIdentifier will clear all the other members of
# the oneof. For example, the following code is INCORRECT and will result in
# a UserIdentifier with ONLY a hashed_phone_number:
#
# incorrectly_populated_user_identifier.hashed_email = "...""
# incorrectly_populated_user_identifier.hashed_phone_number = "...""

raw_record = {
    # Email address that includes a period (.) before the Gmail domain.
    "email" => "alex.2@example.com",
    # Address that includes all four required elements: first name, last
    # name, country code, and postal code.
    "first_name" => "Alex",
    "last_name" => "Quinn",
    "country_code" => "US",
    "postal_code" => "94045",
    # Phone number to be converted to E.164 format, with a leading '+' as
    # required.
    "phone" => "+1 800 5550102",
    # This example lets you input conversion details as arguments, but in
    # reality you might store this data alongside other user data, so we
    # include it in this sample user record.
    "order_id" => order_id,
    "conversion_action_id" => conversion_action_id,
    "conversion_date_time" => conversion_date_time,
    "currency_code" => "USD",
    "user_agent" => user_agent,
}

enhancement = client.resource.conversion_adjustment do |ca|
  ca.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)
  ca.adjustment_type = :ENHANCEMENT
  ca.order_id = order_id

  # Sets the conversion date and time if provided. Providing this value is
  # optional but recommended.
  unless conversion_date_time.nil?
    ca.gclid_date_time_pair = client.resource.gclid_date_time_pair do |pair|
      pair.conversion_date_time = conversion_date_time
    end
  end

  # Creates a user identifier using the hashed email address, using the
  # normalize and hash method specifically for email addresses.
  ca.user_identifiers << client.resource.user_identifier do |ui|
    # Uses the normalize and hash method specifically for email addresses.
    ui.hashed_email = normalize_and_hash_email(raw_record["email"])
    # Optional: Specifies the user identifier source.
    ui.user_identifier_source = :FIRST_PARTY
  end

  # Checks if the record has a phone number, and if so, adds a UserIdentifier
  # for it.
  unless raw_record["phone"].nil?
    ca.user_identifiers << client.resource.user_identifier do |ui|
      ui.hashed_phone_number = normalize_and_hash_email(raw_record["phone"])
    end
  end

  # Checks if the record has all the required mailing address elements, and if
  # so, adds a UserIdentifier for the mailing address.
  unless raw_record["first_name"].nil?
    # Checks if the record contains all the other required elements of a
    # mailing address.
    required_keys = ["last_name", "country_code", "postal_code"]
    # Builds a new list of the required keys that are missing from
    # raw_record.
    missing_keys = required_keys - raw_record.keys
    if missing_keys
        puts(
            "Skipping addition of mailing address information because the" \
            "following required keys are missing: #{missing_keys}"
        )
    else
      ca.user_identifiers << client.resource.user_identifier do |ui|
        ui.address_info = client.resource.offline_user_address_info do |info|
          # Certain fields must be hashed using SHA256 in order to handle
          # identifiers in a privacy-safe way, as described at
          # https://support.google.com/google-ads/answer/9888656.
          info.hashed_first_name = normalize_and_hash( raw_record["first_name"])
          info.hashed_last_name = normalize_and_hash( raw_record["last_name"])
          info.postal_code = normalize_and_hash(raw_record["country_code"])
          info.country_code = normalize_and_hash(raw_record["postal_code"])
        end
      end
    end
  end
      

Perl

# Construct the enhancement adjustment.
my $enhancement =
  Google::Ads::GoogleAds::V25::Services::ConversionAdjustmentUploadService::ConversionAdjustment
  ->new({
    adjustmentType => ENHANCEMENT
  });

# Extract user email, phone, and address info from the raw data,
# normalize and hash it, then wrap it in UserIdentifier objects.
# Create a separate UserIdentifier object for each.
# The data in this example is hardcoded, but in your application
# you might read the raw data from an input file.
#
# IMPORTANT: Since the identifier attribute of UserIdentifier
# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)
# is a oneof
# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set
# only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,
# or address-info. Setting more than one of these attributes on the same UserIdentifier
# will clear all the other members of the oneof. For example, the following code is
# INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:
#
# my $incorrect_user_identifier = Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({
#   hashedEmail => '...',
#   hashedPhoneNumber => '...',
# });
my $raw_record = {
  # Email address that includes a period (.) before the Gmail domain.
  email => 'alex.2@example.com',
  # Address that includes all four required elements: first name, last
  # name, country code, and postal code.
  firstName   => 'Alex',
  lastName    => 'Quinn',
  countryCode => 'US',
  postalCode  => '94045',
  # Phone number to be converted to E.164 format, with a leading '+' as
  # required.
  phone => '+1 800 5550102',
  # This example lets you input conversion details as arguments,
  # but in reality you might store this data alongside other user data,
  # so we include it in this sample user record.
  orderId            => $order_id,
  conversionActionId => $conversion_action_id,
  conversionDateTime => $conversion_date_time,
  currencyCode       => "USD",
  userAgent          => $user_agent,
};
my $user_identifiers = [];

# Create a user identifier using the hashed email address, using the normalize
# and hash method specifically for email addresses.
my $hashed_email = normalize_and_hash_email_address($raw_record->{email});
push(
  @$user_identifiers,
  Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({
      hashedEmail => $hashed_email,
      # Optional: Specify the user identifier source.
      userIdentifierSource => FIRST_PARTY
    }));

# Check if the record has a phone number, and if so, add a UserIdentifier for it.
if (defined $raw_record->{phone}) {
  # Add the hashed phone number identifier to the list of UserIdentifiers.
  push(
    @$user_identifiers,
    Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({
        hashedPhoneNumber => normalize_and_hash($raw_record->{phone}, 1)}));
}

# Confirm the record has all the required mailing address elements, and if so, add
# a UserIdentifier for the mailing address.
if (defined $raw_record->{firstName}) {
  my $required_keys = ["lastName", "countryCode", "postalCode"];
  my $missing_keys  = [];

  foreach my $key (@$required_keys) {
    if (!defined $raw_record->{$key}) {
      push(@$missing_keys, $key);
    }
  }

  if (@$missing_keys) {
    print
      "Skipping addition of mailing address information because the following"
      . "keys are missing: "
      . join(",", @$missing_keys);
  } else {
    push(
      @$user_identifiers,
      Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({
          addressInfo =>
            Google::Ads::GoogleAds::V25::Common::OfflineUserAddressInfo->new({
              # First and last name must be normalized and hashed.
              hashedFirstName => normalize_and_hash($raw_record->{firstName}),
              hashedLastName  => normalize_and_hash($raw_record->{lastName}),
              # Country code and zip code are sent in plain text.
              countryCode => $raw_record->{countryCode},
              postalCode  => $raw_record->{postalCode},
            })}));
  }
}

# Add the user identifiers to the enhancement adjustment.
$enhancement->{userIdentifiers} = $user_identifiers;
      

curl

  • Убедитесь, что для параметра order_id задан идентификатор заказа конверсии, которую нужно скорректировать. Он должен совпадать с идентификатором заказа, отправленным тегом.

  • Присвойте свойству adjustment_type значение ENHANCEMENT.

  • Установите для conversion_action название ресурса ConversionAction с type WEBPAGE.

  • Если возможно, заполните поле gclid_date_time_pair, чтобы получить наиболее точные данные. Установите для параметра conversion_date_time дату и время, когда произошла конверсия с указанным параметром order_id. Укажите смещение часового пояса и используйте формат yyyy-mm-dd HH:mm:ss+|-HH:mm, например 2022-01-01 19:32:45-05:00 (без учета летнего времени).

  • (Необязательно.) Установите для параметра user_agent значение user-agent запроса, который отправил исходную конверсию, чтобы конверсия и ее расширение были отнесены к одному устройству или к нескольким устройствам.

Java

// Sets the conversion action.
enhancementBuilder.setConversionAction(
    ResourceNames.conversionAction(
        customerId, Long.parseLong(rawRecord.get("conversionActionId"))));

// Sets the order ID. Enhancements MUST use order ID instead of GCLID date/time pair.
enhancementBuilder.setOrderId(rawRecord.get("orderId"));

// Sets the conversion date and time if provided. Providing this value is optional but
// recommended.
if (rawRecord.containsKey("conversionDateTime")) {
  enhancementBuilder.setGclidDateTimePair(
      GclidDateTimePair.newBuilder()
          .setConversionDateTime(rawRecord.get("conversionDateTime")));
}

// Sets the user agent if provided. This should match the user agent of the request that sent
// the original conversion so the conversion and its enhancement are either both attributed as
// same-device or both attributed as cross-device.
if (rawRecord.containsKey("userAgent")) {
  enhancementBuilder.setUserAgent(rawRecord.get("userAgent"));
}
      

C#

// Set the conversion action.
enhancement.ConversionAction =
    ResourceNames.ConversionAction(customerId, conversionActionId);

// Set the order ID. Enhancements MUST use order ID instead of GCLID date/time pair.
enhancement.OrderId = orderId;

// Sets the conversion date and time if provided. Providing this value is optional but
// recommended.
if (string.IsNullOrEmpty(conversionDateTime))
{
    enhancement.GclidDateTimePair = new GclidDateTimePair()
    {
        ConversionDateTime = conversionDateTime
    };
}

// Sets optional fields where a value was provided.
if (!string.IsNullOrEmpty(userAgent))
{
    // Sets the user agent. This should match the user agent of the request that
    // sent the original conversion so the conversion and its enhancement are either
    // both attributed as same-device or both attributed as cross-device.
    enhancement.UserAgent = userAgent;
}

      

PHP

// Sets the conversion action.
$enhancement->setConversionAction(
    ResourceNames::forConversionAction($customerId, $rawRecord['conversionActionId'])
);

// Sets the order ID. Enhancements MUST use order ID instead of GCLID date/time pair.
if (!empty($rawRecord['orderId'])) {
    $enhancement->setOrderId($rawRecord['orderId']);
}

// Sets the conversion date and time if provided. Providing this value is optional but
// recommended.
if (!empty($rawRecord['conversionDateTime'])) {
    // Sets the conversion date and time if provided. Providing this value is optional but
    // recommended.
    $enhancement->setGclidDateTimePair(
        new GclidDateTimePair(
            [
            'conversion_date_time' => $rawRecord['conversionDateTime']
            ]
        )
    );
}

// Sets the user agent if provided. This should match the user agent of the request that
// sent the original conversion so the conversion and its enhancement are either both
// attributed as same-device or both attributed as cross-device.
if (!empty($rawRecord['userAgent'])) {
    $enhancement->setUserAgent($rawRecord['userAgent']);
}
      

Python

conversion_action_service = client.get_service("ConversionActionService")
# Sets the conversion action.
conversion_adjustment.conversion_action = (
    conversion_action_service.conversion_action_path(
        customer_id, raw_record["conversion_action_id"]
    )
)

# Sets the order ID. Enhancements MUST use order ID instead of GCLID
# date/time pair.
conversion_adjustment.order_id = order_id

# Sets the conversion date and time if provided. Providing this value is
# optional but recommended.
if raw_record.get("conversion_date_time"):
    conversion_adjustment.gclid_date_time_pair.conversion_date_time = (
        raw_record["conversion_date_time"]
    )

# Sets optional fields where a value was provided
if raw_record.get("user_agent"):
    # Sets the user agent. This should match the user agent of the request
    # that sent the original conversion so the conversion and its
    # enhancement are either both attributed as same-device or both
    # attributed as cross-device.
    conversion_adjustment.user_agent = user_agent
      

Ruby

ca.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)
ca.adjustment_type = :ENHANCEMENT
ca.order_id = order_id

# Sets the conversion date and time if provided. Providing this value is
# optional but recommended.
unless conversion_date_time.nil?
  ca.gclid_date_time_pair = client.resource.gclid_date_time_pair do |pair|
    pair.conversion_date_time = conversion_date_time
  end
end

# Creates a user identifier using the hashed email address, using the
# normalize and hash method specifically for email addresses.
ca.user_identifiers << client.resource.user_identifier do |ui|
  # Uses the normalize and hash method specifically for email addresses.
  ui.hashed_email = normalize_and_hash_email(raw_record["email"])
  # Optional: Specifies the user identifier source.
  ui.user_identifier_source = :FIRST_PARTY
end

# Checks if the record has a phone number, and if so, adds a UserIdentifier
# for it.
unless raw_record["phone"].nil?
  ca.user_identifiers << client.resource.user_identifier do |ui|
    ui.hashed_phone_number = normalize_and_hash_email(raw_record["phone"])
  end
end

# Checks if the record has all the required mailing address elements, and if
# so, adds a UserIdentifier for the mailing address.
unless raw_record["first_name"].nil?
  # Checks if the record contains all the other required elements of a
  # mailing address.
  required_keys = ["last_name", "country_code", "postal_code"]
  # Builds a new list of the required keys that are missing from
  # raw_record.
  missing_keys = required_keys - raw_record.keys
  if missing_keys
      puts(
          "Skipping addition of mailing address information because the" \
          "following required keys are missing: #{missing_keys}"
      )
  else
    ca.user_identifiers << client.resource.user_identifier do |ui|
      ui.address_info = client.resource.offline_user_address_info do |info|
        # Certain fields must be hashed using SHA256 in order to handle
        # identifiers in a privacy-safe way, as described at
        # https://support.google.com/google-ads/answer/9888656.
        info.hashed_first_name = normalize_and_hash( raw_record["first_name"])
        info.hashed_last_name = normalize_and_hash( raw_record["last_name"])
        info.postal_code = normalize_and_hash(raw_record["country_code"])
        info.country_code = normalize_and_hash(raw_record["postal_code"])
      end
    end
  end
end

# Sets optional fields where a value was provided.
unless user_agent.nil?
  # Sets the user agent. This should match the user agent of the request
  # that sent the original conversion so the conversion and its enhancement
  # are either both attributed as same-device or both attributed as
  # cross-device.
  ca.user_agent = user_agent
end
      

Perl

# Set the conversion action.
$enhancement->{conversionAction} =
  Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_action(
  $customer_id, $raw_record->{conversionActionId});

# Set the order ID. Enhancements MUST use order ID instead of GCLID date/time pair.
$enhancement->{orderId} = $raw_record->{orderId};

# Set the conversion date and time if provided. Providing this value is optional
# but recommended.
if (defined $raw_record->{conversionDateTime}) {
  $enhancement->{gclidDateTimePair} =
    Google::Ads::GoogleAds::V25::Services::ConversionAdjustmentUploadService::GclidDateTimePair
    ->new({
      conversionDateTime => $raw_record->{conversionDateTime}});
}

# Set the user agent if provided. This should match the user agent of the
# request that sent the original conversion so the conversion and its enhancement
# are either both attributed as same-device or both attributed as cross-device.
if (defined $raw_record->{userAgent}) {
  $enhancement->{userAgent} = $raw_record->{userAgent};
}
      

curl

Улучшения импорта конверсий

Java

// Creates the conversion adjustment upload service client.
try (ConversionAdjustmentUploadServiceClient conversionUploadServiceClient =
    googleAdsClient.getLatestVersion().createConversionAdjustmentUploadServiceClient()) {
  // Uploads the enhancement adjustment. Partial failure should always be set to true.

  // NOTE: This request contains a single adjustment as a demonstration. However, if you have
  // multiple adjustments to upload, it's best to upload multiple adjustments per request
  // instead of sending a separate request per adjustment. See the following for per-request
  // limits:
  // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjustment_upload_service
  UploadConversionAdjustmentsResponse response =
      conversionUploadServiceClient.uploadConversionAdjustments(
          UploadConversionAdjustmentsRequest.newBuilder()
              .setCustomerId(Long.toString(customerId))
              .addConversionAdjustments(enhancementBuilder)
              // Enables partial failure (must be true).
              .setPartialFailure(true)
              .build());
      

C#

// Uploads the enhancement adjustment. Partial failure should always be set to true.
//
// NOTE: This request contains a single adjustment as a demonstration.
// However, if you have multiple adjustments to upload, it's best to upload
// multiple adjustmenst per request instead of sending a separate request per
// adjustment. See the following for per-request limits:
// https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjust
UploadConversionAdjustmentsResponse response =
    conversionAdjustmentUploadService.UploadConversionAdjustments(
        new UploadConversionAdjustmentsRequest()
        {
            CustomerId = customerId.ToString(),
            ConversionAdjustments = { enhancement },
            // Enables partial failure (must be true).
            PartialFailure = true,
        });
      

PHP

// Issues a request to upload the conversion enhancement.
$conversionAdjustmentUploadServiceClient =
    $googleAdsClient->getConversionAdjustmentUploadServiceClient();
// NOTE: This request contains a single adjustment as a demonstration. However, if you have
// multiple adjustments to upload, it's best to upload multiple adjustments per request
// instead of sending a separate request per adjustment. See the following for per-request
// limits:
// https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjustment_upload_service
$response = $conversionAdjustmentUploadServiceClient->uploadConversionAdjustments(
    // Enables partial failure (must be true).
    UploadConversionAdjustmentsRequest::build($customerId, [$enhancement], true)
);
      

Python

# Creates the conversion adjustment upload service client.
conversion_adjustment_upload_service = client.get_service(
    "ConversionAdjustmentUploadService"
)
# Uploads the enhancement adjustment. Partial failure should always be set
# to true.
# NOTE: This request only uploads a single conversion, but if you have
# multiple conversions to upload, it's still best to upload them in a single
# request. See the following for per-request limits for reference:
# https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service
response = conversion_adjustment_upload_service.upload_conversion_adjustments(
    customer_id=customer_id,
    conversion_adjustments=[conversion_adjustment],
    # Enables partial failure (must be true).
    partial_failure=True,
)
      

Ruby

response = client.service.conversion_adjustment_upload.upload_conversion_adjustments(
  customer_id: customer_id,
  # NOTE: This request only uploads a single conversion, but if you have
  # multiple conversions to upload, it's still best to upload them in a single
  # request. See the following for per-request limits for reference:
  # https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service
  conversion_adjustments: [enhancement],
  # Partial failure must be set to true.
  partial_failure: true,
)
      

Perl

# Upload the enhancement adjustment. Partial failure should always be set to true.
#
# NOTE: This request contains a single adjustment as a demonstration.
# However, if you have multiple adjustments to upload, it's best to
# upload multiple adjustments per request instead of sending a separate
# request per adjustment. See the following for per-request limits:
# https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjustment_upload_service
my $response =
  $api_client->ConversionAdjustmentUploadService()
  ->upload_conversion_adjustments({
    customerId            => $customer_id,
    conversionAdjustments => [$enhancement],
    # Enable partial failure (must be true).
    partialFailure => "true"
  });
      

curl

Как проверить импортированные данные

Используйте отчет о диагностике API расширенного отслеживания конверсий, чтобы проверить, эффективно ли работает расширенное отслеживание конверсий на сайтах, и выявить проблемы в настройках.

Используйте диагностику офлайн-данных, чтобы проверить общее состояние последних импортированных данных.

При создании отчетов о показателях конверсий для кампаний используйте сопоставление показателей интерфейса, чтобы соотносить показатели интерфейса Google Рекламы с полями отчетов Google Ads API. Вы также можете запросить ресурс conversion_action, чтобы посмотреть общее количество конверсий и общую ценность конверсий для определенного действия-конверсии.

Рекомендации

При настройке расширенного отслеживания конверсий на сайтах следуйте приведенным ниже рекомендациям.

Как проверить идентификатор клиента

При импорте конверсий идентификатор клиента должен быть задан для аккаунта, в котором настроено отслеживание конверсий и создано действие-конверсия. Чтобы проверить эту настройку, запросите ресурс Customer, который используется для показа объявлений Google Рекламы, для ConversionTrackingSetting. Чтобы найти значение customer.conversion_tracking_setting.google_ads_conversion_customer, выполните следующий запрос с GoogleAdsService.SearchStream:

SELECT
  customer.conversion_tracking_setting.google_ads_conversion_customer,
  customer.conversion_tracking_setting.conversion_tracking_status,
  customer.conversion_tracking_setting.conversion_tracking_id,
  customer.conversion_tracking_setting.cross_account_conversion_tracking_id
FROM customer

Установите для параметра customer_id значение google_ads_conversion_customer в запросах на импорт конверсий.

Если у вас есть несколько идентификаторов, укажите их все.

Если у вас есть GCLID для конверсии, рекомендуем отправлять его вместе с user_identifiers, чтобы повысить эффективность. Кроме того, если у вас есть несколько UserIdentifier для конверсии, добавление всех этих параметров может повысить вероятность сопоставления. Укажите все идентификаторы в одном теге ConversionAdjustment.

Как устранять ошибки частичного сбоя при настройке интеграции

При первой настройке расширенного отслеживания конверсий на сайтах проверьте и исправьте все частичные ошибки в поле partial_failure_error ответа. Если в настройках есть проблемы, вы можете изучить это поле, чтобы выявить и устранить их. Дополнительную информацию и примеры обработки ошибок частичного сбоя можно найти в руководстве по частичным сбоям.

После того как вы устраните все проблемы, выявленные ошибками частичной загрузки, и завершите интеграцию, перейдите на диагностику офлайн-данных, чтобы регулярно проверять корректность конверсий.

Как выполнять несколько корректировок в одном запросе

Если вам нужно импортировать несколько корректировок, объедините операции (до 2000 корректировок конверсий на запрос) в один UploadConversionAdjustmentsRequest, а не отправляйте запрос на импорт для каждой корректировки.

Ознакомьтесь с руководством по квотам, чтобы узнать, сколько корректировок можно вносить в один запрос. Если вы хотите, чтобы диагностика офлайн-данных сгруппировала набор запросов в одно логическое задание, задайте для параметра job_id всех запросов одно и то же значение.

Устранение неполадок

Используйте отчет о диагностике API расширенного отслеживания конверсий, чтобы проверить, эффективно ли работает расширенное отслеживание конверсий на сайтах, и выявить проблемы в настройках.

Как устранять распространенные ошибки

Ниже приведены инструкции по диагностике и устранению распространенных ошибок, связанных с расширенным отслеживанием конверсий на сайтах.

Ошибка
CONVERSION_NOT_FOUND Не удалось найти конверсию для указанной пары "действие-конверсия – идентификатор конверсии". order_id Убедитесь, что конверсия является конверсией Google Рекламы для клиента, выполняющего конверсии в Google Рекламе, указанного в customer_id запроса.
Кроме того, убедитесь, что конверсия не была связана с кликом, в котором вместо gclid были указаны значения gbraid или wbraid. Google Реклама не поддерживает расширенное отслеживание конверсий на сайтах для таких конверсий.
CUSTOMER_NOT_ACCEPTED_CUSTOMER_DATA_TERMS Условия использования данных клиентов не были приняты для customer_id запроса. Инструкции по этому вопросу приведены в руководстве по предварительным требованиям.
CONVERSION_ALREADY_ENHANCED Для этой конверсии уже была выполнена корректировка с указанными значениями order_id и conversion_action. Убедитесь, что для каждой конверсии указан уникальный order_id.
CONVERSION_ACTION_NOT_ELIGIBLE_FOR_ENHANCEMENT Указанное значение conversion_action не допущено для расширенного отслеживания конверсий на сайтах. В интерфейсе Google Рекламы установите флажок Включить расширенное отслеживание конверсий для действия-конверсии, на которое ссылается ваш запрос. Инструкции можно найти в Справочном центре.