Вы можете пройти это краткое руководство, чтобы ознакомиться с отправкой данных о событиях.
Используйте API Data Manager в одном из следующих случаев:
Отправляйте данные о конверсиях из тегов Google Ads или событиях
purchaseиз Google Analytics в качестве дополнительного источника данных для анализа конверсий из тегов, чтобы максимизировать сигналы взаимодействия с объявлениями и улучшить качество данных и общую эффективность.Эта функция доступна для всех аккаунтов Google Ads, но только для ресурсов Google Analytics, включенных в список разрешенных. Заполните форму, если вы хотите добавить свой ресурс Google Analytics в список разрешенных.
Отправляйте данные о событиях для офлайн-конверсий в Google Ads или для расширенных конверсий лидов .
Выберите версию руководства, которую вы хотите просмотреть.
- Выберите «Рекламодатель», если вы используете учетные данные для аккаунта Google, который является пользователем в аккаунтах рекламодателей, которыми вы хотите управлять.
- Выберите «Партнер по данным», если вы используете учетные данные для аккаунта Google, который является пользователем в аккаунте партнера по данным, и хотите управлять аккаунтами рекламодателей, которые имеют партнерскую ссылку на аккаунт партнера по данным.
В этом кратком руководстве вы выполните следующие шаги:
- Подготовьте
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 . Более подробную информацию и примеры назначений для различных сценариев см. в разделе «Настройка назначений» .
-
operatingAccount Учетная запись, получающая события.
Для событий, отправляемых в качестве дополнительного источника данных, в качестве операционного аккаунта может использоваться либо аккаунт Google Ads, либо ресурс Google Analytics.
Если
accountTypeимеет значениеGOOGLE_ANALYTICS_PROPERTY, то учетные данные запроса должны принадлежать пользователю Google Analytics с ролью редактора или администратора для данного ресурса.Для офлайн-конверсий и повышения конверсии лидов операционный аккаунт должен быть аккаунтом Google Ads.
-
loginAccount - The account where the Google Account for the credentials is a user.
-
productDestinationId Идентификатор сущности в
operatingAccount, которая получает события.Для событий, отправляемых в качестве дополнительного источника данных,
productDestinationIdдолжен быть одним из следующих:Идентификатор конверсии Google Ads с
type, установленным наWEBPAGE. В пользовательском интерфейсе Google Ads источником конверсии для действия конверсииWEBPAGEявляется Website .Идентификатор измерения веб-потока Google Analytics. Вы не можете отправлять события в качестве дополнительного источника данных в поток приложения Google Analytics для iOS или Android.
Для офлайн-конверсий или расширенных конверсий для лидов,
productDestinationIdдолжен быть идентификатором действия конверсии Google Ads сtypeUPLOAD_CLICKS. В пользовательском интерфейсе Google Ads источником конверсии для действия конверсииUPLOAD_CLICKSявляется веб-сайт (импорт из кликов) .
В примере, приведенном в этом руководстве, показано, как составить запрос, который отправляет все события в один и тот же пункт назначения. Если вы хотите отправлять события в несколько пунктов назначения в одном запросе, см. раздел «Отправка событий в несколько пунктов назначения» .
Подготовка данных о событии
Рассмотрим следующие данные о событиях. Каждая таблица соответствует одному событию конверсии. Каждое событие конверсии содержит метку времени события, действие конверсии и значение конверсии.
Каждое событие может иметь идентификаторы рекламы, например, gclid , или идентификаторы пользователей, такие как адреса электронной почты, номера телефонов и адресная информация . Событие также может содержать:
- Информация о пользователе, оцениваемая в момент события , например, ценность клиента или то, является ли он новым, постоянным или возобновившим взаимодействие с компанией.
- Данные корзины покупок.
- Дополнительные параметры события или свойства пользователя для целевого объекта, такие как
client_idилиuser_idдля Google Analytics.
Вот данные о мероприятии:
Событие 1
| Событие №1 | |
|---|---|
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 |
Событие 2
| Событие №2 | |
|---|---|
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 |
Отформатируйте данные
Отформатируйте поля в соответствии с указаниями в руководстве по форматированию . Вот данные о событии после форматирования:
Событие 1
| Событие №1 | |
|---|---|
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 |
Событие 2
| Событие №2 | |
|---|---|
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 и закодированы с использованием шестнадцатеричного или Base64-кодирования. Вот данные события после форматирования, хеширования и кодирования с использованием шестнадцатеричного кодирования:
Событие 1
| Событие №1 | |
|---|---|
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 |
Событие 2
| Событие №2 | |
|---|---|
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равным времени, когда произошло событие.Для отслеживания событий в Google Analytics метка
eventTimestampдолжна быть указана в течение последних 72 часов.Укажите обязательные поля в соответствии с вашим сценарием использования.
Вариант использования Идентификаторы transactionIdeventSourceОфлайн-конверсии или улучшенные конверсии для лидов Обязательно . Укажите хотя бы одно из следующих значений: -
adIdentifiersу которых установлен хотя бы один из параметровgclid,gbraidилиwbraid - Атрибуты сессии
-
userData
Необязательный Обязательно . Установите одно из значений перечисления для EventSource.События отправляются в качестве дополнительного источника данных в целевую страницу Google Ads. Обязательно . Укажите хотя бы одно из следующих значений: -
adIdentifiersу которых установлен хотя бы один из параметровgclid,gbraidилиwbraid -
userData
Необходимый Необязательно. Если задано, должно быть WEB.События отправляются в качестве дополнительного источника данных в целевую систему Google Analytics. Обязательно . Укажите хотя бы одно из следующих значений: -
clientId -
adIdentifierswithgclidset -
userId
Необходимый Необязательно. Если задано, должно быть WEB.-
Если вы отправляете события в качестве дополнительного источника данных в целевую страницу Google Ads, ознакомьтесь с информацией о том, как Google обрабатывает данные из дополнительных источников .
Заполните все остальные поля, в которых у вас есть значение для события. Полный список доступных полей см. в справочной документации
Event.
Как Google обрабатывает дополнительные данные из источников.
В рамках одного и того же действия конверсии Google использует transactionId для удаления дубликатов событий конверсии, отправленных из разных источников (например, из тега вашего веб-сайта и запросов на получение данных через API Data Manager). В следующей таблице объясняется, как обрабатываются данные из ваших запросов на получение данных.
| Сценарий | Поле данных | Как это решается |
|---|---|---|
transactionId соответствует существующему тегу event | conversionValue (with currencyCode ) | Обновлено. Примечание: В течение первоначального 14-дневного пробного периода для действия конверсии обновление значений отключено. Значение тега не будет перезаписано в отчетах Google Ads до окончания пробного периода. |
transactionId соответствует существующему тегу event | Другие поля, кроме conversionValue или currencyCode (например, adIdentifiers.gclid ). | Игнорируется. Значения других полей из вашего дополнительного источника данных не перезапишут значения полей, первоначально записанные вашим тегом Google для соответствующих транзакций. |
transactionId НЕ соответствует ни одному существующему событию | Все предоставленные данные (например, userData , conversionValue , currencyCode ) | Используется для создания нового события конверсии. Затем Google попытается связать эту новую конверсию с кликом по объявлению, используя предоставленные вами идентификаторы (например, Примечание: В течение первоначального 14-дневного пробного периода эти вновь созданные конверсии будут отображаться в ваших отчетах, но не будут использоваться для назначения ставок. После окончания пробного периода они автоматически станут доступными для назначения ставок. |
Добавить атрибуты сессии
Если вы отправляете лиды для офлайн-конверсий или расширенных конверсий, добавьте атрибуты сессии , если другие идентификаторы объявлений, такие как GCLID или WBRAID, недоступны. Вы также можете включать атрибуты сессии в дополнение к другим идентификаторам объявлений.
Атрибуты сессии предоставляют дополнительный контекст и сигналы о взаимодействии пользователя с вашим веб-сайтом, что может повысить точность измерения конверсий, составления отчетов и назначения ставок.
In the Data Manager API, there are two approaches you can use to send session attributes:
Рекомендуется: установите поле
sessionAttributesобъектаadIdentifiersравным строке атрибутов сессии, закодированной в base64. Следуйте инструкциям в разделе «Как получить session_attributes» , чтобы изменить страницы отправки формы и настроить их на получение закодированной строки.Если вы не можете использовать JavaScript, сохраните отдельные поля атрибутов сессии и добавьте каждое из них в список
experimentalFieldsкак отдельноеExperimentalField:-
gad_campaignid -
session_start_time_usec -
gad_source -
landing_page_url -
landing_page_referrer
Если у вас есть значение для атрибута сессии
landing_page_user_agent, передайте его в полеuserAgentобъектаadIdentifiers.landingPageDeviceInfo.Вот лучшие практики при отправке отдельных пар ключ-значение:
- Постоянно отправляйте поля
gad_campaignidиsession_start_time_usec. Эти поля имеют решающее значение для точной атрибуции. - Не указывайте неточное или неполное значение
landing_page_urlнапример, строку-заполнитель, внутренний путь к приложению или неполный URL. Опуститеlanding_page_urlесли у вас нет точного полного URL.
Вот фрагмент примера события с записями в
experimentalFieldsдляgad_campaignidиsession_start_time_usec, а также пользовательским агентом в поле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.
Если в качестве дополнительного источника данных для события указаны свойства Google Analytics, заполните следующие поля, как указано:
-
eventName Обязательно . Название события Google Analytics.
-
transactionId Обязательно . Уникальный идентификатор события.
- По крайней мере один идентификатор
Необходимо заполнить хотя бы одно из следующих полей:
clientId: Уникальный идентификатор экземпляра пользователя веб-клиента. См. раздел «Отправка события в протокол измерения» .userId: Уникальный идентификатор пользователя. Дополнительную информацию см. в разделе « Измерение активности на разных платформах с помощью User-ID» .
-
destinationReferences Этот параметр необходим, если список
destinationsна уровне запроса содержит более одногоDestinationGoogle Analytics. Добавьте запись вdestinationReferences, чтобы указать, какой целевой адрес Google Analytics должен получить событие. Дополнительную информацию о ссылках на целевые адреса см. в разделе «Отправка событий в несколько целевых адресов» .Если
destinationReferencesне задан или содержит несколько записей, ссылающихся на целевые ресурсы Google Analytics, API Data Manager отклоняет событие с ошибкойMULTIPLE_DESTINATIONS_FOR_GOOGLE_ANALYTICS_EVENT.-
userId Необязательно. Идентификатор пользователя.
-
additionalEventParameters Необязательно, но рекомендуется. Заполните этот список любыми параметрами событий Google Analytics, которые не отображаются в других полях
Event. Параметры могут включать дополнительные рекомендуемые параметры из событияpurchaseили другие параметры, которые вы хотите отслеживать. Используйте имя параметра Google Analytics в качествеparameterNameдляEventParameter.Например, если к транзакции привязаны налоги, добавьте запись в
additionalEventParametersсparameterName, установленным наtax, иvalueравным стоимости налога.Мы не рекомендуем добавлять записи для параметров события Google Analytics
transactionId,currencyилиvalue. Вместо этого заполните поляtransactionId,currencyиconversionValueEvent, которые имеют приоритет над любыми записями вadditionalEventParameters.
Добавить данные корзины для событий покупки
Заполните поле cartData объекта Event информацией о приобретенных товарах. Для каждого приобретенного товара добавьте объект Item в список items поля CartData и заполните следующие поля, как указано:
-
itemId - Обязательно . Уникальный идентификатор товара.
-
unitPrice Обязательно . Цена за единицу товара без учета налогов, стоимости доставки и скидок, предоставляемых в рамках конкретного события (на уровне транзакции).
If the item has an item scoped discount, use the discounted unit price. For example, if an item has a unit price of
27.67and a unit discount of6.66, then setunitPriceto21.01.-
quantity Обязательно . Количество единиц, закупленных для данного товара.
-
additionalItemParameters Заполните этот список всеми параметрами, относящимися к конкретному элементу , которые не указаны в других полях
Item. Используйте имя параметра элемента Google Analytics в качествеparameterNameдля поляItemParameter.Например, если у вас есть марка и категория товара, добавьте в
additionalItemParametersэтого товара запись сparameterNameустановленным наitem_brand, иvalueравным названию марки, а также еще одну запись сparameterNameустановленным наitem_category, иvalueравным категории товара.Мы не рекомендуем добавлять записи для параметров товара Google Analytics
quantity,priceилиitem_id. Вместо этого заполнитеitemId,unitPriceиquantityItem, которые имеют приоритет над любыми записями вadditionalItemParameters.
Вот пример Event для отформатированных, хешированных и закодированных данных из второго события, с дополнительными данными для Google Analytics:
{
"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 и нажмите «Открыть в API Explorer» , чтобы открыть API Explorer в новой вкладке или окне.
- В теле запроса в API Explorer замените каждую строку, начинающуюся с
REPLACE_WITH, напримерREPLACE_WITH_OPERATING_ACCOUNT_TYPE, соответствующим значением. - Нажмите кнопку «Выполнить» в нижней части страницы 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>(); } } }
Java
// 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
<?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 );
Python
#!/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"
}
]
}
]
}
}
Отправка событий в несколько пунктов назначения
Если ваши данные содержат события для разных пунктов назначения, вы можете отправить их в одном запросе, используя ссылки на пункты назначения.
Например, если у вас есть событие для действия конверсии с ID 123456789 и другое событие для действия конверсии с ID 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 Data Manager отправит событие всем адресатам, указанным в запросе.
Если у события несколько пунктов назначения, API Data Manager отправляет соответствующие поля в каждый из них. Например, если у события есть пункт назначения Google Ads и пункт назначения Google Analytics, API включает поля Google Analytics, такие как clientId или eventName при отправке события в пункт назначения Google Analytics, и включает поля Google Ads, такие как customVariables при отправке события в пункт назначения Google Ads.
Следующие шаги
- Настройте аутентификацию и создайте среду с клиентской библиотекой.
- Изучите требования к форматированию, хешированию и кодированию для каждого типа данных.
- Узнайте, как зашифровать пользовательские данные .
- Узнайте, как получить диагностические данные для ваших запросов.
- Узнайте о передовых методах .
- Узнайте об ограничениях и квотах .