웹용 향상된 전환 구현하기

웹용 향상된 전환을 사용하면 거래 ID와 해싱된 사용자 식별자가 포함된 전환 조정을 업로드하여 웹 전환 측정의 정확성을 개선할 수 있습니다.

구현을 진행하기 전에 시작하기 가이드를 검토하고 웹용 향상된 전환의 기본 요건을 구현해야 합니다.

사용량 흐름

다음은 Google Ads API에서 웹용 향상된 전환을 업로드하는 전반적인 흐름입니다.

  1. 다음을 포함한 기본 요건을 구현했는지 확인합니다.

    1. 전환 추적을 사용 설정합니다.

    2. 고객 데이터 약관에 동의합니다.

    3. 웹사이트에서 태그를 구성합니다.

    자세한 내용은 기본 요건 구현을 참고하세요.

  2. 이메일 주소, 전화번호, 우편 주소와 같은 사용자 데이터를 정규화하고 해싱합니다.

  3. 정규화되고 해싱된 사용자 데이터를 ConversionAdjustment 객체에 넣습니다. 각 ConversionAdjustment에 다음을 실행합니다.

    1. order_id를 조정할 전환의 주문 ID로 설정합니다. 이 ID는 태그에서 전송한 주문 ID와 일치해야 합니다.

    2. user_identifiers를 정규화되고 해싱된 사용자 데이터로 채웁니다. 사용 가능한 사용자 식별자가 여러 개인 경우 식별자마다 별도의 UserIdentifier(최대 5개의 식별자)을 만듭니다.

    3. adjustment_typeENHANCEMENT로 설정합니다.

    4. conversion_actiontypeWEBPAGEConversionAction의 리소스 이름으로 설정합니다. 기본 요건을 구현할 때 만든 ConversionAction의 리소스 이름이어야 합니다.

    5. (선택사항) 가장 정확한 측정을 위해 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(일광 절약 시간 무시)).

    6. (선택사항) user_agent를 원래 전환을 전송한 요청의 사용자 에이전트로 설정하여 전환 및 개선사항에 동일한 기기로 기인하거나 둘 다 교차 기기로 기인하도록 설정합니다.

  4. ConversionAdjustment 만들기 작업을 ConversionAdjustmentUploadService에 업로드하고 partial_failuretrue로 설정합니다.

  5. 업로드한 동영상을 검토합니다.

권장사항

웹용 향상된 전환을 구현할 때는 다음 권장사항에 유의하세요.

식별자가 있는 경우 여러 식별자를 포함합니다.

전환에 대한 gclid가 있는 경우 성능 향상을 위해 user_identifiers와 함께 전송하는 것이 좋습니다. 또한 변환에 UserIdentifier가 두 개 이상 있는 경우 여러 개를 포함하면 일치 가능성이 향상될 수 있습니다. 동일한 ConversionAdjustment에 모든 식별자를 포함합니다.

통합을 설정할 때 부분 실패 오류 검토

웹용 향상된 전환 통합을 처음 설정할 때 응답의 partial_failure_error 필드에서 부분 오류를 검토하고 해결하세요. 설정에 문제가 있는 경우 이 필드를 검사하여 문제를 조사하고 해결할 수 있습니다. 부분 실패 오류 처리에 관한 자세한 내용과 예는 전용 부분 실패 가이드를 참고하세요.

부분 실패 오류로 발생한 문제를 해결하고 통합이 완료되면 오프라인 데이터 진단으로 전환하여 전환 상태를 정기적으로 검토하세요.

단일 요청으로 여러 조정 일괄 조정

업로드할 조정이 여러 개인 경우 조정당 업로드 요청을 전송하는 대신 작업을 하나의 UploadConversionAdjustmentsRequest로 일괄 처리합니다.

요청당 조정 수의 한도는 할당량 가이드를 참조하세요. 오프라인 데이터 진단을 통해 요청 집합을 동일한 논리 작업에 그룹화하려면 모든 요청의 job_id를 동일한 값으로 설정합니다.

