Google Ads API를 사용하여 Google Ads에서 온라인 클릭 전환을 관리할 수 있습니다. 온라인 클릭 전환은 웹사이트 등을 통해 온라인에서 판매로 이어진 광고를 추적하는 데 도움이 됩니다.
온라인 전환은 Google 태그를 사용하여 추적되지만 Google Ads API를 사용하여 조정하거나 향상시킬 수 있습니다.
이미 Google 태그를 설정했고 온라인 전환을 조정하려면 전환 조정 페이지를 참고하세요. Google 태그가 설정되어 있고 웹용 향상된 전환이라고도 하는 사용자 제공 데이터를 사용하여 온라인 전환을 개선하려면 계속 읽어보세요.
웹용 향상된 전환
다음 섹션에서는 웹용 향상된 전환이라고도 하는 온라인 클릭 전환을 향상시키는 방법을 설명합니다.
웹용 향상된 전환을 사용하려면 사용자가 전환할 때 클릭 식별자(예: GCLID)와 주문 ID를 Google Ads에 자동으로 전송하는 태그를 설정해야 합니다. Google 태그 관리자, Google 태그 또는 Google Ads API를 통해 향상된 전환을 설정할 수 있습니다. Google Ads API를 사용하면 전환 이벤트와 동시에가 아닌 24시간 이내에 퍼스트 파티 전환 데이터를 전송할 수 있습니다. 이를 통해 고객 데이터베이스나 CRM 시스템과 같은 다양한 소스에서 퍼스트 파티 데이터를 찾을 수 있습니다.
웹용 향상된 전환의 작동 방식
Google Ads API의 웹용 향상된 전환은 다음 다이어그램의 3단계를 보완합니다. 사용자가 전환할 때 해싱된 사용자 정보를 전송하는 대신 태그는 GCLID와 주문 ID만 전송하고, 광고주는 나중에 해싱된 데이터와 함께 주문 ID를 가져와 해싱된 사용자 정보를 전송합니다. 웹용 향상된 전환에 대해 자세히 알아보려면 향상된 전환 정보를 참고하세요.
기본 요건
웹용 향상된 전환을 사용하려면 먼저 고객 데이터 약관에 동의해야 합니다. Google Ads 전환 고객에게 다음 쿼리를 실행하여 고객 데이터 약관에 동의했는지 확인할 수 있습니다.
SELECT
customer.id,
customer.conversion_tracking_setting.accepted_customer_data_terms
FROM customer
accepted_customer_data_terms이 false인 경우 웹용 향상된 전환 설정의 안내에 따라 이 필수 요건을 완료하세요.
태그 구성
Google Ads API를 사용하여 웹용 향상된 전환 설정의 안내에 따라 웹사이트의 태그 지정을 구성할 수 있습니다. 전환 추적 태그에 거래 ID (주문 ID)를 추가하여 전환 일치를 지원하는 것이 좋습니다.
API 구현
다음은 Google Ads API에서 웹용 향상된 전환을 가져오는 전체 흐름입니다.
이메일 주소, 전화번호, 우편 주소와 같은 사용자 제공 데이터를 정규화하고 해싱합니다.
정규화되고 해싱된 사용자 제공 데이터로
ConversionAdjustment객체를 채웁니다.ConversionAdjustment객체를 가져와ConversionAdjustmentUploadService에 저장합니다.
사용자 제공 데이터 정규화 및 해싱
개인 정보 보호를 위해 다음 데이터는 가져오기 전에 SHA-256을 사용하여 해싱해야 합니다.
- 이메일 주소
- 전화번호
- 이름
- 성
- 상세 주소
다음 데이터는 해싱하지 마세요.
- 국가
- 주
- 도시
- 우편번호
해싱 결과를 표준화하려면 이러한 값을 해싱하기 전에 다음을 실행하세요.
- 선행 및 후행 공백을 삭제합니다.
- 텍스트를 소문자로 변환합니다.
- E164 표준에 따른 형식으로 전화번호를 작성합니다.
향상된 전환을 위한 이메일 주소 형식
향상된 전환의 일치율을 최대한 높이려면 다음 단계에 따라 이메일 주소를 올바르게 형식화하고 해싱해야 합니다.
표준 정규화 적용 (모든 이메일 주소):
- 선행 및 후행 ASCII 공백을 삭제합니다.
- 이메일 주소의 모든 문자를 소문자로 변환합니다.
도메인별 정규화 적용 (
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로 정규화됩니다.
- 예:
- 이메일 주소를 소문자로 변환한 후 도메인 (
정규화된 이메일 주소 해싱:
- 정규화된 이메일 문자열을 UTF-8 바이트로 인코딩합니다.
- SHA-256 알고리즘을 사용하여 UTF-8 바이트를 해싱합니다.
- 결과 다이제스트를 소문자 16진수 문자열 (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()
코드 예
이 예시에서는 사용자 제공 데이터를 정규화하고 해싱하는 방법을 보여줍니다.
자바
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 객체 채우기
다음 섹션에서는 주문 ID와 이메일 주소, 전화번호, 실제 주소의 식별자가 포함된 보강 조정 기능을 구성하고 필요에 따라 표준화 및 해싱을 적용하는 방법을 보여줍니다. 가능한 경우 조정에 gclid를 포함하는 것이 좋습니다.
이 단계는 전환 조정 가져오기 가이드와 거의 동일하지만 몇 가지 주의사항이 있습니다.
user_identifiers를 정규화되고 해싱된 사용자 데이터로 채웁니다. 사용 가능한 사용자 식별자가 여러 개 있는 경우 각각에 대해 별도의UserIdentifier를 만듭니다(최대 5개 식별자).
자바
// 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::V26::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::V26::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::V26::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::V26::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::V26::Common::UserIdentifier->new({ addressInfo => Google::Ads::GoogleAds::V26::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(WEBPAGE)을 선택사항인gclid_date_time_pair(yyyy-mm-dd HH:mm:ss+|-HH:mm형식, 예:2022-01-01 19:32:45-05:00(일광 절약 시간 무시)) 및user_agent와 함께 설정합니다.
자바
// 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::V26::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::V26::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
Request 및 ConversionAdjustment 스키마 요약
다음 표에는 ConversionAdjustmentUploadService.UploadConversionAdjustments를 사용하여 웹용 향상된 전환을 업로드할 때 UploadConversionAdjustmentsRequest의 최상위 필드와 ConversionAdjustment(conversion_adjustments[])의 항목별 필드가 요약되어 있습니다.
| 요청 필드 | 요구사항 | 형식 및 제약 조건 |
|---|---|---|
customer_id |
필수 | 하이픈 없이 Google Ads 광고 전환 고객 ID (customer.conversion_tracking_setting의 google_ads_conversion_customer)로 설정합니다. |
conversion_adjustments[] |
필수 | 요청당 최대 2,000개의 ConversionAdjustment 항목 (TOO_MANY_ADJUSTMENTS_IN_REQUEST) |
partial_failure |
필수 | 유효한 조정이 처리되고 행 수준 오류가 partial_failure_error에 반환되도록 true로 설정해야 합니다. |
validate_only |
선택사항 | true인 경우 조정을 실행하지 않고 요청의 유효성을 검사합니다. |
job_id |
선택사항 | job_summaries에서 단일 논리 작업 아래에 여러 요청을 그룹화하는 양수 int32 (1~2^31 - 1, 0은 설정되지 않은 센티널이며 지정된 경우 INVALID_JOB_ID 반환) |
conversion_adjustments[]의 각 ConversionAdjustment (요청당 최대 2,000개 조정)는 다음 필드를 지원합니다.
ConversionAdjustment 필드 |
요구사항 | 형식 및 제약 조건 |
|---|---|---|
adjustment_type |
필수 | ENHANCEMENT (ConversionAdjustmentType)로 설정해야 합니다. |
conversion_action |
필수 | 업로드 (TOO_RECENT_CONVERSION_ACTION) 최소 6시간 전에 생성된 type = WEBPAGE가 있는 ENABLED 전환 액션의 리소스 이름 (customers/{conversion_customer_id}/conversionActions/{action_id})입니다. |
order_id |
필수 | 보정할 전환의 거래 ID입니다. 웹사이트 전환 태그에서 전송한 주문 ID와 일치해야 합니다. |
user_identifiers[] |
필수 | 개선사항당 UserIdentifier 객체 1~5개 선택사항인 user_identifier_source (예:FIRST_PARTY) 및 항목당 식별자 1개를 설정합니다.• hashed_email: SHA-256 16진수 문자열• hashed_phone_number: E.164 SHA-256 16진수 문자열• address_info: SHA-256으로 해싱된 hashed_first_name, hashed_last_name, hashed_street_address 및 해싱되지 않은 city, state, country_code, postal_code이 포함된 OfflineUserAddressInfo |
gclid_date_time_pair |
선택사항 (권장) | GclidDateTimePair: 원래 전환의 gclid 및 conversion_date_time(
yyyy-mm-dd HH:mm:ss+|-HH:mm, 예: 2022-01-01 19:32:45-05:00 (일광 절약 시간 무시)
, 예: 2026-06-01 14:30:00-04:00)를 포함합니다. 54일이 지난 전환은 조정할 수 없습니다 (CONVERSION_EXPIRED). |
adjustment_date_time |
선택사항 | 조정이 발생한 타임스탬프(예: yyyy-mm-dd HH:mm:ss+|-HH:mm, 2022-01-01 19:32:45-05:00 (일광 절약 시간 무시))입니다. 설정된 경우 conversion_date_time 이후여야 합니다. |
user_agent |
선택사항 | 전환과 개선이 동일한 기기 기여 분석 (same-device 또는 cross-device)을 공유하도록 원래 전환 요청의 HTTP User-Agent 헤더 문자열입니다. |
restatement_value |
ENHANCEMENT에 설정하지 않음 |
adjustment_type이 ENHANCEMENT인 경우 무시됩니다. 전환 가치를 변경하려면 adjustment_type = RESTATEMENT를 사용하여 별도의 작업을 전송하세요. |
전환 개선 기능 가져오기
자바
// 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
REST 요청 및 응답 구조
REST를 통해 ConversionAdjustmentUploadService.UploadConversionAdjustments를 호출할 때 partialFailure를 true로 설정하여 POST 요청을 보냅니다.
POST https://googleads.googleapis.com/v26/customers/{customer_id}:uploadConversionAdjustments
{
"partialFailure": true,
"jobId": 2001,
"conversionAdjustments": [
{
"conversionAction": "customers/{customer_id}/conversionActions/{conversion_action_id}",
"adjustmentType": "ENHANCEMENT",
"orderId": "ORDER-2026-1001",
"gclidDateTimePair": {
"gclid": "EAIaIQobChMI...",
"conversionDateTime": "2026-06-01 14:30:00-04:00"
},
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
"userIdentifiers": [
{
"userIdentifierSource": "FIRST_PARTY",
"hashedEmail": "f660ab912ec121d1b1e928a0bb4bc61b15f5ad44d5efdc4e1c92a25e99b8e44a"
},
{
"userIdentifierSource": "FIRST_PARTY",
"hashedPhoneNumber": "42975f8f9e46c2e13c1a2294957e5cb8d8e6f4f7341a036c6e595bc9f5e24621"
},
{
"userIdentifierSource": "FIRST_PARTY",
"addressInfo": {
"hashedFirstName": "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3",
"hashedLastName": "b3a8e0e1f9ab1bfe3a36f231f676f78bb30a519d2b21e6c530c0eee8ebb4a5d0",
"hashedStreetAddress": "35a9e381b1a27567549b5f8a6f783c167ebf809f1c4d6a9e367240484d8ce281",
"city": "New York",
"state": "NY",
"postalCode": "10011",
"countryCode": "US"
}
}
]
},
{
"conversionAction": "customers/{customer_id}/conversionActions/{conversion_action_id}",
"adjustmentType": "ENHANCEMENT",
"userIdentifiers": [
{
"userIdentifierSource": "FIRST_PARTY",
"hashedEmail": "f660ab912ec121d1b1e928a0bb4bc61b15f5ad44d5efdc4e1c92a25e99b8e44a"
}
]
}
]
}
두 번의 조정이 포함된 이 일괄 처리에서 두 번째 조정은 orderId를 생략합니다. 성공한 행은 results[]에서 채워진 ConversionAdjustmentResult 항목을 반환하고 실패한 행은 UploadConversionAdjustmentsResponse의 partialFailureError에 행 수준 오류 세부정보와 함께 results[]에서 빈 {} 객체를 반환합니다.
{
"results": [
{
"gclidDateTimePair": {
"gclid": "EAIaIQobChMI...",
"conversionDateTime": "2026-06-01 14:30:00-04:00"
},
"orderId": "ORDER-2026-1001",
"conversionAction": "customers/{customer_id}/conversionActions/{conversion_action_id}",
"adjustmentType": "ENHANCEMENT"
},
{}
],
"jobId": "2001",
"partialFailureError": {
"code": 3,
"message": "The required field was not present., at conversion_adjustments[1].order_id",
"details": [
{
"@type": "type.googleapis.com/google.ads.googleads.v26.errors.GoogleAdsFailure",
"errors": [
{
"errorCode": {
"fieldError": "REQUIRED"
},
"message": "The required field was not present.",
"location": {
"fieldPathElements": [
{
"fieldName": "conversion_adjustments",
"index": 1
},
{
"fieldName": "order_id"
}
]
}
}
]
}
]
}
}
가져오기 검토
Enhanced Conversions API 진단 보고서를 사용하여 웹용 향상된 전환이 효과적으로 작동하는지 확인하고 설정의 문제를 진단하세요.
오프라인 데이터 진단을 사용하여 최근 가져오기의 전반적인 상태를 검토합니다.
GAQL로 업로드 진단 쿼리
전환 고객 ID에 대해 offline_conversion_upload_client_summary를 쿼리하여 계정 수준 업로드 상태, 최근 작업 수, 오류 알림을 프로그래매틱 방식으로 검사할 수도 있습니다.
SELECT
customer.id,
offline_conversion_upload_client_summary.client,
offline_conversion_upload_client_summary.status,
offline_conversion_upload_client_summary.total_event_count,
offline_conversion_upload_client_summary.successful_event_count,
offline_conversion_upload_client_summary.success_rate,
offline_conversion_upload_client_summary.pending_event_count,
offline_conversion_upload_client_summary.pending_rate,
offline_conversion_upload_client_summary.last_upload_date_time,
offline_conversion_upload_client_summary.alerts,
offline_conversion_upload_client_summary.daily_summaries,
offline_conversion_upload_client_summary.job_summaries
FROM offline_conversion_upload_client_summary
캠페인의 전환 측정항목을 보고할 때는 사용자 인터페이스 측정항목 매핑을 참고하여 Google Ads UI 측정항목을 Google Ads API 보고 필드와 연결하세요. conversion_action 리소스를 쿼리하여 특정 전환 액션의 총 전환수와 총 전환 가치를 확인할 수도 있습니다.
권장사항
웹용 향상된 전환을 구현할 때는 다음 권장사항을 염두에 두세요.
고객 식별자 확인
전환을 가져올 때 고객 ID는 전환 액션이 포함되어 있고 전환 추적을 수행하는 계정으로 설정해야 합니다. 이 설정을 확인하려면 게재 Google Ads Customer 리소스에서 ConversionTrackingSetting을 쿼리하세요. GoogleAdsService.SearchStream를 사용하여 다음 쿼리를 실행하여 customer.conversion_tracking_setting.google_ads_conversion_customer 값을 찾습니다.
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 필드에서 부분 오류를 검토하고 해결하세요. 설정에 문제가 있는 경우 이 필드를 검사하여 문제를 조사하고 해결할 수 있습니다. 부분 실패 오류 처리와 관련된 자세한 내용과 예시는 전용 부분 실패 가이드를 참고하세요.
부분 실패 오류로 인해 발생한 문제를 해결하고 통합이 완료되면 오프라인 데이터 진단을 사용하여 전환 상태를 정기적으로 검토하세요.
단일 요청에서 여러 조정 일괄 처리
가져올 조정이 여러 개인 경우 조정당 가져오기 요청을 보내는 대신 작업을 하나의 UploadConversionAdjustmentsRequest로 일괄 처리 (요청당 최대 2,000개의 전환 조정)합니다.
요청당 조정 수 한도는 할당량 가이드를 확인하세요. 오프라인 데이터 진단에서 동일한 논리적 작업에 따라 요청 집합을 그룹화하려면 모든 요청의 job_id를 동일한 값으로 설정합니다.
문제 해결
Enhanced Conversions API 진단 보고서를 사용하여 웹용 향상된 전환이 효과적으로 작동하는지 확인하고 설정의 문제를 진단합니다.
일반적인 오류 디버그
다음 안내는 웹용 향상된 전환의 일반적인 오류를 진단하고 해결하는 데 도움이 됩니다.
| 오류 | |
|---|---|
CONVERSION_NOT_FOUND
|
제공된 전환 액션 및 전환 식별자 쌍의 전환을 찾을 수 없습니다.
order_id 전환이 요청의 customer_id에 지정된
Google Ads 전환 고객의 Google Ads 전환인지 확인합니다.또한 gclid 대신 gbraid
또는 wbraid가 채워진 클릭에 대한 전환이 아닌지 확인합니다.
이러한 전환에는 Google Ads의 웹용 향상된 전환이 지원되지 않습니다.
|
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 Ads UI에서 요청에 참조된 전환 액션의 향상된 전환 사용 설정 체크박스를 선택해야 합니다. 고객센터에서 이와 관련된 안내를 확인하세요.
|