您可以使用 Google Ads API,在 Google Ads 中管理線上點擊轉換。線上點擊轉換可協助您追蹤在線上世界 (例如透過網站) 促成銷售的廣告。
線上轉換是使用 Google 代碼追蹤,但您可以使用 Google Ads API 調整或強化轉換。
如果您已設定 Google 代碼,且想調整線上轉換,請參閱轉換調整項頁面。如果您已設定 Google 代碼,並想使用使用者提供的資料來強化線上轉換 (也就是網站強化轉換),請繼續閱讀。
網站強化轉換
以下各節將說明如何強化線上點擊轉換,也就是網站強化轉換。
如要使用網站強化轉換,您必須設定代碼,在使用者完成轉換時,自動將點擊 ID (例如 Google 點擊 ID) 和訂單 ID 傳送至 Google Ads。您可以選擇透過 Google 代碼管理工具、Google 代碼或 Google Ads API 設定強化轉換。使用 Google Ads API 的好處是,您可以在轉換事件發生後 24 小時內傳送第一方轉換資料,而不必在轉換事件發生時立即傳送。這樣一來,您就能從多種來源 (例如顧客資料庫或客戶關係管理系統) 找出第一方資料。
網站強化轉換的運作方式
Google Ads API 中的網站強化轉換功能,可補充下圖中的步驟 3。代碼不會在使用者完成轉換時傳送經雜湊處理的使用者資訊,而是只傳送 Google 點擊 ID 和訂單 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 設定網站強化轉換」一文的操作說明,為網站設定代碼。使用 Google Ads API 進行網站強化轉換時,您也必須在網站轉換代碼中加入交易 ID (訂單 ID)。Google Ads 會使用交易 ID,將每個 API 強化資料的 order_id 欄位與相應的轉換事件進行比對。
API 導入
以下是在 Google Ads API 中匯入網站強化轉換的整體流程。
將使用者提供的資料正規化並進行雜湊處理,例如電子郵件地址、電話號碼和郵寄地址。
使用經過正規化和雜湊處理的使用者提供資料,填入
ConversionAdjustment物件。將
ConversionAdjustment物件匯入ConversionAdjustmentUploadService。
正規化及雜湊處理使用者提供的資料
基於隱私權考量,請務必使用 SHA-256 雜湊處理,才能匯入下列資料:
- 電子郵件地址
- 電話號碼
- 名字
- 姓氏
- 街道地址
請勿雜湊處理下列資料:
- 國家/地區 (
country_code,格式為兩個英文字母的 ISO 3166-1 alpha-2 代碼) - 州/省 (
state) - 城市 (
city) - 郵遞區號 (
postal_code)
為了使雜湊處理結果標準化,在對其中任何值進行雜湊處理前,請先完成下列事項:
- 移除開頭和結尾的空白字元。
- 將文字轉換為小寫。
- 根據 E.164 標準設定電話號碼格式。
強化轉換的電子郵件地址格式
為確保強化轉換的媒合率達到最高,請務必按照下列步驟,正確設定電子郵件地址的格式並進行雜湊處理:
套用標準正規化 (所有電子郵件地址):
- 移除開頭和結尾的 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 位元組進行雜湊處理。
- 將產生的摘要格式設為小寫十六進位字串 (64 個字元)。
import hashlib
import re
def normalize_and_hash_email(email: str) -> str:
"""Normalizes and SHA-256 hashes an email address for enhanced conversions."""
normalized = email.strip().lower()
parts = normalized.split("@")
if len(parts) != 2:
raise ValueError("Invalid email address format.")
username, domain = parts
if domain in ("gmail.com", "googlemail.com"):
username = username.replace(".", "")
username = re.sub(r"\+.*$", "", username)
normalized = f"{username}@{domain}"
return hashlib.sha256(normalized.encode("utf-8")).hexdigest()
程式碼範例
這個範例說明如何正規化及雜湊處理使用者提供的資料。
Java
private String normalizeAndHash(MessageDigest digest, String s, boolean trimIntermediateSpaces) throws UnsupportedEncodingException { // Normalizes by first converting all characters to lowercase, then trimming spaces. String normalized = s.toLowerCase(); if (trimIntermediateSpaces) { // Removes leading, trailing, and intermediate spaces. normalized = normalized.replaceAll("\\s+", ""); } else { // Removes only leading and trailing spaces. normalized = normalized.trim(); } // Hashes the normalized string using the hashing algorithm. byte[] hash = digest.digest(normalized.getBytes("UTF-8")); StringBuilder result = new StringBuilder(); for (byte b : hash) { result.append(String.format("%02x", b)); } return result.toString(); } /** * Returns the result of normalizing and hashing an email address. For this use case, Google Ads * requires removal of any '.' characters or trailing '+' and characters that follow it from the * username portion of the email address if the domain is {@code gmail.com} or {@code * googlemail.com}. * * @param digest the digest to use to hash the normalized string. * @param emailAddress the email address to normalize and hash. */ private String normalizeAndHashEmailAddress(MessageDigest digest, String emailAddress) throws UnsupportedEncodingException { // Removes all whitespace (leading, trailing, and intermediate) from the email address. String normalizedEmail = emailAddress.toLowerCase().replaceAll("\\s+", ""); String[] emailParts = normalizedEmail.split("@", 2); if (emailParts.length == 2 && emailParts[1].matches("^(gmail|googlemail)\\.com$")) { // Removes any '.' characters from the portion of the email address before the domain if the // domain is gmail.com or googlemail.com. emailParts[0] = emailParts[0].replaceAll("\\.", ""); // Removes any '+' and all characters that follow it from the portion of the email address // before the domain if the domain is gmail.com or googlemail.com. emailParts[0] = emailParts[0].replaceAll("\\+.*", ""); normalizedEmail = String.format("%s@%s", emailParts[0], emailParts[1]); } return normalizeAndHash(digest, normalizedEmail, true); } /** * Returns the result of normalizing and hashing a phone number. For this use case, Google Ads * requires phone numbers to be in E.164 format. * * @param digest the digest to use to hash the normalized string. * @param phoneNumber the phone number to normalize and hash. */ private String normalizeAndHashPhoneNumber(MessageDigest digest, String phoneNumber) throws UnsupportedEncodingException { // Removes non-digit characters and prepends a leading '+' sign. String digitsOnly = phoneNumber.replaceAll("[^0-9]", ""); String formattedPhone = "+" + digitsOnly; if (!formattedPhone.matches("^\\+[1-9]\\d{6,14}$")) { throw new IllegalArgumentException("Phone number must be in E.164 format: " + phoneNumber); } return normalizeAndHash(digest, formattedPhone, true); }
C#
/// <summary> /// Normalizes the email address and hashes it. For this use case, Google Ads requires /// removal of any '.' characters preceding <code>gmail.com</code> or /// <code>googlemail.com</code>. /// </summary> /// <param name="emailAddress">The email address.</param> /// <returns>The hash code.</returns> private string NormalizeAndHashEmailAddress(string emailAddress) { string normalizedEmail = emailAddress.ToLower(); string[] emailParts = normalizedEmail.Split('@'); if (emailParts.Length > 1 && (emailParts[1] == "gmail.com" || emailParts[1] == "googlemail.com")) { // Removes any '.' characters from the portion of the email address before // the domain if the domain is gmail.com or googlemail.com. emailParts[0] = emailParts[0].Replace(".", ""); normalizedEmail = $"{emailParts[0]}@{emailParts[1]}"; } return NormalizeAndHash(normalizedEmail); } /// <summary> /// Normalizes and hashes a string value. /// </summary> /// <param name="value">The value to normalize and hash.</param> /// <returns>The normalized and hashed value.</returns> private static string NormalizeAndHash(string value) { return ToSha256String(digest, ToNormalizedValue(value)); } /// <summary> /// Hash a string value using SHA-256 hashing algorithm. /// </summary> /// <param name="digest">Provides the algorithm for SHA-256.</param> /// <param name="value">The string value (e.g. an email address) to hash.</param> /// <returns>The hashed value.</returns> private static string ToSha256String(SHA256 digest, string value) { byte[] digestBytes = digest.ComputeHash(Encoding.UTF8.GetBytes(value)); // Convert the byte array into an unhyphenated hexadecimal string. return BitConverter.ToString(digestBytes).Replace("-", string.Empty); } /// <summary> /// Removes leading and trailing whitespace and converts all characters to /// lower case. /// </summary> /// <param name="value">The value to normalize.</param> /// <returns>The normalized value.</returns> private static string ToNormalizedValue(string value) { return value.Trim().ToLower(); }
PHP
private static function normalizeAndHash( string $hashAlgorithm, string $value, bool $trimIntermediateSpaces ): string { // Normalizes by first converting all characters to lowercase, then trimming spaces. $normalized = strtolower($value); if ($trimIntermediateSpaces === true) { // Removes leading, trailing, and intermediate spaces. $normalized = str_replace(' ', '', $normalized); } else { // Removes only leading and trailing spaces. $normalized = trim($normalized); } return hash($hashAlgorithm, strtolower(trim($normalized))); } /** * Returns the result of normalizing and hashing an email address. For this use case, Google * Ads requires removal of any '.' characters preceding "gmail.com" or "googlemail.com". * * @param string $hashAlgorithm the hash algorithm to use * @param string $emailAddress the email address to normalize and hash * @return string the normalized and hashed email address */ private static function normalizeAndHashEmailAddress( string $hashAlgorithm, string $emailAddress ): string { $normalizedEmail = strtolower($emailAddress); $emailParts = explode("@", $normalizedEmail); if ( count($emailParts) > 1 && preg_match('/^(gmail|googlemail)\.com\s*/', $emailParts[1]) ) { // Removes any '.' characters from the portion of the email address before the domain // if the domain is gmail.com or googlemail.com. $emailParts[0] = str_replace(".", "", $emailParts[0]); $normalizedEmail = sprintf('%s@%s', $emailParts[0], $emailParts[1]); } return self::normalizeAndHash($hashAlgorithm, $normalizedEmail, true); }
Python
def normalize_and_hash_email_address(email_address): """Returns the result of normalizing and hashing an email address. For this use case, Google Ads requires removal of any '.' characters preceding "gmail.com" or "googlemail.com" Args: email_address: An email address to normalize. Returns: A normalized (lowercase, removed whitespace) and SHA-265 hashed string. """ normalized_email = email_address.strip().lower() email_parts = normalized_email.split("@") # Check that there are at least two segments if len(email_parts) > 1: # Removes any '.' and '+' characters from the portion of the email address # before the domain chars_to_remove = ".+" translation_table = str.maketrans("", "", chars_to_remove) email_parts[0] = email_parts[0].translate(translation_table) normalized_email = "@".join(email_parts) return normalize_and_hash(normalized_email) def normalize_and_hash(s): """Normalizes and hashes a string with SHA-256. Private customer data must be hashed during upload, as described at: https://support.google.com/google-ads/answer/9888656 Args: s: The string to perform this operation on. Returns: A normalized (lowercase, removed whitespace) and SHA-256 hashed string. """ return hashlib.sha256(s.strip().lower().encode()).hexdigest()
小茹
# 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 的強化調整項,以及電子郵件地址、電話號碼和實際地址的 ID,並視需要套用標準化和雜湊處理。最佳做法是在調整項中加入 gclid (如有)。
這個步驟與匯入轉換調整項指南幾乎相同,但有幾點需要注意:
- 在
user_identifiers欄位中填入正規化及經過雜湊處理的使用者資料。如果您有多個使用者 ID,請為每個 ID 分別建立UserIdentifier物件,最多可建立五個 ID。如要在address_info欄位中加入實際地址,請提供所有四個必填欄位 (hashed_first_name、hashed_last_name、country_code和postal_code)。如果這四個欄位中有任何一個無法提供 (例如只有使用者的姓氏和名字),請省略address_info欄位的UserIdentifier物件。
Java
// Creates a builder for constructing the enhancement adjustment. ConversionAdjustment.Builder enhancementBuilder = ConversionAdjustment.newBuilder().setAdjustmentType(ConversionAdjustmentType.ENHANCEMENT); // Extracts user email, phone, and address info from the raw data, normalizes and hashes it, // then wraps it in UserIdentifier objects. // Creates a separate UserIdentifier object for each. The data in this example is hardcoded, but // in your application you might read the raw data from an input file. // IMPORTANT: Since the identifier attribute of UserIdentifier // (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a // oneof // (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE of // hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting more // than one of these attributes on the same UserIdentifier will clear all the other members // of the oneof. For example, the following code is INCORRECT and will result in a // UserIdentifier with ONLY a hashedPhoneNumber. // // UserIdentifier incorrectlyPopulatedUserIdentifier = // UserIdentifier.newBuilder() // .setHashedEmail("...") // .setHashedPhoneNumber("...") // .build(); ImmutableMap.Builder<String, String> rawRecordBuilder = ImmutableMap.<String, String>builder() // Email address that includes a period (.) and plus (+) suffix before the Gmail domain. .put("email", "alex.2+myalias@gmail.com") // Address that includes all four required elements: first name, last name, country // code, and postal code. .put("firstName", "Alex") .put("lastName", "Quinn") .put("countryCode", "US") .put("postalCode", "94045") // Phone number to be converted to E.164 format, with a leading '+' as required. .put("phone", "+1 800-555-0102") // This example lets you put conversion details as arguments, but in reality you might // store this data alongside other user data, so we include it in this sample user // record. .put("orderId", orderId) .put("conversionActionId", Long.toString(conversionActionId)) .put("currencyCode", "USD"); // Adds entries for the optional fields. if (conversionDateTime != null) { rawRecordBuilder.put("conversionDateTime", conversionDateTime); } if (userAgent != null) { rawRecordBuilder.put("userAgent", userAgent); } // Builds the map representing the record. Map<String, String> rawRecord = rawRecordBuilder.build(); // Creates a SHA256 message digest for hashing user identifiers in a privacy-safe way, as // described at https://support.google.com/google-ads/answer/9888656. MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256"); // Creates a list for the user identifiers. List<UserIdentifier> userIdentifiers = new ArrayList<>(); // Creates a user identifier using the hashed email address, using the normalize and hash method // specifically for email addresses. UserIdentifier emailIdentifier = UserIdentifier.newBuilder() // Optional: specify the user identifier source. .setUserIdentifierSource(UserIdentifierSource.FIRST_PARTY) // Uses the normalize and hash method specifically for email addresses. .setHashedEmail(normalizeAndHashEmailAddress(sha256Digest, rawRecord.get("email"))) .build(); userIdentifiers.add(emailIdentifier); // Checks if the record has a phone number, and if so, adds a UserIdentifier for it. if (rawRecord.containsKey("phone")) { UserIdentifier hashedPhoneNumberIdentifier = UserIdentifier.newBuilder() .setHashedPhoneNumber( normalizeAndHashPhoneNumber(sha256Digest, rawRecord.get("phone"))) .build(); // Adds the hashed phone number identifier to the UserData object's list. userIdentifiers.add(hashedPhoneNumberIdentifier); } // Checks if the record has all the required mailing address elements, and if so, adds a // UserIdentifier for the mailing address. if (rawRecord.containsKey("firstName")) { // Checks if the record contains all the other required elements of a mailing address. Set<String> missingAddressKeys = new HashSet<>(); for (String addressKey : new String[] {"lastName", "countryCode", "postalCode"}) { if (!rawRecord.containsKey(addressKey)) { missingAddressKeys.add(addressKey); } } if (!missingAddressKeys.isEmpty()) { System.out.printf( "Skipping addition of mailing address information because the following required keys" + " are missing: %s%n", missingAddressKeys); } else { // Creates an OfflineUserAddressInfo object that contains all the required elements of a // mailing address. OfflineUserAddressInfo addressInfo = OfflineUserAddressInfo.newBuilder() .setHashedFirstName( normalizeAndHash(sha256Digest, rawRecord.get("firstName"), false)) .setHashedLastName(normalizeAndHash(sha256Digest, rawRecord.get("lastName"), false)) .setCountryCode(rawRecord.get("countryCode")) .setPostalCode(rawRecord.get("postalCode")) .build(); UserIdentifier addressIdentifier = UserIdentifier.newBuilder().setAddressInfo(addressInfo).build(); // Adds the address identifier to the UserData object's list. userIdentifiers.add(addressIdentifier); } } // Adds the user identifiers to the enhancement adjustment. enhancementBuilder.addAllUserIdentifiers(userIdentifiers);
C#
// Normalize and hash the raw data, then wrap it in UserIdentifier objects. // Create a separate UserIdentifier object for each. The data in this example is // hardcoded, but in your application you might read the raw data from an input file. // // IMPORTANT: Since the identifier attribute of UserIdentifier // (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) // is a oneof // (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set // only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id, // or address-info. Setting more than one of these attributes on the same UserIdentifier // will clear all the other members of the oneof. For example, the following code is // INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number: // UserIdentifier incorrectlyPopulatedUserIdentifier = new UserIdentifier() // { // HashedEmail = "..." // HashedPhoneNumber = "..." // } UserIdentifier addressIdentifier = new UserIdentifier() { AddressInfo = new OfflineUserAddressInfo() { HashedFirstName = NormalizeAndHash("Dana"), HashedLastName = NormalizeAndHash("Quinn"), HashedStreetAddress = NormalizeAndHash("1600 Amphitheatre Pkwy"), City = "Mountain View", State = "CA", PostalCode = "94043", CountryCode = "US" }, // Optional: Specifies the user identifier source. UserIdentifierSource = UserIdentifierSource.FirstParty }; // Creates a user identifier using the hashed email address. UserIdentifier emailIdentifier = new UserIdentifier() { UserIdentifierSource = UserIdentifierSource.FirstParty, // Uses the normalize and hash method specifically for email addresses. HashedEmail = NormalizeAndHashEmailAddress("dana@example.com") }; // Adds the user identifiers to the enhancement adjustment. enhancement.UserIdentifiers.AddRange(new[] { addressIdentifier, emailIdentifier });
PHP
// Creates the conversion enhancement. $enhancement = new ConversionAdjustment(['adjustment_type' => ConversionAdjustmentType::ENHANCEMENT]); // Extracts user email, phone, and address info from the raw data, normalizes and hashes it, // then wraps it in UserIdentifier objects. // Creates a separate UserIdentifier object for each. The data in this example is hardcoded, // but in your application you might read the raw data from an input file. // IMPORTANT: Since the identifier attribute of UserIdentifier // (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a // oneof // (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE // of hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting // more than one of these attributes on the same UserIdentifier will clear all the other // members of the oneof. For example, the following code is INCORRECT and will result in a // UserIdentifier with ONLY a hashedPhoneNumber. // // $incorrectlyPopulatedUserIdentifier = new UserIdentifier([ // 'hashed_email' => '...', // 'hashed_phone_number' => '...' // ]); $rawRecord = [ // Email address that includes a period (.) before the Gmail domain. 'email' => 'alex.2@example.com', // Address that includes all four required elements: first name, last name, country // code, and postal code. 'firstName' => 'Alex', 'lastName' => 'Quinn', 'countryCode' => 'US', 'postalCode' => '94045', // Phone number to be converted to E.164 format, with a leading '+' as required. 'phone' => '+1 800 5550102', // This example lets you input conversion details as arguments, but in reality you might // store this data alongside other user data, so we include it in this sample user // record. 'orderId' => $orderId, 'conversionActionId' => $conversionActionId, 'conversionDateTime' => $conversionDateTime, 'currencyCode' => 'USD' ]; // Creates a list for the user identifiers. $userIdentifiers = []; // Uses the SHA-256 hash algorithm for hashing user identifiers in a privacy-safe way, as // described at https://support.google.com/google-ads/answer/9888656. $hashAlgorithm = "sha256"; // Creates a user identifier using the hashed email address, using the normalize and hash // method specifically for email addresses. $emailIdentifier = new UserIdentifier( [ // Uses the normalize and hash method specifically for email addresses. 'hashed_email' => self::normalizeAndHashEmailAddress( $hashAlgorithm, $rawRecord['email'] ), // Optional: Specifies the user identifier source. 'user_identifier_source' => UserIdentifierSource::FIRST_PARTY ] ); $userIdentifiers[] = $emailIdentifier; // Checks if the record has a phone number, and if so, adds a UserIdentifier for it. if (array_key_exists('phone', $rawRecord)) { $hashedPhoneNumberIdentifier = new UserIdentifier( [ 'hashed_phone_number' => self::normalizeAndHash( $hashAlgorithm, $rawRecord['phone'], true ) ] ); // Adds the hashed email identifier to the user identifiers list. $userIdentifiers[] = $hashedPhoneNumberIdentifier; } // Checks if the record has all the required mailing address elements, and if so, adds a // UserIdentifier for the mailing address. if (array_key_exists('firstName', $rawRecord)) { // Checks if the record contains all the other required elements of a mailing // address. $missingAddressKeys = []; foreach (['lastName', 'countryCode', 'postalCode'] as $addressKey) { if (!array_key_exists($addressKey, $rawRecord)) { $missingAddressKeys[] = $addressKey; } } if (!empty($missingAddressKeys)) { printf( "Skipping addition of mailing address information because the " . "following required keys are missing: %s%s", json_encode($missingAddressKeys), PHP_EOL ); } else { // Creates an OfflineUserAddressInfo object that contains all the required // elements of a mailing address. $addressIdentifier = new UserIdentifier( [ 'address_info' => new OfflineUserAddressInfo( [ 'hashed_first_name' => self::normalizeAndHash( $hashAlgorithm, $rawRecord['firstName'], false ), 'hashed_last_name' => self::normalizeAndHash( $hashAlgorithm, $rawRecord['lastName'], false ), 'country_code' => $rawRecord['countryCode'], 'postal_code' => $rawRecord['postalCode'] ] ) ] ); // Adds the address identifier to the user identifiers list. $userIdentifiers[] = $addressIdentifier; } } // Adds the user identifiers to the conversion. $enhancement->setUserIdentifiers($userIdentifiers);
Python
# Extracts user email, phone, and address info from the raw data, normalizes # and hashes it, then wraps it in UserIdentifier objects. Creates a separate # UserIdentifier object for each. The data in this example is hardcoded, but # in your application you might read the raw data from an input file. # IMPORTANT: Since the identifier attribute of UserIdentifier # (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) # is a oneof # (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must # set only ONE of hashed_email, hashed_phone_number, mobile_id, # third_party_user_id, or address_info. Setting more than one of these # attributes on the same UserIdentifier will clear all the other members of # the oneof. For example, the following code is INCORRECT and will result in # a UserIdentifier with ONLY a hashed_phone_number: # # incorrectly_populated_user_identifier = client.get_type("UserIdentifier") # incorrectly_populated_user_identifier.hashed_email = "..."" # incorrectly_populated_user_identifier.hashed_phone_number = "..."" raw_record = { # Email address that includes a period (.) before the Gmail domain. "email": "alex.2@example.com", # Address that includes all four required elements: first name, last # name, country code, and postal code. "first_name": "Alex", "last_name": "Quinn", "country_code": "US", "postal_code": "94045", # Phone number to be converted to E.164 format, with a leading '+' as # required. "phone": "+1 800 5550102", # This example lets you input conversion details as arguments, but in # reality you might store this data alongside other user data, so we # include it in this sample user record. "order_id": order_id, "conversion_action_id": conversion_action_id, "conversion_date_time": conversion_date_time, "currency_code": "USD", "user_agent": user_agent, } # Constructs the enhancement adjustment. conversion_adjustment = client.get_type("ConversionAdjustment") conversion_adjustment.adjustment_type = ( client.enums.ConversionAdjustmentTypeEnum.ENHANCEMENT ) # Creates a user identifier using the hashed email address, using the # normalize and hash method specifically for email addresses. email_identifier = client.get_type("UserIdentifier") # Optional: Specifies the user identifier source. email_identifier.user_identifier_source = ( client.enums.UserIdentifierSourceEnum.FIRST_PARTY ) # Uses the normalize and hash method specifically for email addresses. email_identifier.hashed_email = normalize_and_hash_email_address( raw_record["email"] ) # Adds the email identifier to the conversion adjustment. conversion_adjustment.user_identifiers.append(email_identifier) # Checks if the record has a phone number, and if so, adds a UserIdentifier # for it. if raw_record.get("phone") is not None: phone_identifier = client.get_type("UserIdentifier") phone_identifier.hashed_phone_number = normalize_and_hash( raw_record["phone"] ) # Adds the phone identifier to the conversion adjustment. conversion_adjustment.user_identifiers.append(phone_identifier) # Checks if the record has all the required mailing address elements, and if # so, adds a UserIdentifier for the mailing address. if raw_record.get("first_name") is not None: # Checks if the record contains all the other required elements of a # mailing address. required_keys = ["last_name", "country_code", "postal_code"] # Builds a new list of the required keys that are missing from # raw_record. missing_keys = [ key for key in required_keys if key not in raw_record.keys() ] if len(missing_keys) > 0: print( "Skipping addition of mailing address information because the" f"following required keys are missing: {missing_keys}" ) else: # Creates a user identifier using sample values for the user address, # hashing where required. address_identifier = client.get_type("UserIdentifier") address_info = address_identifier.address_info address_info.hashed_first_name = normalize_and_hash( raw_record["first_name"] ) address_info.hashed_last_name = normalize_and_hash( raw_record["last_name"] ) address_info.country_code = raw_record["country_code"] address_info.postal_code = raw_record["postal_code"] # Adds the address identifier to the conversion adjustment. conversion_adjustment.user_identifiers.append(address_identifier)
小茹
# 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:
Java
// Sets the conversion action. enhancementBuilder.setConversionAction( ResourceNames.conversionAction( customerId, Long.parseLong(rawRecord.get("conversionActionId")))); // Sets the order ID. Enhancements MUST use order ID instead of GCLID date/time pair. enhancementBuilder.setOrderId(rawRecord.get("orderId")); // Sets the conversion date and time if provided. Providing this value is optional but // recommended. if (rawRecord.containsKey("conversionDateTime")) { enhancementBuilder.setGclidDateTimePair( GclidDateTimePair.newBuilder() .setConversionDateTime(rawRecord.get("conversionDateTime"))); } // Sets the user agent if provided. This should match the user agent of the request that sent // the original conversion so the conversion and its enhancement are either both attributed as // same-device or both attributed as cross-device. if (rawRecord.containsKey("userAgent")) { enhancementBuilder.setUserAgent(rawRecord.get("userAgent")); }
C#
// Set the conversion action. enhancement.ConversionAction = ResourceNames.ConversionAction(customerId, conversionActionId); // Set the order ID. Enhancements MUST use order ID instead of GCLID date/time pair. enhancement.OrderId = orderId; // Sets the conversion date and time if provided. Providing this value is optional but // recommended. if (string.IsNullOrEmpty(conversionDateTime)) { enhancement.GclidDateTimePair = new GclidDateTimePair() { ConversionDateTime = conversionDateTime }; } // Sets optional fields where a value was provided. if (!string.IsNullOrEmpty(userAgent)) { // Sets the user agent. This should match the user agent of the request that // sent the original conversion so the conversion and its enhancement are either // both attributed as same-device or both attributed as cross-device. enhancement.UserAgent = userAgent; }
PHP
// Sets the conversion action. $enhancement->setConversionAction( ResourceNames::forConversionAction($customerId, $rawRecord['conversionActionId']) ); // Sets the order ID. Enhancements MUST use order ID instead of GCLID date/time pair. if (!empty($rawRecord['orderId'])) { $enhancement->setOrderId($rawRecord['orderId']); } // Sets the conversion date and time if provided. Providing this value is optional but // recommended. if (!empty($rawRecord['conversionDateTime'])) { // Sets the conversion date and time if provided. Providing this value is optional but // recommended. $enhancement->setGclidDateTimePair( new GclidDateTimePair( [ 'conversion_date_time' => $rawRecord['conversionDateTime'] ] ) ); } // Sets the user agent if provided. This should match the user agent of the request that // sent the original conversion so the conversion and its enhancement are either both // attributed as same-device or both attributed as cross-device. if (!empty($rawRecord['userAgent'])) { $enhancement->setUserAgent($rawRecord['userAgent']); }
Python
conversion_action_service = client.get_service("ConversionActionService") # Sets the conversion action. conversion_adjustment.conversion_action = ( conversion_action_service.conversion_action_path( customer_id, raw_record["conversion_action_id"] ) ) # Sets the order ID. Enhancements MUST use order ID instead of GCLID # date/time pair. conversion_adjustment.order_id = order_id # Sets the conversion date and time if provided. Providing this value is # optional but recommended. if raw_record.get("conversion_date_time"): conversion_adjustment.gclid_date_time_pair.conversion_date_time = ( raw_record["conversion_date_time"] ) # Sets optional fields where a value was provided if raw_record.get("user_agent"): # Sets the user agent. This should match the user agent of the request # that sent the original conversion so the conversion and its # enhancement are either both attributed as same-device or both # attributed as cross-device. conversion_adjustment.user_agent = user_agent
小茹
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[] |
必填 | 每項要求 (TOO_MANY_ADJUSTMENTS_IN_REQUEST) 最多可包含 2,000 個 ConversionAdjustment 項目。 |
partial_failure |
必填 | 必須設為 true,系統才會處理有效的調整項,並在 partial_failure_error 中傳回資料列層級錯誤。 |
validate_only |
選用 | 如果是 true,則會驗證要求,但不執行調整。 |
job_id |
選用 | 正數 int32 (1 至 2^31 - 1;0 是未設定的標記,如果指定,則傳回 INVALID_JOB_ID),用於將多個要求分組到 job_summaries 中的單一邏輯工作。 |
conversion_adjustments[] 中的每個 ConversionAdjustment (每次要求最多 2,000 項調整) 支援下列欄位:
ConversionAdjustment 欄位 |
規定 | 格式和限制 |
|---|---|---|
adjustment_type |
必填 | 必須設為 ENHANCEMENT (ConversionAdjustmentType)。 |
conversion_action |
必填 | ENABLED 轉換動作的資源名稱 (customers/{conversion_customer_id}/conversionActions/{action_id}),其中 type = WEBPAGE 至少在上傳前 6 小時建立 (TOO_RECENT_CONVERSION_ACTION)。 |
order_id |
必填 | 要強化的轉換交易 ID;必須與網站轉換代碼傳送的訂單 ID 相符。 |
user_identifiers[] |
必填 | 每項強化功能可包含一到五個 UserIdentifier 物件。設定選用的 user_identifier_source 欄位 (例如 FIRST_PARTY),以及每個項目一個 ID 欄位:
|
gclid_date_time_pair |
選用 (建議) | GclidDateTimePair 物件,包含原始轉換的 gclid 和 conversion_date_time 欄位,格式為 yyyy-mm-dd HH:mm:ss+|-HH:mm (例如 2026-06-01 14:30:00-04:00)。請在轉換事件發生後的 24 小時內上傳強化資料。GclidDateTimePair 訊息只接受 gclid 欄位。如果點擊填入 gbraid 或 wbraid 參數,而非 gclid 參數,Google Ads 就不支援網站強化轉換。 |
adjustment_date_time |
選用 | 調整項目的發生時間戳記,格式為 yyyy-mm-dd HH:mm:ss+|-HH:mm。如果設定,這個時間戳記必須晚於 conversion_date_time 值,且不得為未來時間。 |
user_agent |
選用 | 原始轉換要求的 HTTP User-Agent 標頭字串,讓轉換和強化功能共用相同的裝置歸因 (same-device 或 cross-device)。 |
restatement_value |
請勿在 ENHANCEMENT 上設定 |
如果 adjustment_type 為 ENHANCEMENT,則會忽略。請使用 adjustment_type = RESTATEMENT 傳送個別作業,變更轉換價值。 |
匯入轉換強化資料
Java
// Creates the conversion adjustment upload service client. try (ConversionAdjustmentUploadServiceClient conversionUploadServiceClient = googleAdsClient.getLatestVersion().createConversionAdjustmentUploadServiceClient()) { // Uploads the enhancement adjustment. Partial failure should always be set to true. // NOTE: This request contains a single adjustment as a demonstration. However, if you have // multiple adjustments to upload, it's best to upload multiple adjustments per request // instead of sending a separate request per adjustment. See the following for per-request // limits: // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjustment_upload_service UploadConversionAdjustmentsResponse response = conversionUploadServiceClient.uploadConversionAdjustments( UploadConversionAdjustmentsRequest.newBuilder() .setCustomerId(Long.toString(customerId)) .addConversionAdjustments(enhancementBuilder) // Enables partial failure (must be true). .setPartialFailure(true) .build());
C#
// Uploads the enhancement adjustment. Partial failure should always be set to true. // // NOTE: This request contains a single adjustment as a demonstration. // However, if you have multiple adjustments to upload, it's best to upload // multiple adjustmenst per request instead of sending a separate request per // adjustment. See the following for per-request limits: // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjust UploadConversionAdjustmentsResponse response = conversionAdjustmentUploadService.UploadConversionAdjustments( new UploadConversionAdjustmentsRequest() { CustomerId = customerId.ToString(), ConversionAdjustments = { enhancement }, // Enables partial failure (must be true). PartialFailure = true, });
PHP
// Issues a request to upload the conversion enhancement. $conversionAdjustmentUploadServiceClient = $googleAdsClient->getConversionAdjustmentUploadServiceClient(); // NOTE: This request contains a single adjustment as a demonstration. However, if you have // multiple adjustments to upload, it's best to upload multiple adjustments per request // instead of sending a separate request per adjustment. See the following for per-request // limits: // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjustment_upload_service $response = $conversionAdjustmentUploadServiceClient->uploadConversionAdjustments( // Enables partial failure (must be true). UploadConversionAdjustmentsRequest::build($customerId, [$enhancement], true) );
Python
# Creates the conversion adjustment upload service client. conversion_adjustment_upload_service = client.get_service( "ConversionAdjustmentUploadService" ) # Uploads the enhancement adjustment. Partial failure should always be set # to true. # NOTE: This request only uploads a single conversion, but if you have # multiple conversions to upload, it's still best to upload them in a single # request. See the following for per-request limits for reference: # https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service response = conversion_adjustment_upload_service.upload_conversion_adjustments( customer_id=customer_id, conversion_adjustments=[conversion_adjustment], # Enables partial failure (must be true). partial_failure=True, )
小茹
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 時,請傳送 POST 要求,並將 partialFailure 設為 true:
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 項目,而失敗的資料列則會在 results[] 中傳回空白的 {} 物件,並在 UploadConversionAdjustmentsResponse 的 partialFailureError 中傳回資料列層級的錯誤詳細資料:
{
"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"
}
]
}
}
]
}
]
}
}
查看匯入內容
請同時監控 Google Ads UI 診斷和離線資料診斷,因為這兩項工具會檢查管道的不同階段:
- 強化轉換診斷報告 (Google Ads 使用者介面):
驗證網站代碼事件和 API 強化功能之間的非同步比對。請查看這份報表,瞭解是否有代碼交易 ID 遺漏、代碼和 API
order_id值不一致、API 強化涵蓋率偏低、使用者資料未正規化,或address_info欄位不完整等問題。這類問題不會傳回 API 錯誤,也不會顯示在離線資料診斷中。如需疑難排解步驟,請參閱「成功上傳 API 後的使用者介面診斷快訊」。 - 離線資料診斷 (Google Ads API):追蹤要求層級的 API 擷取健康狀態和驗證錯誤,例如
INVALID_USER_IDENTIFIER、CONVERSION_ALREADY_ENHANCED或FieldError.REQUIRED錯誤。EXCELLENT狀態僅確認 API 接受了您的ENHANCEMENT要求,不代表上傳的強化資料與網站代碼事件相符。
使用 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
匯入轉換時,客戶 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 值。
如果有多個 ID,請一併提供
如果您有轉換的 gclid 值,請在 gclid_date_time_pair 欄位和 user_identifiers 欄位中傳送該值,以提升成效。由於 GclidDateTimePair 訊息只接受 gclid 欄位,因此當點擊填入 gbraid 或 wbraid 參數 (而非 gclid 參數) 時,Google Ads 不支援網站強化轉換。如果轉換有多個 UserIdentifier 物件,請將所有物件納入同一個 ConversionAdjustment 物件,提高比對成功的機率。
查看設定整合時發生的部分失敗錯誤
首次設定網站強化轉換整合時,請查看並解決回應中 partial_failure_error 欄位的部分錯誤。如果設定有問題,您可以檢查這個欄位,調查並解決問題。如需更多有關部分失敗錯誤處理的詳細資料和範例,請參閱部分失敗指南。
解決 partial_failure_error 欄位中的問題並完成整合後,請監控這兩項診斷工具。請查看離線資料診斷,瞭解 API 擷取健康狀態,並在 Google Ads 使用者介面中查看強化轉換診斷報告,瞭解代碼與 API 比對健康狀態。
在單一要求中批次處理多項調整
如要匯入多項調整項,請將作業批次處理 (每項要求最多 2,000 項轉換調整項),並合併為一個 UploadConversionAdjustmentsRequest,而不是為每項調整項傳送匯入要求。
請參閱配額指南,瞭解每次要求可調整的數量上限。如要讓離線資料診斷將一組要求歸入同一個邏輯工作,請將所有要求的 job_id 設為相同的值。
疑難排解
在 Google Ads 使用者介面中,使用強化轉換診斷報告,驗證網站強化轉換是否正常運作。如果 API 上傳成功,但使用者介面仍顯示快訊,請參閱「API 上傳成功後的使用者介面診斷快訊」。
排解常見錯誤
以下指引可協助您診斷及修正網站強化轉換的常見錯誤。
| 錯誤 | |
|---|---|
CUSTOMER_NOT_ACCEPTED_CUSTOMER_DATA_TERMS
|
要求的customer_id尚未接受顧客數位資料條款及細則。如需操作說明,請參閱必備條件指南。 |
CONVERSION_ALREADY_ENHANCED
|
這項轉換已收到ENHANCEMENT調整項,其中包含指定的order_id和conversion_action欄位。請務必為每項轉換提供不重複的 order_id 值。 |
CONVERSION_ACTION_NOT_ELIGIBLE_FOR_ENHANCEMENT
|
提供的 conversion_action 不符合網站強化轉換的使用資格。在 Google Ads 使用者介面中,請務必在要求中參照的轉換動作上,勾選「啟用強化轉換」方塊。如需操作說明,請前往說明中心。 |
INVALID_USER_IDENTIFIER
|
雜湊使用者 ID 欄位 (hashed_email、hashed_phone_number、hashed_first_name、hashed_last_name 或 hashed_street_address) 不是有效的 64 字元十六進位 SHA-256 摘要。請先對使用者提供的資料進行正規化和雜湊處理,再匯入資料。如需操作說明,請參閱「正規化及雜湊處理使用者提供的資料」。 |
CONVERSION_NOT_FOUND
|
Google Ads 找不到與提供的 如果是 |