데이터 업로드 준비

개인 정보 보호를 위해 다음 데이터는 업로드하기 전에 SHA-256을 사용하여 해싱해야 합니다.

  • 이메일 주소
  • 전화번호
  • 이름
  • 상세 주소

다음 데이터는 해싱하지 않습니다.

  • 국가
  • 상태
  • 도시
  • 우편번호

해시 결과를 표준화하려면 이러한 값 중 하나를 해싱하기 전에 다음을 수행해야 합니다.

  • 선행 및 후행 공백을 삭제합니다.
  • 텍스트를 소문자로 변환합니다.
  • 전화번호는 E164 표준에 따른 형식으로 작성합니다.
  • gmail.comgooglemail.com 이메일 주소에서 도메인 이름 앞에 있는 모든 마침표 (.)를 삭제합니다.

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 preceding {@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 {
  String normalizedEmail = emailAddress.toLowerCase();
  String[] emailParts = normalizedEmail.split("@");
  if (emailParts.length > 1 && emailParts[1].matches("^(gmail|googlemail)\\.com\\s*")) {
    // 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("\\.", "");
    normalizedEmail = String.format("%s@%s", emailParts[0], emailParts[1]);
  }
  return normalizeAndHash(digest, normalizedEmail, 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();
}
      

2,399필리핀

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.lower()
    email_parts = normalized_email.split("@")
    # Checks whether the domain of the email address is either "gmail.com"
    # or "googlemail.com". If this regex does not match then this statement
    # will evaluate to None.
    is_gmail = re.match(r"^(gmail|googlemail)\.com$", email_parts[1])

    # Check that there are at least two segments and the second segment
    # matches the above regex expression validating the email domain name.
    if len(email_parts) > 1 and is_gmail:
        # Removes any '.' characters from the portion of the email address
        # before the domain if the domain is gmail.com or googlemail.com.
        email_parts[0] = email_parts[0].replace(".", "")
        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);
}
      

향상된 전환 업로드

다음 스니펫은 주문 ID와 이메일 주소, 전화번호, 실제 주소 식별자를 포함하며 필요에 따라 표준화 및 해싱을 적용하여 보정 조정을 구성하는 방법을 보여줍니다. 가능한 경우 조정에 gclid를 포함하는 것이 좋습니다.

ConversionAdjustment에 사용자 식별자 추가

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();

Map<String, String> rawRecord =
    ImmutableMap.<String, String>builder()
        // Email address that includes a period (.) before the Gmail domain.
        .put("email", "alex.2@example.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 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.
        .put("orderId", orderId)
        .put("conversionActionId", Long.toString(conversionActionId))
        .put("conversionDateTime", conversionDateTime)
        .put("currencyCode", "USD")
        .put("userAgent", userAgent)
        .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(normalizeAndHash(sha256Digest, rawRecord.get("phone"), true))
          .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 });
      

2,399필리핀

// 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::V16::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::V16::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::V16::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::V16::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::V16::Common::UserIdentifier->new({
          addressInfo =>
            Google::Ads::GoogleAds::V16::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;
      

전환 조정에 전환 세부정보 추가

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.get("conversionDateTime") != null) {
  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.get("userAgent") != null) {
  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;
}

      

2,399필리핀

// 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::V16::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::V16::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};
}
      

전환 조정 업로드

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,
        });
      

2,399필리핀

// 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"
  });
      

업로드 검토

오프라인 데이터 진단을 사용하여 최근 업로드의 전반적인 상태를 검토합니다. 업로드에 성공한다고 해서 반드시 업로드에 일치 항목이 있는 것은 아닙니다.

캠페인의 전환 측정항목을 보고할 때는 사용자 인터페이스 측정항목 매핑을 참조하여 Google Ads UI 측정항목을 Google Ads API 보고 필드와 연결하세요. 또한 conversion_action 리소스를 쿼리하여 특정 전환 액션의 총 전환수와 총 전환 가치를 볼 수 있습니다.