میتوانید از طریق این راهنمای سریع با ارسال دادههای رویداد آشنا شوید.
از API مدیریت داده برای هر یک از سناریوهای زیر استفاده کنید:
تبدیلهای تگ گوگل ادز یا رویدادهای
purchaseگوگل آنالیتیکس را به عنوان منبع داده اضافی برای تبدیلهای تگ خود ارسال کنید تا سیگنالهای تعامل تبلیغات را به حداکثر برسانید و دادهها و عملکرد کلی خود را تقویت کنید.این ویژگی برای همه حسابهای گوگل ادز در دسترس است، اما فقط برای ویژگیهای گوگل آنالیتیکس موجود در یک لیست مجاز در دسترس است. اگر علاقهمند به اضافه کردن ویژگی گوگل آنالیتیکس خود به لیست مجاز هستید، فرم را پر کنید .
دادههای رویداد را برای تبدیلهای آفلاین گوگل ادز یا تبدیلهای پیشرفته برای سرنخها ارسال کنید.
نسخهای از راهنما را که میخواهید ببینید انتخاب کنید.
- اگر از اعتبارنامههای حساب گوگلی استفاده میکنید که کاربر آن در حسابهای تبلیغکنندهای است که میخواهید مدیریت کنید، تبلیغکننده را انتخاب کنید.
- اگر از اعتبارنامههای یک حساب گوگل استفاده میکنید که کاربری در یک حساب شریک داده است و میخواهید حسابهای تبلیغکنندهای را مدیریت کنید که یک لینک شریک به حساب شریک داده دارند، گزینه شریک داده را انتخاب کنید.
در این راهنمای سریع، مراحل زیر را انجام میدهید:
- یک
Destinationبرای دریافت دادههای رویداد آماده کنید. - دادههای رویداد را برای ارسال آماده کنید.
- یک درخواست
IngestionServiceبرای رویدادها بسازید. - درخواست را با Google APIs Explorer ارسال کنید.
- واکنشهای موفقیت و شکست را درک کنید.
آمادهسازی مقاصد
قبل از اینکه بتوانید داده ارسال کنید، باید حداقل یک Destination برای دادهها آماده کنید. در اینجا یک نمونه Destination برای استفاده شما آورده شده است:
{
"operatingAccount": {
"accountType": "OPERATING_ACCOUNT_TYPE",
"accountId": "OPERATING_ACCOUNT_ID"
},
"loginAccount": {
"accountType": "LOGIN_ACCOUNT_TYPE",
"accountId": "LOGIN_ACCOUNT_ID"
},
"productDestinationId": "PRODUCT_DESTINATION_ID"
}
فیلدهای یک Destination به شرح زیر است. برای جزئیات بیشتر و مثالهایی از مقصدها برای سناریوهای مختلف، به پیکربندی مقصدها (Configure destinations) مراجعه کنید.
-
operatingAccount حسابی که رویدادها را دریافت میکند.
برای رویدادهایی که به عنوان منبع داده اضافی ارسال میشوند، حساب عملیاتی میتواند یک حساب Google Ads یا یک ویژگی Google Analytics باشد.
اگر
accountTypeGOOGLE_ANALYTICS_PROPERTYباشد، اعتبارنامههای درخواست باید برای یک کاربر Google Analytics با نقش ویرایشگر یا مدیر برای ویژگی باشد.برای تبدیلهای آفلاین و تبدیلهای پیشرفته برای سرنخها، حساب کاربری باید یک حساب گوگل ادز باشد.
-
loginAccount - حسابی که حساب گوگل برای اعتبارنامهها در آن یک کاربر است.
-
productDestinationId شناسهی موجودیتی در
operatingAccountکه رویدادها را دریافت میکند.برای رویدادهایی که به عنوان منبع داده اضافی ارسال میشوند،
productDestinationIdباید یکی از موارد زیر باشد:شناسه تبدیل گوگل ادز با
typeتنظیم شده رویWEBPAGE. در رابط کاربری گوگل ادز، منبع تبدیل برای یک اقدام تبدیلWEBPAGEوبسایت است.شناسه اندازهگیری یک جریان وب گوگل آنالیتیکس. شما نمیتوانید رویدادها را به عنوان منبع داده اضافی به جریان برنامه iOS یا برنامه اندروید گوگل آنالیتیکس ارسال کنید.
برای تبدیلهای آفلاین یا تبدیلهای پیشرفته برای سرنخها،
productDestinationIdباید شناسه یک اقدام تبدیل گوگل ادز باtypeتنظیم شده رویUPLOAD_CLICKSباشد. در رابط کاربری گوگل ادز، منبع تبدیل برای یک اقدام تبدیلUPLOAD_CLICKSوبسایت (وارد کردن از کلیکها) است.
مثال موجود در این راهنما نحوه ساخت درخواستی را نشان میدهد که هر رویداد را به یک مقصد ارسال میکند. اگر میخواهید رویدادهایی را برای چندین مقصد در یک درخواست ارسال کنید، به بخش ارسال رویدادها برای چندین مقصد مراجعه کنید.
آمادهسازی دادههای رویداد
دادههای رویداد زیر را در نظر بگیرید. هر جدول مربوط به یک رویداد تبدیل است. هر رویداد تبدیل دارای یک مهر زمانی از رویداد، عمل تبدیل آن و مقدار تبدیل است.
هر رویداد ممکن است شناسههای تبلیغاتی مانند gclid یا شناسههای کاربری مانند آدرسهای ایمیل، شماره تلفنها و اطلاعات آدرس داشته باشد. یک رویداد همچنین میتواند موارد زیر را داشته باشد:
- اطلاعات مربوط به کاربر که در زمان رویداد ارزیابی شده است ، مانند ارزش مشتری یا اینکه آیا آنها مشتری جدید، مشتری قدیمی یا مشتری فعال هستند.
- دادههای سبد خرید.
- پارامترهای رویداد اضافی یا ویژگیهای کاربر برای یک مقصد، مانند
client_idیاuser_idبرای Google Analytics.
دادههای رویداد به شرح زیر است:
رویداد ۱
| رویداد شماره ۱ | |
|---|---|
conversion_time | 2025-06-10 15:07:01-05:00 |
conversion_action_id | 123456789 |
transaction_id | ABC798654321 |
conversion_value | 30.03 |
currency | USD |
gclid | GCLID_1 |
emails | |
given_name | John |
family_name | Smith-Jones |
region_code | us |
postal_code | 94045 |
customer_type | NEW |
customer_value_bucket | HIGH |
client_id | 1234567890.1761581763 |
user_id | user_ABC12345 |
ad_unit_name | Banner_01 |
event_name | purchase |
| اقلام سبد خرید | |
item_id | SKU_12345 |
item_name | Stan and Friends Tee |
item_affiliation | Google Merchandise Store |
item_coupon | SUMMER_FUN |
item_discount | 2.22 |
item_index | 0 |
item_brand | Google |
item_category | Apparel |
item_category2 | Adult |
item_category3 | Shirts |
item_category4 | Crew |
item_category5 | Short sleeve |
item_list_id | related_products |
item_list_name | Related Products |
item_price | 10.01 |
item_quantity | 3 |
رویداد ۲
| رویداد شماره ۲ | |
|---|---|
conversion_time | June 10, 2025 11:42:33PM America/New_York |
conversion_action_id | 123456789 |
transaction_id | DEF999911111 |
conversion_value | 42.02 |
currency | eur |
gclid | GCLID_2 |
emails | |
given_name | zoë |
family_name | pérez |
region_code | PT |
postal_code | 1229-076 |
customer_type | RETURNING |
client_id | 9876543210.1761582117 |
user_id | user_DEF9876 |
ad_unit_name | Banner_02 |
event_name | purchase |
| اقلام سبد خرید | |
item_id | SKU_12346 |
item_name | Google Grey Women's Tee |
item_affiliation | Google Merchandise Store |
item_coupon | SUMMER_FUN |
item_discount | 3.33 |
item_index | 1 |
item_brand | Google |
item_category | Apparel |
item_category2 | Adult |
item_category3 | Shirts |
item_category4 | Crew |
item_category5 | Short sleeve |
item_list_id | related_products |
item_list_name | Related Products |
item_price | 21.01 |
item_quantity | 2 |
قالببندی دادهها
فیلدها را مطابق با راهنمای قالببندی مشخص شده، قالببندی کنید. دادههای رویداد پس از قالببندی به صورت زیر است:
رویداد ۱
| رویداد شماره ۱ | |
|---|---|
conversion_time | 2025-06-10T15:07:01-05:00 |
conversion_action_id | 123456789 |
transaction_id | ABC798654321 |
conversion_value | 30.03 |
currency | USD |
gclid | GCLID_1 |
emails | |
given_name | john |
family_name | smith-jones |
region_code | US |
postal_code | 94045 |
customer_type | NEW |
customer_value_bucket | HIGH |
client_id | 1234567890.1761581763 |
user_id | user_ABC12345 |
ad_unit_name | Banner_01 |
event_name | purchase |
| اقلام سبد خرید | |
item_id | SKU_12345 |
item_name | Stan and Friends Tee |
item_affiliation | Google Merchandise Store |
item_coupon | SUMMER_FUN |
item_discount | 2.22 |
item_index | 0 |
item_brand | Google |
item_category | Apparel |
item_category2 | Adult |
item_category3 | Shirts |
item_category4 | Crew |
item_category5 | Short sleeve |
item_list_id | related_products |
item_list_name | Related Products |
item_price | 10.01 |
item_quantity | 3 |
رویداد ۲
| رویداد شماره ۲ | |
|---|---|
conversion_time | 2025-06-10T23:42:33-05:00 |
conversion_action_id | 123456789 |
transaction_id | DEF999911111 |
conversion_value | 42.02 |
currency | EUR |
gclid | GCLID_2 |
emails | |
given_name | zoë |
family_name | pérez |
region_code | PT |
postal_code | 1229-076 |
customer_type | RETURNING |
client_id | 9876543210.1761582117 |
user_id | user_DEF9876 |
ad_unit_name | Banner_02 |
event_name | purchase |
| اقلام سبد خرید | |
item_id | SKU_12346 |
item_name | Google Grey Women's Tee |
item_affiliation | Google Merchandise Store |
item_coupon | SUMMER_FUN |
item_discount | 3.33 |
item_index | 1 |
item_brand | Google |
item_category | Apparel |
item_category2 | Adult |
item_category3 | Shirts |
item_category4 | Crew |
item_category5 | Short sleeve |
item_list_id | related_products |
item_list_name | Related Products |
item_price | 21.01 |
item_quantity | 2 |
هش کردن و رمزگذاری دادهها
علاوه بر این، آدرسهای ایمیل فرمتشده، نامهای دادهشده و نامهای خانوادگی باید با استفاده از الگوریتم SHA-256 هش شده و با استفاده از کدگذاری hex یا Base64 کدگذاری شوند. در اینجا دادههای رویداد پس از قالببندی، هش کردن و کدگذاری با استفاده از کدگذاری hex آمده است:
رویداد ۱
| رویداد شماره ۱ | |
|---|---|
conversion_time | 2025-06-10T15:07:01-05:00 |
conversion_action_id | 123456789 |
transaction_id | ABC798654321 |
conversion_value | 30.03 |
currency | USD |
gclid | GCLID_1 |
emails | |
given_name | 96D9632F363564CC3032521409CF22A852F2032EEC099ED5967C0D000CEC607A |
family_name | DB98D2607EFFFA28AFF66975868BF54C075ECA7157E35064DCE08E20B85B1081 |
region_code | US |
postal_code | 94045 |
customer_type | NEW |
customer_value_bucket | HIGH |
client_id | 1234567890.1761581763 |
user_id | user_ABC12345 |
ad_unit_name | Banner_01 |
event_name | purchase |
| اقلام سبد خرید | |
item_id | SKU_12345 |
item_name | Stan and Friends Tee |
item_affiliation | Google Merchandise Store |
item_coupon | SUMMER_FUN |
item_discount | 2.22 |
item_index | 0 |
item_brand | Google |
item_category | Apparel |
item_category2 | Adult |
item_category3 | Shirts |
item_category4 | Crew |
item_category5 | Short sleeve |
item_list_id | related_products |
item_list_name | Related Products |
item_price | 10.01 |
item_quantity | 3 |
رویداد ۲
| رویداد شماره ۲ | |
|---|---|
conversion_time | 2025-06-10T23:42:33-05:00 |
conversion_action_id | 123456789 |
transaction_id | DEF999911111 |
conversion_value | 42.02 |
currency | EUR |
gclid | GCLID_2 |
emails | |
given_name | 2752B88686847FA5C86F47B94CE652B7B3F22A91C37617D451A4DB9AFA431450 |
family_name | 6654977D57DDDD3C0329CA741B109EF6CD6430BEDD00008AAD213DF25683D77F |
region_code | PT |
postal_code | 1229-076 |
customer_type | RETURNING |
client_id | 9876543210.1761582117 |
user_id | user_DEF9876 |
ad_unit_name | Banner_02 |
event_name | purchase |
| اقلام سبد خرید | |
item_id | SKU_12346 |
item_name | Google Grey Women's Tee |
item_affiliation | Google Merchandise Store |
item_coupon | SUMMER_FUN |
item_discount | 3.33 |
item_index | 1 |
item_brand | Google |
item_category | Apparel |
item_category2 | Adult |
item_category3 | Shirts |
item_category4 | Crew |
item_category5 | Short sleeve |
item_list_id | related_products |
item_list_name | Related Products |
item_price | 21.01 |
item_quantity | 2 |
تبدیل دادهها به اشیاء Event
دادههای قالببندی و هششدهی هر رویداد را به یک Event تبدیل کنید. فیلدهای زیر را مطابق دستورالعمل پر کنید:
eventTimestampرا روی زمان وقوع رویداد تنظیم کنید.رویدادهای گوگل آنالیتیکس باید در ۷۲ ساعت گذشته دارای
eventTimestampباشند.فیلدهای مورد نیاز برای مورد استفاده خود را تنظیم کنید.
مورد استفاده شناسهها transactionIdeventSourceتبدیلهای آفلاین یا تبدیلهای بهبود یافته برای سرنخها الزامی . حداقل یکی از موارد زیر را تنظیم کنید: -
adIdentifiersبا حداقل یکی از مجموعههایgclid،gbraidیاwbraid - ویژگیهای جلسه
-
userData
اختیاری الزامی . روی یکی از مقادیر شمارشی برای EventSourceتنظیم شود.رویدادهایی که به عنوان منبع داده اضافی به مقصد تبلیغات گوگل ارسال میشوند الزامی . حداقل یکی از موارد زیر را تنظیم کنید: -
adIdentifiersکه حداقل یکی از مجموعههایgclid،gbraidیاwbraidرا داشته باشند -
userData
مورد نیاز اختیاری. در صورت تنظیم، باید WEBباشد.رویدادهایی که به عنوان منبع داده اضافی به مقصد Google Analytics ارسال میشوند الزامی . حداقل یکی از موارد زیر را تنظیم کنید: -
clientId -
adIdentifiersبا مجموعهgclid -
userId
مورد نیاز اختیاری. در صورت تنظیم، باید WEBباشد.-
اگر رویدادها را به عنوان منبع داده اضافی به مقصد تبلیغات گوگل ارسال میکنید، نحوه مدیریت دادههای منبع داده اضافی توسط گوگل را بررسی کنید.
هر فیلد دیگری را که مقداری برای رویداد دارید، پر کنید. برای مشاهده لیست کامل فیلدهای موجود، به مستندات مرجع
Eventمراجعه کنید.
نحوه مدیریت دادههای منبع داده اضافی توسط گوگل
در همان اقدام تبدیل، گوگل از transactionId برای حذف رویدادهای تبدیل ارسال شده از منابع مختلف (مانند تگ وبسایت شما و درخواستهای دریافت داده از API مدیریت داده) استفاده میکند. جدول زیر نحوه پردازش دادههای درخواستهای دریافت داده شما را توضیح میدهد.
| سناریو | فیلد داده | چگونه مدیریت میشود؟ |
|---|---|---|
transactionId با یک رویداد تگ موجود مطابقت دارد | conversionValue (با currencyCode ) | بهروزرسانی شد. توجه: در طول دوره آزمایشی اولیه ۱۴ روزه برای یک اقدام تبدیل، بهروزرسانیهای ارزش غیرفعال هستند. ارزش برچسب در گزارشهای Google Ads تا پایان دوره آزمایشی لغو نخواهد شد. |
transactionId با یک رویداد تگ موجود مطابقت دارد | فیلدهای دیگر به جز conversionValue یا currencyCode (برای مثال، adIdentifiers.gclid ) | نادیده گرفته شد. مقادیر فیلدهای دیگر از منبع داده اضافی شما، مقادیر فیلدی که در ابتدا توسط برچسب گوگل شما برای تراکنشهای منطبق ثبت شدهاند را بازنویسی نمیکنند. |
transactionId با هیچ رویدادی مطابقت ندارد. | تمام دادههای ارائه شده (برای مثال، userData ، conversionValue ، currencyCode ) | برای ایجاد یک رویداد تبدیل جدید استفاده میشود. سپس گوگل سعی میکند این تبدیل جدید را با استفاده از شناسههایی که شما ارائه کردهاید (مانند توجه: در طول دوره آزمایشی ۱۴ روزه اولیه، این تبدیلهای تازه ایجاد شده در گزارش شما ظاهر میشوند اما برای پیشنهاد قیمت استفاده نخواهند شد. پس از پایان دوره آزمایشی، آنها به طور خودکار قابل پیشنهاد قیمت میشوند. |
اضافه کردن ویژگیهای جلسه
اگر در حال ارسال تبدیلهای آفلاین یا تبدیلهای پیشرفته برای سرنخها هستید، وقتی سایر شناسههای تبلیغ مانند GCLID یا WBRAID در دسترس نیستند ، ویژگیهای جلسه را اضافه کنید. همچنین میتوانید علاوه بر سایر شناسههای تبلیغ، ویژگیهای جلسه را نیز اضافه کنید.
ویژگیهای جلسه، زمینه و سیگنالهای بیشتری در مورد تعامل کاربر با وبسایت شما ارائه میدهند که میتواند اندازهگیری تبدیل، گزارشدهی و دقت پیشنهاد قیمت را افزایش دهد.
در رابط برنامهنویسی کاربردی مدیریت داده (Data Manager API)، دو رویکرد وجود دارد که میتوانید برای ارسال ویژگیهای جلسه (session attributes) از آنها استفاده کنید:
توصیه میشود: فیلد
sessionAttributesازadIdentifiersرا روی رشته ویژگیهای جلسه کدگذاری شده base64 تنظیم کنید. دستورالعملهای موجود در نحوه ضبط session_attributes را دنبال کنید تا صفحات ارسال فرم خود را برای ضبط رشته کدگذاری شده تغییر دهید.اگر نمیتوانید از جاوا اسکریپت استفاده کنید، فیلدهای ویژگی جلسه را به صورت جداگانه ضبط کنید و هر کدام را به عنوان یک
ExperimentalFieldجداگانه به لیستexperimentalFieldsاضافه کنید:-
gad_campaignid -
session_start_time_usec -
gad_source -
landing_page_url -
landing_page_referrer
اگر مقداری برای ویژگی session مربوط به
landing_page_user_agentدارید، آن را در فیلدuserAgentازadIdentifiers.landingPageDeviceInfoارسال کنید.در اینجا بهترین شیوهها هنگام ارسال جفتهای کلید-مقدار به صورت جداگانه آورده شده است:
- به طور مداوم
gad_campaignidوsession_start_time_usecرا ارسال کنید. این فیلدها برای انتساب دقیق بسیار مهم هستند. - مقدار نادرست یا ناقصی
landing_page_urlمانند رشتهی جایگزین، مسیر داخلی برنامه یا URL ناقص ارائه ندهید. اگر URL دقیق و کامل را ندارید،landing_page_urlحذف کنید.
در اینجا بخشی از یک رویداد نمونه با ورودیهای
gad_campaignidوsession_start_time_usecدرexperimentalFieldsو user agent در فیلدlandingPageDeviceInfoآمده است:{ ..., "experimentalFields": [ { "field": "gad_campaignid", "value": "21288051566" }, { "field": "session_start_time_usec", "value": "1767711548052000" } ], "adIdentifiers": { "landingPageDeviceInfo": { "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36" } } }-
اطلاعات گوگل آنالیتیکس را اضافه کنید
اگر مقاصد رویدادی که به عنوان منبع داده اضافی ارسال میشود شامل یک ویژگی Google Analytics است، فیلدهای زیر را همانطور که نشان داده شده است، پر کنید:
-
eventName الزامی . نام رویداد گوگل آنالیتیکس.
-
transactionId الزامی . شناسه منحصر به فرد برای رویداد.
- حداقل یک شناسه
حداقل یکی از فیلدهای زیر باید تنظیم شود:
clientId: شناسه منحصر به فرد برای یک نمونه کاربری از یک کلاینت وب. به ارسال رویداد به پروتکل اندازهگیری مراجعه کنید.userId: یک شناسه منحصر به فرد برای یک کاربر. برای اطلاعات بیشتر به بخش اندازهگیری فعالیت در پلتفرمهای مختلف با استفاده از User-ID مراجعه کنید.
-
destinationReferences اگر لیست
destinationsسطح درخواست شامل بیش از یکDestinationگوگل آنالیتیکس باشد، الزامی است . یک ورودی بهdestinationReferencesاضافه کنید تا مشخص شود کدام مقصد گوگل آنالیتیکس باید رویداد را دریافت کند. برای اطلاعات بیشتر در مورد ارجاعات مقصد ، به ارسال رویدادها به چندین مقصد مراجعه کنید.اگر
destinationReferencesتنظیم نشده باشد یا چندین ورودی داشته باشد که به مقصدهای گوگل آنالیتیکس اشاره میکنند، رابط برنامهنویسی کاربردی مدیریت داده (Data Manager API) این رویداد را با خطایMULTIPLE_DESTINATIONS_FOR_GOOGLE_ANALYTICS_EVENTرد میکند.-
userId اختیاری. شناسه کاربری برای کاربر.
-
additionalEventParameters اختیاری، اما توصیه میشود. این لیست را با هر پارامتر رویداد گوگل آنالیتیکس که در فیلدهای
Eventدیگر ثبت نشدهاند، پر کنید. این پارامترها میتوانند شامل پارامترهای پیشنهادی اضافی از رویدادpurchaseیا سایر پارامترهایی باشند که میخواهید ثبت کنید. از نام پارامتر گوگل آنالیتیکس برایparameterNameEventParameterاستفاده کنید.برای مثال، اگر مالیاتهای مرتبط با یک تراکنش را دارید، یک ورودی به
additionalEventParametersاضافه کنید که در آنparameterNameبرابر باtaxوvalueبرابر با هزینه مالیات تنظیم شده باشد.ما توصیه نمیکنیم که برای پارامترهای رویداد Google Analytics، ورودیهایی برای
transactionId،currencyیاvalueاضافه کنید. در عوض،transactionId،currencyوconversionValueEventرا پر کنید، که بر هر ورودی درadditionalEventParametersاولویت دارند.
اضافه کردن دادههای سبد خرید برای رویدادهای خرید
فیلد cartData از Event را با اطلاعات مربوط به اقلام خریداری شده پر کنید. برای هر کالای خریداری شده، یک شیء Item به لیست items CartData اضافه کنید و فیلدهای زیر را همانطور که نشان داده شده است پر کنید:
-
itemId - الزامی . یک شناسه منحصر به فرد برای کالا.
-
unitPrice الزامی . قیمت واحد بدون احتساب مالیات، هزینه ارسال و تخفیفهای مربوط به رویداد (در سطح تراکنش).
اگر کالا تخفیف ویژه دارد، از قیمت واحد تخفیفدار استفاده کنید. برای مثال، اگر قیمت واحد یک کالا
27.67و تخفیف واحد آن6.66باشد،unitPriceروی21.01تنظیم کنید.-
quantity الزامی . تعداد واحدهای خریداری شده برای این کالای خاص.
-
additionalItemParameters این لیست را با هر پارامتری که در سایر فیلدهای Item قرار نمیگیرد و در محدوده
Itemقرار نمیگیرد، پر کنید. برایparameterNameازItemParameterاز نام پارامتر آیتم گوگل آنالیتیکس استفاده کنید.برای مثال، اگر برند و دستهبندی یک کالا را دارید، یک ورودی به
additionalItemParametersآن کالا اضافه کنید کهparameterNameآن رویitem_brandتنظیم شده وvalueروی نام برند تنظیم شده باشد، و یک ورودی دیگر باparameterNameرویitem_categoryتنظیم شده وvalueآن روی دستهبندی کالا تنظیم شده باشد.ما توصیه نمیکنیم که برای پارامترهای آیتم Google Analytics مانند
quantity،priceیاitem_idورودی اضافه کنید. در عوض،itemId،unitPriceوquantityمربوط بهItemرا که بر هر ورودی دیگری درadditionalItemParametersاولویت دارند، پر کنید.
در اینجا یک Event نمونه برای دادههای قالببندی شده، هش شده و رمزگذاری شده از رویداد دوم، به همراه دادههای اضافی برای گوگل آنالیتیکس، آورده شده است:
{
"adIdentifiers": {
"gclid": "GCLID_2"
},
"conversionValue": 42.02,
"currency": "EUR",
"eventTimestamp": "2025-06-10T23:42:33-05:00",
"transactionId": "DEF999911111",
"eventSource": "WEB",
"userData": {
"userIdentifiers": [
{
"emailAddress": "3E693CF7E5B67880BFF33B2D2626DADB7BF1D4BC737192E47CF8BAA89ACF2250"
},
{
"emailAddress": "223EBDA6F6889B1494551BA902D9D381DAF2F642BAE055888E96343D53E9F9C4"
},
{
"address": {
"givenName": "2752B88686847FA5C86F47B94CE652B7B3F22A91C37617D451A4DB9AFA431450",
"familyName": "6654977D57DDDD3C0329CA741B109EF6CD6430BEDD00008AAD213DF25683D77F",
"regionCode": "PT",
"postalCode": "1229-076"
}
}
],
},
"userProperties": {
"customerType": "RETURNING"
},
"eventName": "purchase",
"clientId": "9876543210.1761582117",
"userId": "user_DEF9876",
"additionalEventParameters": [
{
"parameterName": "ad_unit_name",
"value": "Banner_02"
}
],
"cartData": {
"transactionDiscount": 6.66,
"items": [
{
"itemId": "SKU_12346",
"quantity": 2,
"unitPrice": 21.01,
"additionalItemParameters": [
{
"parameterName": "item_name",
"value": "Google Grey Women's Tee"
},
{
"parameterName": "affiliation",
"value": "Google Merchandise Store"
},
{
"parameterName": "coupon",
"value": "SUMMER_FUN"
},
{
"parameterName": "discount",
"value": "3.33"
},
{
"parameterName": "index",
"value": "1"
},
{
"parameterName": "item_brand",
"value": "Google"
},
{
"parameterName": "item_category",
"value": "Apparel"
},
{
"parameterName": "item_category2",
"value": "Adult"
},
{
"parameterName": "item_category3",
"value": "Shirts"
},
{
"parameterName": "item_category4",
"value": "Crew"
},
{
"parameterName": "item_category5",
"value": "Short sleeve"
},
{
"parameterName": "item_list_id",
"value": "related_products"
},
{
"parameterName": "item_list_name",
"value": "Related Products"
}
]
}
]
}
}
ساخت بدنه درخواست
برای ساخت بدنه درخواست، destinations و events را ترکیب کنید، فیلد encoding را تنظیم کنید و هر فیلد درخواست دیگری که میخواهید شامل شود، مانند validateOnly و consent را اضافه کنید.
مثالهای این راهنما از رمزگذاری استفاده نمیکنند، اما میتوانید دستورالعملهای موجود در بخش «رمزگذاری دادههای کاربر» را برای افزودن رمزگذاری به فرآیند خود دنبال کنید.
درخواست را ارسال کنید
در اینجا مراحل امتحان کردن یک درخواست از مرورگر شما آمده است:
- تب REST را انتخاب کنید و روی Open in API Explorer کلیک کنید تا API Explorer در یک تب یا پنجره جدید باز شود.
- در بدنه درخواست در API Explorer، هر رشتهای که با
REPLACE_WITHشروع میشود، مانندREPLACE_WITH_OPERATING_ACCOUNT_TYPEرا با مقدار مربوطه جایگزین کنید. - روی Execute در پایین صفحه API Explorer کلیک کنید و مراحل احراز هویت را برای ارسال درخواست تکمیل کنید.
- برای اعتبارسنجی درخواست بدون اعمال تغییرات،
validateOnlyرا رویtrueتنظیم کنید. وقتی آماده اعمال تغییرات شدید،validateOnlyرا رویfalseتنظیم کنید.
اگر یک کتابخانه کلاینت نصب کردهاید ، برای مشاهده نمونه کد کامل نحوه ساخت و ارسال یک درخواست، زبان برنامهنویسی انتخابی خود را انتخاب کنید.
استراحت
{ "destinations": [ { "operatingAccount": { "accountType": "OPERATING_ACCOUNT_TYPE", "accountId": "OPERATING_ACCOUNT_ID" }, "loginAccount": { "accountType": "LOGIN_ACCOUNT_TYPE", "accountId": "LOGIN_ACCOUNT_ID" }, "productDestinationId": "CONVERSION_ACTION_ID" } ], "encoding": "HEX", "events": [ { "adIdentifiers": { "gclid": "GCLID_1" }, "conversionValue": 30.03, "currency": "USD", "eventTimestamp": "2025-06-10T20:07:01Z", "transactionId": "ABC798654321", "eventSource": "WEB", "userData": { "userIdentifiers": [ { "address": { "givenName": "96D9632F363564CC3032521409CF22A852F2032EEC099ED5967C0D000CEC607A", "familyName": "DB98D2607EFFFA28AFF66975868BF54C075ECA7157E35064DCE08E20B85B1081", "regionCode": "US", "postalCode": "94045" } } ] }, "userProperties": { "customerType": "NEW", "customerValueBucket": "HIGH" }, "eventName": "purchase", "clientId": "1234567890.1761581763", "userId": "user_ABC12345", "additionalEventParameters": [ { "parameterName": "ad_unit_name", "value": "Banner_01" } ], "cartData": { "transactionDiscount": 6.66, "items": [ { "itemId": "SKU_12345", "quantity": 3, "unitPrice": 10.01, "additionalItemParameters": [ { "parameterName": "item_name", "value": "Stan and Friends Tee" }, { "parameterName": "affiliation", "value": "Google Merchandise Store" }, { "parameterName": "coupon", "value": "SUMMER_FUN" }, { "parameterName": "discount", "value": "2.22" }, { "parameterName": "index", "value": "0" }, { "parameterName": "item_brand", "value": "Google" }, { "parameterName": "item_category", "value": "Apparel" }, { "parameterName": "item_category2", "value": "Adult" }, { "parameterName": "item_category3", "value": "Shirts" }, { "parameterName": "item_category4", "value": "Crew" }, { "parameterName": "item_category5", "value": "Short sleeve" }, { "parameterName": "item_list_id", "value": "related_products" }, { "parameterName": "item_list_name", "value": "Related Products" } ] } ] } }, { "adIdentifiers": { "gclid": "GCLID_2" }, "conversionValue": 42.02, "currency": "EUR", "eventTimestamp": "2025-06-11T04:42:33Z", "transactionId": "DEF999911111", "eventSource": "WEB", "userData": { "userIdentifiers": [ { "emailAddress": "3E693CF7E5B67880BFF33B2D2626DADB7BF1D4BC737192E47CF8BAA89ACF2250" }, { "emailAddress": "223EBDA6F6889B1494551BA902D9D381DAF2F642BAE055888E96343D53E9F9C4" }, { "address": { "givenName": "2752B88686847FA5C86F47B94CE652B7B3F22A91C37617D451A4DB9AFA431450", "familyName": "6654977D57DDDD3C0329CA741B109EF6CD6430BEDD00008AAD213DF25683D77F", "regionCode": "PT", "postalCode": "1229-076" } } ] }, "userProperties": { "customerType": "RETURNING" }, "eventName": "purchase", "clientId": "9876543210.1761582117", "userId": "user_DEF9876", "additionalEventParameters": [ { "parameterName": "ad_unit_name", "value": "Banner_02" } ], "cartData": { "transactionDiscount": 6.66, "items": [ { "itemId": "SKU_12346", "quantity": 2, "unitPrice": 21.01, "additionalItemParameters": [ { "parameterName": "item_name", "value": "Google Grey Women's Tee" }, { "parameterName": "affiliation", "value": "Google Merchandise Store" }, { "parameterName": "coupon", "value": "SUMMER_FUN" }, { "parameterName": "discount", "value": "3.33" }, { "parameterName": "index", "value": "1" }, { "parameterName": "item_brand", "value": "Google" }, { "parameterName": "item_category", "value": "Apparel" }, { "parameterName": "item_category2", "value": "Adult" }, { "parameterName": "item_category3", "value": "Shirts" }, { "parameterName": "item_category4", "value": "Crew" }, { "parameterName": "item_category5", "value": "Short sleeve" }, { "parameterName": "item_list_id", "value": "related_products" }, { "parameterName": "item_list_name", "value": "Related Products" } ] } ] } } ], "validateOnly": true }
دات نت
// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using System.Text.Json; using CommandLine; using Google.Ads.DataManager.Util; using Google.Ads.DataManager.V1; using Google.Protobuf.WellKnownTypes; using static Google.Ads.DataManager.V1.ProductAccount.Types; namespace Google.Ads.DataManager.Samples { // <summary> // Sends an <see cref="IngestEventsRequest" /> without using encryption. // // Event data is read from a data file. See the <c>events_1.json</c> file in the // <c>sampledata</c> directory for an example. // </summary> public class IngestEvents { private static readonly int MaxEventsPerRequest = 2_000; [Verb("ingest-events", HelpText = "Sends an IngestEventsRequest without using encryption.")] public class Options { [Option( "operatingAccountType", Required = true, HelpText = "Account type of the operating account" )] public AccountType OperatingAccountType { get; set; } [Option( "operatingAccountId", Required = true, HelpText = "ID of the operating account" )] public string OperatingAccountId { get; set; } = null!; [Option( "loginAccountType", Required = false, HelpText = "Account type of the login account" )] public AccountType? LoginAccountType { get; set; } [Option("loginAccountId", Required = false, HelpText = "ID of the login account")] public string? LoginAccountId { get; set; } [Option( "linkedAccountProduct", Required = false, HelpText = "Account type of the linked account" )] public AccountType? LinkedAccountType { get; set; } [Option("linkedAccountId", Required = false, HelpText = "ID of the linked account")] public string? LinkedAccountId { get; set; } [Option( "conversionActionId", Required = true, HelpText = "ID of the conversion action" )] public string ConversionActionId { get; set; } = null!; [Option( "jsonFile", Required = true, HelpText = "JSON file containing user data to ingest" )] public string JsonFile { get; set; } = null!; [Option( "validateOnly", Default = true, HelpText = "Whether to enable validateOnly on the request" )] public bool ValidateOnly { get; set; } } public void Run(Options options) { RunExample( options.OperatingAccountType, options.OperatingAccountId, options.LoginAccountType, options.LoginAccountId, options.LinkedAccountType, options.LinkedAccountId, options.ConversionActionId, options.JsonFile, options.ValidateOnly ); } private void RunExample( AccountType operatingAccountType, string operatingAccountId, AccountType? loginAccountType, string? loginAccountId, AccountType? linkedAccountType, string? linkedAccountId, string conversionActionId, string jsonFile, bool validateOnly ) { if (loginAccountId == null ^ loginAccountType == null) { throw new ArgumentException( "Must specify either both or neither of login account ID and login account " + "type" ); } if (linkedAccountId == null ^ linkedAccountType == null) { throw new ArgumentException( "Must specify either both or neither of linked account ID and linked account " + "type" ); } // Reads member data from the data file. List<EventRecord> eventRecords = ReadEventData(jsonFile); // Gets an instance of the UserDataFormatter for normalizing and formatting the data. UserDataFormatter userDataFormatter = new UserDataFormatter(); // Builds the events collection for the request. var events = new List<Event>(); foreach (var eventRecord in eventRecords) { var eventBuilder = new Event(); try { eventBuilder.EventTimestamp = Timestamp.FromDateTime( DateTime.Parse(eventRecord.Timestamp ?? "").ToUniversalTime() ); } catch (FormatException) { Console.WriteLine( $"Skipping event with invalid timestamp: {eventRecord.Timestamp}" ); continue; } if (string.IsNullOrEmpty(eventRecord.TransactionId)) { Console.WriteLine("Skipping event with no transaction ID"); continue; } eventBuilder.TransactionId = eventRecord.TransactionId; if (!string.IsNullOrEmpty(eventRecord.EventSource)) { if ( System.Enum.TryParse( eventRecord.EventSource, true, out EventSource eventSource ) ) { eventBuilder.EventSource = eventSource; } else { Console.WriteLine( $"Skipping event with invalid event source: {eventRecord.EventSource}" ); continue; } } if (!string.IsNullOrEmpty(eventRecord.Gclid)) { eventBuilder.AdIdentifiers = new AdIdentifiers { Gclid = eventRecord.Gclid }; } if (!string.IsNullOrEmpty(eventRecord.Currency)) { eventBuilder.Currency = eventRecord.Currency; } if (eventRecord.Value.HasValue) { eventBuilder.ConversionValue = eventRecord.Value.Value; } var userDataBuilder = new UserData(); // Adds a UserIdentifier for each valid email address for the eventRecord. if (eventRecord.Emails != null) { foreach (var email in eventRecord.Emails) { try { string preparedEmail = userDataFormatter.ProcessEmailAddress( email, UserDataFormatter.Encoding.Hex ); // Adds an email address identifier with the encoded email hash. userDataBuilder.UserIdentifiers.Add( new UserIdentifier { EmailAddress = preparedEmail } ); } catch (ArgumentException) { // Skips invalid input. continue; } } } // Adds a UserIdentifier for each valid phone number for the eventRecord. if (eventRecord.PhoneNumbers != null) { foreach (var phoneNumber in eventRecord.PhoneNumbers) { try { string preparedPhoneNumber = userDataFormatter.ProcessPhoneNumber( phoneNumber, UserDataFormatter.Encoding.Hex ); // Adds a phone number identifier with the encoded phone hash. userDataBuilder.UserIdentifiers.Add( new UserIdentifier { PhoneNumber = preparedPhoneNumber } ); } catch (ArgumentException) { // Skips invalid input. continue; } } } if (userDataBuilder.UserIdentifiers.Any()) { eventBuilder.UserData = userDataBuilder; } events.Add(eventBuilder); } // Builds the Destination for the request. var destinationBuilder = new Destination { OperatingAccount = new ProductAccount { AccountType = operatingAccountType, AccountId = operatingAccountId, }, ProductDestinationId = conversionActionId, }; if (loginAccountType.HasValue && loginAccountId != null) { destinationBuilder.LoginAccount = new ProductAccount { AccountType = loginAccountType.Value, AccountId = loginAccountId, }; } if (linkedAccountType.HasValue && linkedAccountId != null) { destinationBuilder.LinkedAccount = new ProductAccount { AccountType = linkedAccountType.Value, AccountId = linkedAccountId, }; } IngestionServiceClient ingestionServiceClient = IngestionServiceClient.Create(); int requestCount = 0; // Batches requests to send up to the maximum number of events per request. for (var i = 0; i < events.Count; i += MaxEventsPerRequest) { IEnumerable<Event> batch = events.Skip(i).Take(MaxEventsPerRequest); requestCount++; var request = new IngestEventsRequest { Destinations = { destinationBuilder }, // Adds events from the current batch. Events = { batch }, Consent = new Consent { AdPersonalization = ConsentStatus.ConsentGranted, AdUserData = ConsentStatus.ConsentGranted, }, // Sets validate_only. If true, then the Data Manager API only validates the // request but doesn't apply changes. ValidateOnly = validateOnly, Encoding = V1.Encoding.Hex, }; // Sends the data to the Data Manager API. IngestEventsResponse response = ingestionServiceClient.IngestEvents(request); Console.WriteLine($"Response for request #{requestCount}:\n{response}"); } Console.WriteLine($"# of requests sent: {requestCount}"); } private class EventRecord { public List<string>? Emails { get; set; } public List<string>? PhoneNumbers { get; set; } public string? Timestamp { get; set; } public string? TransactionId { get; set; } public string? EventSource { get; set; } public double? Value { get; set; } public string? Currency { get; set; } public string? Gclid { get; set; } } private List<EventRecord> ReadEventData(string jsonFile) { string jsonString = File.ReadAllText(jsonFile); var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; return JsonSerializer.Deserialize<List<EventRecord>>(jsonString, options) ?? new List<EventRecord>(); } } }
جاوا
// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.ads.datamanager.samples; import com.beust.jcommander.Parameter; import com.google.ads.datamanager.samples.common.BaseParamsConfig; import com.google.ads.datamanager.util.UserDataFormatter; import com.google.ads.datamanager.util.UserDataFormatter.Encoding; import com.google.ads.datamanager.v1.AdIdentifiers; import com.google.ads.datamanager.v1.Consent; import com.google.ads.datamanager.v1.ConsentStatus; import com.google.ads.datamanager.v1.Destination; import com.google.ads.datamanager.v1.Event; import com.google.ads.datamanager.v1.EventSource; import com.google.ads.datamanager.v1.IngestEventsRequest; import com.google.ads.datamanager.v1.IngestEventsResponse; import com.google.ads.datamanager.v1.IngestionServiceClient; import com.google.ads.datamanager.v1.ProductAccount; import com.google.ads.datamanager.v1.ProductAccount.AccountType; import com.google.ads.datamanager.v1.UserData; import com.google.ads.datamanager.v1.UserIdentifier; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.reflect.TypeToken; import com.google.gson.GsonBuilder; import com.google.protobuf.util.Timestamps; import java.io.BufferedReader; import java.io.IOException; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.text.ParseException; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; /** * Sends an {@link IngestEventsRequest} without using encryption. * * <p>Event data is read from a data file. See the {@code events_1.json} file in the {@code * resources/sampledata} directory for a sample file. */ public class IngestEvents { private static final Logger LOGGER = Logger.getLogger(IngestEvents.class.getName()); /** The maximum number of events allowed per request. */ private static final int MAX_EVENTS_PER_REQUEST = 2_000; private static final class ParamsConfig extends BaseParamsConfig<ParamsConfig> { @Parameter( names = "--operatingAccountType", required = true, description = "Account type of the operating account") AccountType operatingAccountType; @Parameter( names = "--operatingAccountId", required = true, description = "ID of the operating account") String operatingAccountId; @Parameter( names = "--loginAccountType", required = false, description = "Account type of the login account") AccountType loginAccountType; @Parameter( names = "--loginAccountId", required = false, description = "ID of the login account") String loginAccountId; @Parameter( names = "--linkedAccountType", required = false, description = "Account type of the linked account") AccountType linkedAccountType; @Parameter( names = "--linkedAccountId", required = false, description = "ID of the linked account") String linkedAccountId; @Parameter( names = "--conversionActionId", required = true, description = "ID of the conversion action") String conversionActionId; @Parameter( names = "--jsonFile", required = true, description = "JSON file containing user data to ingest") String jsonFile; @Parameter( names = "--validateOnly", required = false, arity = 1, description = "Whether to enable validateOnly on the request") boolean validateOnly = true; } public static void main(String[] args) throws IOException { ParamsConfig paramsConfig = new ParamsConfig().parseOrExit(args); if ((paramsConfig.loginAccountId == null) != (paramsConfig.loginAccountType == null)) { throw new IllegalArgumentException( "Must specify either both or neither of login account ID and login account type"); } if ((paramsConfig.linkedAccountId == null) != (paramsConfig.linkedAccountType == null)) { throw new IllegalArgumentException( "Must specify either both or neither of linked account ID and linked account type"); } new IngestEvents().runExample(paramsConfig); } /** * Runs the example. This sample assumes that the login and operating account are the same. * * @param params the parameters for the example */ private void runExample(ParamsConfig params) throws IOException { // Reads event data from the JSON file. List<EventRecord> eventRecords = readEventData(params.jsonFile); // Gets an instance of the UserDataFormatter for normalizing and formatting the data. UserDataFormatter userDataFormatter = UserDataFormatter.create(); // Builds the events collection for the request. List<Event> events = new ArrayList<>(); for (EventRecord eventRecord : eventRecords) { Event.Builder eventBuilder = Event.newBuilder(); try { eventBuilder.setEventTimestamp(Timestamps.parse(eventRecord.timestamp)); } catch (ParseException pe) { LOGGER.warning( () -> String.format("Skipping event with invalid timestamp: %s", eventRecord.timestamp)); continue; } if (Strings.isNullOrEmpty(eventRecord.transactionId)) { LOGGER.warning("Skipping event with no transaction ID"); continue; } eventBuilder.setTransactionId(eventRecord.transactionId); if (!Strings.isNullOrEmpty(eventRecord.eventSource)) { try { eventBuilder.setEventSource(EventSource.valueOf(eventRecord.eventSource)); } catch (IllegalArgumentException iae) { LOGGER.warning("Skipping event with invalid event source: " + eventRecord.eventSource); continue; } } if (!Strings.isNullOrEmpty(eventRecord.gclid)) { eventBuilder.setAdIdentifiers(AdIdentifiers.newBuilder().setGclid(eventRecord.gclid)); } if (!Strings.isNullOrEmpty(eventRecord.currency)) { eventBuilder.setCurrency(eventRecord.currency); } if (eventRecord.value != null) { eventBuilder.setConversionValue(eventRecord.value); } UserData.Builder userDataBuilder = UserData.newBuilder(); // Adds a UserIdentifier for each valid email address for the eventRecord. if (eventRecord.emails != null) { for (String email : eventRecord.emails) { String preparedEmail; try { preparedEmail = userDataFormatter.processEmailAddress(email, Encoding.HEX); } catch (IllegalArgumentException iae) { // Skips invalid input. continue; } // Sets the email address identifier to the encoded email hash. userDataBuilder.addUserIdentifiers( UserIdentifier.newBuilder().setEmailAddress(preparedEmail)); } } // Adds a UserIdentifier for each valid phone number for the eventRecord. if (eventRecord.phoneNumbers != null) { for (String phoneNumber : eventRecord.phoneNumbers) { String preparedPhoneNumber; try { preparedPhoneNumber = userDataFormatter.processPhoneNumber(phoneNumber, Encoding.HEX); } catch (IllegalArgumentException iae) { // Skips invalid input. continue; } // Sets the phone number identifier to the encoded phone number hash. userDataBuilder.addUserIdentifiers( UserIdentifier.newBuilder().setPhoneNumber(preparedPhoneNumber)); } } if (userDataBuilder.getUserIdentifiersCount() > 0) { eventBuilder.setUserData(userDataBuilder); } events.add(eventBuilder.build()); } // Builds the Destination for the request. Destination.Builder destinationBuilder = Destination.newBuilder() .setOperatingAccount( ProductAccount.newBuilder() .setAccountType(params.operatingAccountType) .setAccountId(params.operatingAccountId)) .setProductDestinationId(params.conversionActionId); if (params.loginAccountType != null && params.loginAccountId != null) { destinationBuilder.setLoginAccount( ProductAccount.newBuilder() .setAccountType(params.loginAccountType) .setAccountId(params.loginAccountId)); } if (params.linkedAccountType != null && params.linkedAccountId != null) { destinationBuilder.setLinkedAccount( ProductAccount.newBuilder() .setAccountType(params.linkedAccountType) .setAccountId(params.linkedAccountId)); } try (IngestionServiceClient ingestionServiceClient = IngestionServiceClient.create()) { int requestCount = 0; // Batches requests to send up to the maximum number of events per request. for (List<Event> eventsBatch : Lists.partition(events, MAX_EVENTS_PER_REQUEST)) { requestCount++; // Builds the request. IngestEventsRequest request = IngestEventsRequest.newBuilder() .addDestinations(destinationBuilder) // Adds events from the current batch. .addAllEvents(eventsBatch) .setConsent( Consent.newBuilder() .setAdPersonalization(ConsentStatus.CONSENT_GRANTED) .setAdUserData(ConsentStatus.CONSENT_GRANTED)) // Sets validate_only. If true, then the Data Manager API only validates the request // but doesn't apply changes. .setValidateOnly(params.validateOnly) // Sets encoding to match the encoding used. .setEncoding(com.google.ads.datamanager.v1.Encoding.HEX) .build(); LOGGER.info(() -> String.format("Request:%n%s", request)); IngestEventsResponse response = ingestionServiceClient.ingestEvents(request); LOGGER.info(String.format("Response for request #:%n%s", requestCount, response)); } LOGGER.info("# of requests sent: " + requestCount); } } /** Data object for a single row of input data. */ @SuppressWarnings("unused") private static class EventRecord { private List<String> emails; private List<String> phoneNumbers; private String timestamp; private String transactionId; private String eventSource; private Double value; private String currency; private String gclid; } /** Reads the data file and parses each line into a {@link EventRecord} object. */ private List<EventRecord> readEventData(String jsonFile) throws IOException { try (BufferedReader jsonReader = Files.newBufferedReader(Paths.get(jsonFile), StandardCharsets.UTF_8)) { // Define the type for Gson to deserialize into (List of EventRecord objects) Type recordListType = new TypeToken<ArrayList<EventRecord>>() {}.getType(); // Parse the JSON string from the file into a List of EventRecord objects return new GsonBuilder().create().fromJson(jsonReader, recordListType); } } }
گره
#!/usr/bin/env node // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. 'use strict'; import {IngestionServiceClient} from '@google-ads/datamanager'; import {protos} from '@google-ads/datamanager'; const { Event: DataManagerEvent, Destination, Encoding: DataManagerEncoding, EventSource, Consent, ConsentStatus, IngestEventsRequest, ProductAccount, UserData, UserIdentifier, } = protos.google.ads.datamanager.v1; import {UserDataFormatter, Encoding} from '@google-ads/data-manager-util'; import * as fs from 'fs'; import * as yargs from 'yargs'; const MAX_EVENTS_PER_REQUEST = 10000; interface Arguments { operating_account_type: string; operating_account_id: string; conversion_action_id: string; json_file: string; validate_only: boolean; login_account_type?: string; login_account_id?: string; linked_account_type?: string; linked_account_id?: string; [x: string]: unknown; } interface EventRow { timestamp: string; transactionId: string; eventSource?: string; gclid?: string; currency?: string; value?: number; emails?: string[]; phoneNumbers?: string[]; } /** * The main function for the IngestEvents sample. */ async function main() { const argv: Arguments = yargs .option('operating_account_type', { describe: 'The account type of the operating account.', type: 'string', required: true, }) .option('operating_account_id', { describe: 'The ID of the operating account.', type: 'string', required: true, }) .option('conversion_action_id', { describe: 'The ID of the conversion action.', type: 'string', required: true, }) .option('json_file', { describe: 'JSON file containing user data to ingest.', type: 'string', required: true, }) .option('validate_only', { describe: 'Whether to enable validate_only on the request.', type: 'boolean', default: true, }) .option('login_account_type', { describe: 'The account type of the login account.', type: 'string', }) .option('login_account_id', { describe: 'The ID of the login account.', type: 'string', }) .option('linked_account_type', { describe: 'The account type of the linked account.', type: 'string', }) .option('linked_account_id', { describe: 'The ID of the linked account.', type: 'string', }) .option('config', { describe: 'Path to a JSON file with arguments.', type: 'string', }) .config('config') .check((args: Arguments) => { if ( (args.login_account_type && !args.login_account_id) || (!args.login_account_type && args.login_account_id) ) { throw new Error( 'Must specify either both or neither of login account type ' + 'and login account ID', ); } if ( (args.linked_account_type && !args.linked_account_id) || (!args.linked_account_type && args.linked_account_id) ) { throw new Error( 'Must specify either both or neither of linked account type ' + 'and linked account ID', ); } return true; }) .parseSync(); // Reads event data from the JSON file. const eventRows: EventRow[] = readEventDataFile(argv.json_file); // Builds the events collection for the request. const events = []; const formatter = new UserDataFormatter(); for (const eventRow of eventRows) { const event = DataManagerEvent.create(); try { const date = new Date(eventRow.timestamp); event.eventTimestamp = { seconds: Math.floor(date.getTime() / 1000), nanos: (date.getTime() % 1000) * 1e6, }; } catch (e) { console.warn( `Invalid timestamp format: ${eventRow.timestamp}. Skipping row.`, ); continue; } if (!eventRow.transactionId) { console.warn('Skipping event with no transaction ID'); continue; } event.transactionId = eventRow.transactionId; if (eventRow.eventSource) { const eventSourceEnumValue: number | undefined = EventSource[eventRow.eventSource as keyof typeof EventSource]; if (eventSourceEnumValue === undefined) { console.warn( `Skipping event with invalid event_source: ${eventRow.eventSource}`, ); continue; } event.eventSource = eventSourceEnumValue; } if (eventRow.gclid) { event.adIdentifiers = {gclid: eventRow.gclid}; } if (eventRow.currency) { event.currency = eventRow.currency; } if (eventRow.value) { event.conversionValue = eventRow.value; } const userData = UserData.create(); // Adds a UserIdentifier for each valid email address for the eventRecord. if (eventRow.emails) { for (const email of eventRow.emails) { try { const processedEmail = formatter.processEmailAddress( email, Encoding.HEX, ); userData.userIdentifiers.push( UserIdentifier.create({emailAddress: processedEmail}), ); } catch (e) { console.warn(`Invalid email address: ${email}. Skipping.`); } } } // Adds a UserIdentifier for each valid phone number for the eventRecord. if (eventRow.phoneNumbers) { for (const phoneNumber of eventRow.phoneNumbers) { try { const processedPhone = formatter.processPhoneNumber( phoneNumber, Encoding.HEX, ); userData.userIdentifiers.push( UserIdentifier.create({phoneNumber: processedPhone}), ); } catch (e) { console.warn(`Invalid phone: ${phoneNumber}. Skipping.`); } } } if (userData.userIdentifiers.length > 0) { event.userData = userData; } events.push(event); } // Sets up the Destination. const operatingAccountType = convertToAccountType( argv.operating_account_type, 'operating_account_type', ); const destination = Destination.create({ operatingAccount: ProductAccount.create({ accountType: operatingAccountType, accountId: argv.operating_account_id, }), productDestinationId: argv.conversion_action_id, }); // The login account is optional. if (argv.login_account_type) { const loginAccountType = convertToAccountType( argv.login_account_type, 'login_account_type', ); destination.loginAccount = ProductAccount.create({ accountType: loginAccountType, accountId: argv.login_account_id, }); } // The linked account is optional. if (argv.linked_account_type) { const linkedAccountType = convertToAccountType( argv.linked_account_type, 'linked_account_type', ); destination.linkedAccount = ProductAccount.create({ accountType: linkedAccountType, accountId: argv.linked_account_id, }); } const client = new IngestionServiceClient(); let requestCount = 0; // Batches requests to send up to the maximum number of events per request. for (let i = 0; i < events.length; i += MAX_EVENTS_PER_REQUEST) { requestCount++; const eventsBatch = events.slice(i, i + MAX_EVENTS_PER_REQUEST); // Builds the request. const request = IngestEventsRequest.create({ destinations: [destination], // Adds events from the current batch. events: eventsBatch, consent: Consent.create({ adUserData: ConsentStatus.CONSENT_GRANTED, adPersonalization: ConsentStatus.CONSENT_GRANTED, }), // Sets encoding to match the encoding used. encoding: DataManagerEncoding.HEX, // Sets validate_only. If true, then the Data Manager API only validates the request validateOnly: argv.validate_only, }); const [response] = await client.ingestEvents(request); console.log(`Response for request #${requestCount}:\n`, response); } console.log(`# of requests sent: ${requestCount}`); } /** * Reads the event data from the given JSON file. * @param {string} jsonFile The path to the JSON file. * @return {EventRow[]} An array of event data. */ function readEventDataFile(jsonFile: string): EventRow[] { const fileContent = fs.readFileSync(jsonFile, 'utf8'); return JSON.parse(fileContent); } /** * Validates that a given string is an enum value for the AccountType enum, and * if validation passes, returns the AccountType enum value. * @param proposedValue the name of an AccountType enum value * @param paramName the name of the parameter to use in the error message if validation fails * @returns {protos.google.ads.datamanager.v1.ProductAccount.AccountType} The corresponding enum value. * @throws {Error} If the string is not an AccountType enum value. */ function convertToAccountType( proposedValue: string, paramName: string, ): protos.google.ads.datamanager.v1.ProductAccount.AccountType { const AccountType = ProductAccount.AccountType; const accountTypeEnumNames = Object.keys(AccountType).filter(key => isNaN(Number(key)), ); if (!accountTypeEnumNames.includes(proposedValue)) { throw new Error(`Invalid ${paramName}: ${proposedValue}`); } return AccountType[proposedValue as keyof typeof AccountType]; } if (require.main === module) { main().catch(console.error); }
پی اچ پی
<?php // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * Sample of sending an IngestEventsRequest without encryption. */ require_once dirname(__DIR__, 1) . '/vendor/autoload.php'; use Google\Ads\DataManager\V1\AdIdentifiers; use Google\Ads\DataManager\V1\Client\IngestionServiceClient; use Google\Ads\DataManager\V1\Consent; use Google\Ads\DataManager\V1\ConsentStatus; use Google\Ads\DataManager\V1\Destination; use Google\Ads\DataManager\V1\Encoding as DataManagerEncoding; use Google\Ads\DataManager\V1\Event; use Google\Ads\DataManager\V1\EventSource; use Google\Ads\DataManager\V1\IngestEventsRequest; use Google\Ads\DataManager\V1\ProductAccount; use Google\Ads\DataManager\V1\ProductAccount\AccountType; use Google\Ads\DataManager\V1\UserData; use Google\Ads\DataManager\V1\UserIdentifier; use Google\Ads\DataManagerUtil\Encoding; use Google\Ads\DataManagerUtil\Formatter; use Google\ApiCore\ApiException; use Google\Protobuf\Timestamp; // The maximum number of events allowed per request. const MAX_EVENTS_PER_REQUEST = 2000; /** * Reads the JSON-formatted event data file. * * @param string $jsonFile The event data file. * @return array A list of associative arrays, each representing an event. */ function readEventDataFile(string $jsonFile): array { $jsonContent = file_get_contents($jsonFile); if ($jsonContent === false) { throw new \RuntimeException(sprintf('Could not read JSON file: %s', $jsonFile)); } $events = json_decode($jsonContent, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new \RuntimeException(sprintf('Invalid JSON in file: %s', $jsonFile)); } return $events; } /** * Runs the sample. * * @param int $operatingAccountType The account type of the operating account. * @param string $operatingAccountId The ID of the operating account. * @param string $conversionActionId The ID of the conversion action. * @param string $jsonFile The JSON file containing event data. * @param bool $validateOnly Whether to enable validateOnly on the request. * @param int|null $loginAccountType The account type of the login account. * @param string|null $loginAccountId The ID of the login account. * @param int|null $linkedAccountType The account type of the linked account. * @param string|null $linkedAccountId The ID of the linked account. */ function main( int $operatingAccountType, string $operatingAccountId, string $conversionActionId, string $jsonFile, bool $validateOnly, ?int $loginAccountType = null, ?string $loginAccountId = null, ?int $linkedAccountType = null, ?string $linkedAccountId = null ): void { // Reads event data from the data file. $eventRecords = readEventDataFile($jsonFile); // Gets an instance of the UserDataFormatter for normalizing and formatting the data. $formatter = new Formatter(); // Builds the events collection for the request. $events = []; foreach ($eventRecords as $eventRecord) { $event = new Event(); if (empty($eventRecord['timestamp'])) { error_log('Skipping event with no timestamp.'); continue; } try { $dateTime = new DateTime($eventRecord['timestamp']); $timestamp = new Timestamp(); $timestamp->fromDateTime($dateTime); $event->setEventTimestamp($timestamp); } catch (\Exception $e) { error_log(sprintf('Skipping event with invalid timestamp: %s', $eventRecord['timestamp'])); continue; } if (empty($eventRecord['transactionId'])) { error_log('Skipping event with no transaction ID'); continue; } $event->setTransactionId($eventRecord['transactionId']); if (!empty($eventRecord['eventSource'])) { try { $event->setEventSource(EventSource::value($eventRecord['eventSource'])); } catch (\UnexpectedValueException $e) { error_log('Skipping event with invalid event source: ' . $eventRecord['eventSource']); continue; } } if (!empty($eventRecord['gclid'])) { $event->setAdIdentifiers((new AdIdentifiers())->setGclid($eventRecord['gclid'])); } if (!empty($eventRecord['currency'])) { $event->setCurrency($eventRecord['currency']); } if (isset($eventRecord['value'])) { $event->setConversionValue($eventRecord['value']); } $userData = new UserData(); $identifiers = []; if (!empty($eventRecord['emails'])) { foreach ($eventRecord['emails'] as $email) { try { $preparedEmail = $formatter->processEmailAddress($email, Encoding::Hex); $identifiers[] = (new UserIdentifier())->setEmailAddress($preparedEmail); } catch (\InvalidArgumentException $e) { // Skips invalid input. error_log(sprintf('Skipping invalid email: %s', $e->getMessage())); continue; } } } if (!empty($eventRecord['phoneNumbers'])) { foreach ($eventRecord['phoneNumbers'] as $phoneNumber) { try { $preparedPhoneNumber = $formatter->processPhoneNumber($phoneNumber, Encoding::Hex); $identifiers[] = (new UserIdentifier())->setPhoneNumber($preparedPhoneNumber); } catch (\InvalidArgumentException $e) { // Skips invalid input. error_log(sprintf('Skipping invalid phone number: %s', $e->getMessage())); continue; } } } if (!empty($identifiers)) { $userData->setUserIdentifiers($identifiers); $event->setUserData($userData); } $events[] = $event; } // Builds the destination for the request. $destination = (new Destination()) ->setOperatingAccount((new ProductAccount()) ->setAccountType($operatingAccountType) ->setAccountId($operatingAccountId)) ->setProductDestinationId($conversionActionId); if ($loginAccountType !== null && $loginAccountId !== null) { $destination->setLoginAccount((new ProductAccount()) ->setAccountType($loginAccountType) ->setAccountId($loginAccountId)); } if ($linkedAccountType !== null && $linkedAccountId !== null) { $destination->setLinkedAccount((new ProductAccount()) ->setAccountType($linkedAccountType) ->setAccountId($linkedAccountId)); } $client = new IngestionServiceClient(); try { $requestCount = 0; // Batches requests to send up to the maximum number of events per request. foreach (array_chunk($events, MAX_EVENTS_PER_REQUEST) as $eventsBatch) { $requestCount++; // Builds the request. $request = (new IngestEventsRequest()) ->setDestinations([$destination]) ->setEvents($eventsBatch) ->setConsent((new Consent()) ->setAdUserData(ConsentStatus::CONSENT_GRANTED) ->setAdPersonalization(ConsentStatus::CONSENT_GRANTED) ) ->setValidateOnly($validateOnly) ->setEncoding(DataManagerEncoding::HEX); echo "Request:\n" . json_encode(json_decode($request->serializeToJsonString()), JSON_PRETTY_PRINT) . "\n"; $response = $client->ingestEvents($request); echo "Response for request #{$requestCount}:\n" . json_encode(json_decode($response->serializeToJsonString()), JSON_PRETTY_PRINT) . "\n"; } echo "# of requests sent: {$requestCount}\n"; } catch (ApiException $e) { echo 'Error sending request: ' . $e->getMessage() . "\n"; } finally { $client->close(); } } // Command-line argument parsing $options = getopt( '', [ 'operating_account_type:', 'operating_account_id:', 'login_account_type::', 'login_account_id::', 'linked_account_type::', 'linked_account_id::', 'conversion_action_id:', 'json_file:', 'validate_only::' ] ); $operatingAccountType = $options['operating_account_type'] ?? null; $operatingAccountId = $options['operating_account_id'] ?? null; $conversionActionId = $options['conversion_action_id'] ?? null; $jsonFile = $options['json_file'] ?? null; // Only validates requests by default. $validateOnly = true; if (array_key_exists('validate_only', $options)) { $value = $options['validate_only']; // `getopt` with `::` returns boolean `false` if the option is passed without a value. if ($value === false || !in_array($value, ['true', 'false'], true)) { echo "Error: --validate_only requires a value of 'true' or 'false'.\n"; exit(1); } $validateOnly = ($value === 'true'); } if (empty($operatingAccountType) || empty($operatingAccountId) || empty($conversionActionId) || empty($jsonFile)) { echo 'Usage: php ingest_events.php ' . '--operating_account_type=<account_type> ' . '--operating_account_id=<account_id> ' . '--conversion_action_id=<conversion_action_id> ' . "--json_file=<path_to_json>\n" . 'Optional: --login_account_type=<account_type> --login_account_id=<account_id> ' . '--linked_account_type=<account_type> --linked_account_id=<account_id> ' . "--validate_only=<true|false>\n"; exit(1); } // Converts the operating account type string to an AccountType enum. $parsedOperatingAccountType = AccountType::value($operatingAccountType); if (isset($options['login_account_type']) != isset($options['login_account_id'])) { throw new \InvalidArgumentException( 'Must specify either both or neither of login account type and login account ID' ); } $parsedLoginAccountType = null; if (isset($options['login_account_type'])) { // Converts the login account type string to an AccountType enum. $parsedLoginAccountType = AccountType::value($options['login_account_type']); } if (isset($options['linked_account_type']) != isset($options['linked_account_id'])) { throw new \InvalidArgumentException( 'Must specify either both or neither of linked account type and linked account ID' ); } $parsedLinkedAccountType = null; if (isset($options['linked_account_type'])) { // Converts the linked account type string to an AccountType enum. $parsedLinkedAccountType = AccountType::value($options['linked_account_type']); } main( $parsedOperatingAccountType, $operatingAccountId, $conversionActionId, $jsonFile, $validateOnly, $parsedLoginAccountType, $options['login_account_id'] ?? null, $parsedLinkedAccountType, $options['linked_account_id'] ?? null );
پایتون
#!/usr/bin/env python # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Sample of sending an IngestEventsRequest without encryption.""" import argparse import json import logging from typing import Any, Dict, List, Optional from google.ads import datamanager_v1 from google.ads.datamanager_util import Formatter from google.ads.datamanager_util.format import Encoding from google.protobuf.timestamp_pb2 import Timestamp _logger = logging.getLogger(__name__) # The maximum number of events allowed per request. _MAX_EVENTS_PER_REQUEST = 10_000 def main( operating_account_type: datamanager_v1.ProductAccount.AccountType, operating_account_id: str, conversion_action_id: str, json_file: str, validate_only: bool, login_account_type: Optional[ datamanager_v1.ProductAccount.AccountType ] = None, login_account_id: Optional[str] = None, linked_account_type: Optional[ datamanager_v1.ProductAccount.AccountType ] = None, linked_account_id: Optional[str] = None, ) -> None: """Runs the sample. Args: operating_account_type: the account type of the operating account. operating_account_id: the ID of the operating account. json_file: the JSON file containing event data. validate_only: whether to enable validate_only on the request. login_account_type: the account type of the login account. login_account_id: the ID of the login account. linked_account_type: the account type of the linked account. linked_account_id: the ID of the linked account. """ # Gets an instance of the formatter. formatter: Formatter = Formatter() # Reads the input file. event_rows: List[Dict[str, Any]] = read_event_data_file(json_file) events: List[datamanager_v1.Event] = [] for event_row in event_rows: event = datamanager_v1.Event() try: event_timestamp = Timestamp() event_timestamp.FromJsonString(str(event_row["timestamp"])) event.event_timestamp = event_timestamp except ValueError: _logger.warning( "Invalid timestamp format: %s. Skipping row.", event_row["timestamp"], ) continue if "transactionId" not in event_row: _logger.warning("Skipping event with no transaction ID") continue event.transaction_id = event_row["transactionId"] if "eventSource" in event_row: event.event_source = event_row["eventSource"] if "gclid" in event_row: event.ad_identifiers = datamanager_v1.AdIdentifiers( gclid=event_row["gclid"] ) if "currency" in event_row: event.currency = event_row["currency"] if "value" in event_row: event.conversion_value = event_row["value"] user_data = datamanager_v1.UserData() # Adds a UserIdentifier for each valid email address for the event row. if "emails" in event_row: for email in event_row["emails"]: try: processed_email: str = formatter.process_email_address( email, Encoding.HEX ) user_data.user_identifiers.append( datamanager_v1.UserIdentifier( email_address=processed_email ) ) except ValueError: # Skips invalid input. _logger.warning( "Invalid email address: %s. Skipping.", event_row["email_address"], ) # Adds a UserIdentifier for each valid phone number for the event row. if "phoneNumbers" in event_row: for phone_number in event_row["phoneNumbers"]: try: processed_phone: str = formatter.process_phone_number( phone_number, Encoding.HEX ) user_data.user_identifiers.append( datamanager_v1.UserIdentifier( phone_number=processed_phone ) ) except ValueError: # Skips invalid input. _logger.warning( "Invalid phone: %s. Skipping.", event_row["phone_number"], ) if user_data.user_identifiers: event.user_data = user_data # Adds the event to the list of events to send in the request. events.append(event) # Configures the destination. destination: datamanager_v1.Destination = datamanager_v1.Destination() destination.operating_account.account_type = operating_account_type destination.operating_account.account_id = operating_account_id destination.product_destination_id = str(conversion_action_id) if login_account_type or login_account_id: if bool(login_account_type) != bool(login_account_id): raise ValueError( "Must specify either both or neither of login " + "account type and login account ID" ) destination.login_account.account_type = login_account_type destination.login_account.account_id = login_account_id if linked_account_type or linked_account_id: if bool(linked_account_type) != bool(linked_account_id): raise ValueError( "Must specify either both or neither of linked account " + "type and linked account ID" ) destination.linked_account.account_type = linked_account_type destination.linked_account.account_id = linked_account_id # Creates a client for the ingestion service. client: datamanager_v1.IngestionServiceClient = ( datamanager_v1.IngestionServiceClient() ) # Batches requests to send up to the maximum number of events per # request. request_count = 0 for i in range(0, len(events), _MAX_EVENTS_PER_REQUEST): request_count += 1 events_batch = events[i : i + _MAX_EVENTS_PER_REQUEST] # Sends the request. request: datamanager_v1.IngestEventsRequest = ( datamanager_v1.IngestEventsRequest( destinations=[destination], # Adds events from the current batch. events=events_batch, consent=datamanager_v1.Consent( ad_user_data=datamanager_v1.ConsentStatus.CONSENT_GRANTED, ad_personalization=datamanager_v1.ConsentStatus.CONSENT_GRANTED, ), # Sets encoding to match the encoding used. encoding=datamanager_v1.Encoding.HEX, # Sets validate_only. If true, then the Data Manager API only # validates the request but doesn't apply changes. validate_only=validate_only, ) ) # Sends the request. response: datamanager_v1.IngestEventsResponse = client.ingest_events( request=request ) # Logs the response. _logger.info("Response for request #%d:\n%s", request_count, response) _logger.info("# of requests sent: %d", request_count) def read_event_data_file(json_file: str) -> List[Dict[str, Any]]: """Reads the JSON-formatted event data file. Args: json_file: the event data file. """ with open(json_file, "r") as f: return json.load(f) if __name__ == "__main__": # Configures logging. logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser( description=("Sends events from a JSON file to a destination."), fromfile_prefix_chars="@", ) # The following argument(s) should be provided to run the example. parser.add_argument( "--operating_account_type", type=str, required=True, help="The account type of the operating account.", ) parser.add_argument( "--operating_account_id", type=str, required=True, help="The ID of the operating account.", ) parser.add_argument( "--conversion_action_id", type=int, required=True, help="The ID of the conversion action", ) parser.add_argument( "--login_account_type", type=str, required=False, help="The account type of the login account.", ) parser.add_argument( "--login_account_id", type=str, required=False, help="The ID of the login account.", ) parser.add_argument( "--linked_account_type", type=str, required=False, help="The account type of the linked account.", ) parser.add_argument( "--linked_account_id", type=str, required=False, help="The ID of the linked account.", ) parser.add_argument( "--json_file", type=str, required=True, help="JSON file containing user data to ingest.", ) parser.add_argument( "--validate_only", choices=["true", "false"], default="true", help="""Whether to enable validate_only on the request. Must be 'true' or 'false'. Defaults to 'true'.""", ) args = parser.parse_args() main( args.operating_account_type, args.operating_account_id, args.conversion_action_id, args.json_file, args.validate_only == "true", args.login_account_type, args.login_account_id, args.linked_account_type, args.linked_account_id, )
پاسخهای موفقیت
یک درخواست موفق، پاسخی را با یک شیء حاوی requestId برمیگرداند.
{
"requestId": "126365e1-16d0-4c81-9de9-f362711e250a"
}
requestId برگردانده شده را ثبت کنید تا بتوانید با پردازش هر مقصد در درخواست، عیبیابی را بازیابی کنید.
پاسخهای مربوط به شکست
یک درخواست ناموفق منجر به یک کد وضعیت پاسخ خطا مانند 400 Bad Request و پاسخی با جزئیات خطا میشود.
برای مثال، یک emailAddress که حاوی یک رشته متن ساده به جای یک مقدار کدگذاری شده هگز است، پاسخ زیر را تولید میکند:
{
"error": {
"code": 400,
"message": "There was a problem with the request.",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "INVALID_ARGUMENT",
"domain": "datamanager.googleapis.com"
},
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "events.events[0].user_data.user_identifiers",
"description": "Email is not hex encoded.",
"reason": "INVALID_HEX_ENCODING"
}
]
}
]
}
}
یک emailAddress که هش نشده و فقط به صورت هگز کدگذاری شده باشد، پاسخ زیر را تولید میکند:
{
"error": {
"code": 400,
"message": "There was a problem with the request.",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "INVALID_ARGUMENT",
"domain": "datamanager.googleapis.com"
},
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "events.events[0]",
"reason": "INVALID_SHA256_FORMAT"
}
]
}
]
}
}
ارسال رویدادها برای چندین مقصد
اگر دادههای شما حاوی رویدادهایی برای مقاصد مختلف است، میتوانید با استفاده از ارجاعات مقصد، آنها را در یک درخواست واحد ارسال کنید.
برای مثال، اگر رویدادی برای اقدام تبدیل با شناسه 123456789 و رویداد دیگری برای اقدام تبدیل با شناسه 777111122 دارید، هر دو رویداد را در یک درخواست واحد با تنظیم reference هر Destination ارسال کنید. reference توسط کاربر تعریف میشود. تنها شرط این است که هر Destination یک reference منحصر به فرد داشته باشد. در اینجا لیست destinations اصلاح شده برای درخواست آمده است:
"destinations": [
{
"operatingAccount": {
"accountType": "OPERATING_ACCOUNT_TYPE",
"accountId": "OPERATING_ACCOUNT_ID"
},
"loginAccount": {
"accountType": "LOGIN_ACCOUNT_TYPE",
"accountId": "LOGIN_ACCOUNT_ID"
},
"productDestinationId": "PRODUCT_DESTINATION_ID",
"reference": "destination_a"
},
{
"operatingAccount": {
"accountType": "OPERATING_ACCOUNT_2_TYPE",
"accountId": "OPERATING_ACCOUNT_2_ID"
},
"loginAccount": {
"accountType": "LOGIN_ACCOUNT_2_TYPE",
"accountId": "LOGIN_ACCOUNT_2_ID"
},
"productDestinationId": "777111122",
"reference": "destination_b"
}
]
destinationReferences هر Event را طوری تنظیم کنید که آن را به یک یا چند مقصد خاص ارسال کند. برای مثال، در اینجا Event داریم که فقط برای اولین Destination است، بنابراین لیست destinationReferences آن فقط شامل reference اولین Destination است:
{
"adIdentifiers": {
"gclid": "GCLID_1"
},
"conversionValue": 1.99,
"currency": "USD",
"eventTimestamp": "2025-06-10T20:07:01Z",
"transactionId": "ABC798654321",
"eventSource": "WEB",
"destinationReferences": [
"destination_a"
]
}
فیلد destinationReferences یک لیست است، بنابراین میتوانید چندین مقصد را برای یک رویداد مشخص کنید. اگر destinationReferences یک Event تنظیم نکنید، رابط برنامهنویسی کاربردی (API) مدیریت داده، رویداد را به تمام مقاصد موجود در درخواست ارسال میکند.
اگر یک رویداد چندین مقصد داشته باشد، رابط برنامهنویسی کاربردی (API) مدیریت داده، فیلدهای مرتبط را به هر مقصد ارسال میکند. برای مثال، اگر یک رویداد یک مقصد Google Ads و یک مقصد Google Analytics داشته باشد، API هنگام ارسال رویداد به مقصد Google Analytics، فیلدهای Google Analytics مانند clientId یا eventName را شامل میشود و هنگام ارسال رویداد به مقصد Google Ads، فیلدهای Google Ads مانند customVariables نیز شامل میشود.
مراحل بعدی
- احراز هویت را پیکربندی کنید و محیط خود را با یک کتابخانه کلاینت راهاندازی کنید.
- در مورد الزامات قالببندی، هش کردن و کدگذاری برای هر نوع داده اطلاعات کسب کنید.
- آموزش رمزگذاری دادههای کاربران
- یاد بگیرید چگونه برای درخواستهایتان ، تشخیصهای لازم را بازیابی کنید .
- درباره بهترین شیوهها اطلاعات کسب کنید.
- درباره محدودیتها و سهمیهها اطلاعات کسب کنید.