Leia este guia para se familiarizar com o processo de envio de eventos com a API Data Manager.
Use a API Data Manager em um dos seguintes cenários:
Envie conversões da tag do Google Ads ou qualquer evento do Google Analytics com um ID de transação como uma fonte de dados adicional para sua tag ou eventos do Google Analytics para Firebase (GA4F). Assim, você maximiza os indicadores de interação com o anúncio e melhora seus dados e a performance geral.
Esse recurso está disponível para todas as contas do Google Ads, mas apenas para propriedades do Google Analytics em uma lista de permissões. Preencha o formulário se quiser adicionar sua propriedade do Google Analytics à lista de permissão.
Envie dados de eventos para conversões off-line ou conversões otimizadas para leads do Google Ads.
Envie conversões de vendas na loja do Google Ads. Esse recurso está disponível apenas para contas do Google Ads em uma lista de permissão.
Envie eventos recomendados e personalizados para o Google Analytics.
Neste guia, você vai concluir as seguintes etapas:
- Prepare um
Destinationpara receber dados de eventos. - Prepare os dados de eventos para envio.
- Crie uma solicitação
IngestionServicepara eventos. - Envie a solicitação com o APIs Explorer do Google.
- Entenda as respostas de sucesso e falha.
Preparar destinos
Antes de enviar dados, é necessário preparar pelo menos um Destination para eles. Estes são os campos de um Destination. Confira Configurar destinos para mais detalhes e exemplos de destinos para diferentes cenários.
Selecione a guia que corresponde ao seu caso de uso.
- Selecione Anunciante se você estiver usando credenciais de uma Conta do Google que é um usuário nas contas de anunciante que você quer gerenciar.
- Selecione Parceiro de dados se você estiver usando credenciais de uma Conta do Google que é um usuário em uma conta de parceiro de dados e quiser gerenciar contas de anunciante com uma vinculação de parceiro à conta de parceiro de dados.
Advertiser
operatingAccountA conta que recebe os eventos.
Para eventos enviados como uma fonte de dados adicional, a conta operacional pode ser uma conta do Google Ads ou uma propriedade do Google Analytics.
Se o
accountTypeforGOOGLE_ANALYTICS_PROPERTY, as credenciais da solicitação precisarão ser de um usuário do Google Analytics com a função de editor ou administrador na propriedade.Para conversões off-line, conversões otimizadas para leads, conversões de vendas na loja e conversões com dados do carrinho, a conta operacional precisa ser do Google Ads.
loginAccountA conta em que a Conta do Google para as credenciais é um usuário.
productDestinationIdO ID da entidade no
operatingAccountque recebe os eventos.Para eventos enviados como uma fonte de dados adicional, o
productDestinationIdprecisa ser um dos seguintes:O ID de uma conversão do Google Ads com
typedefinido comoWEBPAGE. Na interface do Google Ads, a Origem da conversão de uma ação de conversãoWEBPAGEé Site.O ID de métricas de um fluxo de dados da Web do Google Analytics.
O ID do app do Firebase de um fluxo de dados de app iOS ou Android do Google Analytics.
Para outros eventos enviados ao Google Analytics, o
productDestinationIdprecisa ser um dos seguintes:O ID de métricas de um fluxo de dados da Web do Google Analytics.
O ID do app do Firebase de um fluxo de dados de app iOS ou Android do Google Analytics.
Para conversões off-line ou conversões otimizadas para leads, o
productDestinationIdprecisa ser o ID de uma ação de conversão do Google Ads comtypedefinido comoUPLOAD_CLICKS. Na interface do Google Ads, a Origem da conversão de uma ação de conversãoUPLOAD_CLICKSé Site (importação de cliques).Para conversões de vendas na loja, o
productDestinationIdprecisa ser o ID de uma ação de conversão do Google Ads comtypedefinido comoSTORE_SALES. Na interface do Google Ads, a Origem da conversão de uma ação de conversãoSTORE_SALESé Vendas na loja.
{
"operatingAccount": {
"accountType": "OPERATING_ACCOUNT_TYPE",
"accountId": "OPERATING_ACCOUNT_ID"
},
"loginAccount": {
"accountType": "LOGIN_ACCOUNT_TYPE",
"accountId": "LOGIN_ACCOUNT_ID"
},
"productDestinationId": "PRODUCT_DESTINATION_ID"
}
Parceiro de dados
operatingAccount- A conta que recebe os eventos. Precisa ser uma conta do Google Ads. A API Data Manager não permite enviar eventos para uma propriedade do Google Analytics usando um parceiro de dados vinculado.
loginAccountA conta em que o usuário da credencial tem acesso.
Defina o
accountIdcomo o ID da conta do parceiro de dados e oaccountTypecomoDATA_PARTNER.linkedAccountA conta com um link de produto estabelecido em que o usuário da credencial tem acesso ao
operatingAccount.Se um familiar responsável do
operatingAccountestiver vinculado à sua conta de parceiro de dados, defina olinkedAccountcomo o familiar responsável dooperatingAccount. Se ooperatingAccountestiver vinculado diretamente à sua conta de parceiro de dados, não definalinkedAccount.productDestinationIdO ID da entidade no
operatingAccountque recebe os eventos.Para eventos enviados como uma fonte de dados adicional usando um parceiro de dados vinculado, o
productDestinationIdprecisa ser o ID de uma conversão do Google Ads comtypedefinido comoWEBPAGE. Na interface do Google Ads, a Origem da conversão de uma ação de conversãoWEBPAGEé Site.Para conversões off-line ou conversões otimizadas para leads, o
productDestinationIdprecisa ser o ID de uma ação de conversão do Google Ads comtypedefinido comoUPLOAD_CLICKS. Na interface do Google Ads, a Origem da conversão de uma ação de conversãoUPLOAD_CLICKSé Site (importação de cliques).Para conversões de vendas na loja, o
productDestinationIdprecisa ser o ID de uma ação de conversão do Google Ads comtypedefinido comoSTORE_SALES. Na interface do Google Ads, a Origem da conversão de uma ação de conversãoSTORE_SALESé Vendas na loja.
{
"operatingAccount": {
"accountType": "OPERATING_ACCOUNT_TYPE",
"accountId": "OPERATING_ACCOUNT_ID"
},
"loginAccount": {
"accountType": "DATA_PARTNER",
"accountId": "DATA_PARTNER_ACCOUNT_ID"
},
"linkedAccount": {
"accountType": "LINKED_ACCOUNT_TYPE",
"accountId": "LINKED_ACCOUNT_ID"
},
"productDestinationId": "PRODUCT_DESTINATION_ID"
}
O exemplo neste guia mostra como construir uma solicitação que envia todos os eventos para o mesmo destino. Se quiser enviar eventos para vários destinos na mesma solicitação, consulte Enviar eventos para vários destinos.
Preparar dados de eventos
Considere os seguintes dados de evento. Cada tabela corresponde a um evento de conversão. Cada evento de conversão tem um carimbo de data/hora, uma ação de conversão e um valor de conversão.
Cada evento pode ter identificadores de publicidade, como gclid, ou identificadores de usuário, como endereços de e-mail, números de telefone e informações de endereço. Um evento também pode ter:
- Informações sobre o usuário avaliadas no momento do evento, como o valor do cliente ou se ele é novo, recorrente ou cliente reengajado.
- Dados do carrinho do Shopping.
- Outros parâmetros de evento ou propriedades do usuário para um destino, como
clientId,appInstanceIdouuserIdpara o Google Analytics.
Confira os dados de eventos:
Evento 1
| Evento nº 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 |
| Itens no carrinho | |
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 |
Evento 2
| Evento 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 |
| Itens no carrinho | |
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 |
Evento 3
| Evento nº 3 (vendas na loja) | |
|---|---|
conversion_time |
June 11, 2025 12:05:00PM America/New_York |
conversion_action_id |
123456789 |
transaction_id |
XYZ987654321 |
conversion_value |
450.0 |
currency |
usd |
given_name |
Charlie |
family_name |
Smith-Jones |
region_code |
US |
postal_code |
10011 |
store_id |
STORE_NY_001 |
merchant_id |
123 |
merchant_feed_label |
US |
merchant_feed_language_code |
en |
| Itens no carrinho | |
merchant_product_id |
ITEM_SKU_987 |
item_quantity |
1 |
item_price |
450.0 |
variable |
DEPARTMENT |
value |
ELECTRONICS |
Formatar os dados
Formate os campos conforme especificado no guia de formatação. Confira os dados de eventos após a formatação:
Evento 1
| Evento nº 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 |
| Itens no carrinho | |
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 |
Evento 2
| Evento 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 |
| Itens no carrinho | |
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 |
Evento 3
| Evento nº 3 (vendas na loja) | |
|---|---|
conversion_time |
2025-06-11T12:05:00-04:00 |
conversion_action_id |
123456789 |
transaction_id |
XYZ987654321 |
conversion_value |
450.0 |
currency |
USD |
given_name |
charlie |
family_name |
smith-jones |
region_code |
US |
postal_code |
10011 |
store_id |
STORE_NY_001 |
merchant_id |
123 |
merchant_feed_label |
US |
merchant_feed_language_code |
en |
| Itens no carrinho | |
merchant_product_id |
ITEM_SKU_987 |
item_quantity |
1 |
item_price |
450.0 |
variable |
DEPARTMENT |
value |
ELECTRONICS |
Gerar hash e codificar os dados
Além disso, os endereços de e-mail, nomes e sobrenomes formatados precisam ser criptografados com hash usando o algoritmo SHA-256 e codificados com hexadecimal ou Base64. Estes são os dados de evento após formatação, hash e codificação usando codificação hexadecimal:
Evento 1
| Evento nº 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 |
| Itens no carrinho | |
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 |
Evento 2
| Evento 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 |
| Itens no carrinho | |
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 |
Evento 3
| Evento nº 3 (vendas na loja) | |
|---|---|
conversion_time |
2025-06-11T12:05:00-04:00 |
conversion_action_id |
123456789 |
transaction_id |
XYZ987654321 |
conversion_value |
450.0 |
currency |
USD |
given_name |
B9DD960C1753459A78115D3CB845A57D924B6877E805B08BD01086CCDF34433C |
family_name |
DB98D2607EFFFA28AFF66975868BF54C075ECA7157E35064DCE08E20B85B1081 |
region_code |
US |
postal_code |
10011 |
store_id |
STORE_NY_001 |
merchant_id |
123 |
merchant_feed_label |
US |
merchant_feed_language_code |
en |
| Itens no carrinho | |
merchant_product_id |
ITEM_SKU_987 |
item_quantity |
1 |
item_price |
450.0 |
variable |
DEPARTMENT |
value |
ELECTRONICS |
Converter os dados em objetos Event
Converta os dados formatados e com hash de cada evento em um Event. Preencha os seguintes campos conforme indicado:
Defina
eventTimestampcomo o horário em que o evento ocorreu.Os eventos do Google Analytics precisam ter um
eventTimestampnas últimas 72 horas.Defina os campos obrigatórios para seu caso de uso do Google Ads ou do Google Analytics.
Google Ads
Caso de uso Identificadores transactionIdeventSourceConversões off-line ou conversões otimizadas para leads Obrigatório. Defina pelo menos uma das seguintes opções: adIdentifierscom pelo menos um degclid,gbraidouwbraiddefinido- Atributos da sessão
userData
Opcional Obrigatório. Defina como um dos valores de enumeração para EventSource.Eventos enviados como uma fonte de dados extra Obrigatório. Defina pelo menos uma das seguintes opções: adIdentifierscom pelo menos um degclid,gbraidouwbraiddefinidouserData
Obrigatório Opcional. Se definido, precisa ser WEB.Conversões de vendas na loja - Obrigatório. Defina os seguintes campos do
Event:eventLocation.storeIdcurrencyconversionValue
- Obrigatório. Defina os seguintes campos para fornecer identificadores de usuário:
- Preencha
thirdPartyUserDatase os identificadores de usuário forem de uma fonte terceirizada. A configuração dethirdPartyUserDatasó é permitida se a conta de login doDestinationfor um parceiro de dados (loginAccount.accountTypeéDATA_PARTNER). - Caso contrário, preencha
userData.
- Preencha
Obrigatório Obrigatório. Defina como IN_STORE.Google Analytics
Caso de uso Identificadores transactionIdeventSourceEventos enviados como uma fonte de dados adicional para um fluxo de dados da Web Obrigatório. Defina pelo menos uma das seguintes opções: clientIdadIdentifierscomgcliddefinidouserId
Obrigatório Opcional. Se definido, precisa ser WEB.Eventos enviados como uma fonte de dados adicional a um fluxo de dados do app Obrigatório. Defina
appInstanceId.Defina campos opcionais, como
userIdeadIdentifierscomgbraidougclidpara melhorar ainda mais a medição.Obrigatório Opcional. Se definido, precisa ser APP.Outros eventos enviados a um fluxo de dados da Web Obrigatório. Defina clientId.Obrigatório para alguns eventos, como purchaseerefund. Confira os detalhes nos requisitos do evento específico.Opcional. Se definido, precisa ser WEB.Outros eventos enviados a um fluxo de dados do app Obrigatório. Defina appInstanceId.Obrigatório para alguns eventos, como purchaseerefund. Confira os requisitos do evento específico para mais detalhes.Opcional. Se definido, precisa ser APP.Se você estiver enviando eventos como uma fonte de dados adicional, consulte Como o Google lida com dados de fontes de dados adicionais.
Se você estiver enviando eventos para o Google Analytics, adicione informações do Google Analytics a eles.
Se você estiver enviando conversões do Google Ads com dados do carrinho, eventos recomendados do Google Analytics, como
purchase, que exigem uma listaitems, ou se quiser incluir informações do carrinho com as conversões de vendas na loja do Google Ads, adicione dados do carrinho aos eventos.Preencha os outros campos em que você tem um valor para o evento. Consulte a documentação de referência de
Eventpara ver a lista completa de campos disponíveis.
Como o Google processa dados de fontes de dados adicionais
Na mesma ação de conversão, o Google usa o transactionId para remover a duplicação de eventos de conversão enviados de diferentes fontes (como a tag do site e solicitações de ingestão da API Data Manager). A tabela a seguir explica como os dados das suas solicitações de ingestão são tratados.
| Cenário | Campo de dados | Como isso é feito |
|---|---|---|
transactionId CORRESPONDE a um evento de tag existente
|
conversionValue (com currencyCode) |
Atualizado. O Observação:durante o período de teste inicial de 14 dias de uma ação de conversão, as atualizações de valor ficam desativadas. O valor da tag não será substituído nos relatórios do Google Ads até o fim do período de teste. |
transactionId CORRESPONDE a um evento de tag existente |
Outros campos, exceto conversionValue ou
currencyCode (por exemplo, adIdentifiers.gclid)
|
Ignorado. Outros valores de campo da sua fonte de dados extra não vão substituir os valores de campo que a tag do Google havia registrado para transações correspondentes. |
transactionId NÃO corresponde a nenhum evento |
Todos os dados fornecidos (por exemplo, userData, conversionValue, currencyCode)
|
Usado para criar um evento de conversão. Em seguida, o Google vai tentar atribuir essa nova conversão a um clique no anúncio usando os identificadores que você forneceu (como Observação:durante o período de teste inicial de 14 dias, essas conversões recém-criadas vão aparecer nos seus relatórios, mas não serão usadas para dar lances. Depois que o período de teste terminar, eles vão se tornar automaticamente disponíveis para lances. |
Adicionar atributos de sessão
Se você estiver enviando conversões off-line ou conversões otimizadas para leads, adicione atributos de sessão quando outros identificadores de anúncio, como um GCLID ou WBRAID, não estiverem disponíveis. Além de outros identificadores de publicidade, também é possível incluir atributos de sessão.
Os atributos de sessão oferecem mais contexto e indicadores sobre a interação do usuário com seu site, o que pode melhorar a medição de conversões, os relatórios e a precisão dos lances.
Na API Data Manager, há duas abordagens que podem ser usadas para enviar atributos de sessão:
Recomendado:defina o campo
sessionAttributesdeadIdentifierscomo a string de atributos de sessão codificada em base64. Siga as instruções em Como capturar session_attributes para modificar as páginas de envio de formulário e capturar a string codificada.Se não for possível usar o JavaScript, capture os campos de atributo de sessão individuais e adicione cada um à lista
experimentalFieldscomo umExperimentalFieldseparado:gad_campaignidsession_start_time_usecgad_sourcelanding_page_urllanding_page_referrer
Se você tiver um valor para o atributo de sessão
landing_page_user_agent, envie-o no campouserAgentdeadIdentifiers.landingPageDeviceInfo.Confira as práticas recomendadas ao enviar pares chave-valor individuais:
- Envie
gad_campaignidesession_start_time_usecde forma consistente. Esses campos são cruciais para uma atribuição precisa. - Não forneça um valor
landing_page_urlimpreciso ou parcial, como uma string de marcador de posição, um caminho de aplicativo interno ou um URL incompleto. Omita olanding_page_urlse você não tiver o URL completo e preciso.
Confira uma parte de um evento de amostra com entradas em
experimentalFieldsparagad_campaignidesession_start_time_usec, e o user agent no campolandingPageDeviceInfo:{ ..., "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" } } }
Adicionar informações do Google Analytics
Se os destinos de um evento enviado como uma fonte de dados adicional incluírem uma propriedade do Google Analytics, preencha os seguintes campos conforme indicado:
eventNameObrigatório. O nome do evento do Google Analytics.
Se o nome do evento for reservado, a API Data Manager vai rejeitar o evento com o erro
INVALID_EVENT_NAME.transactionIdObrigatório se indicado na especificação de evento do Google Analytics para o evento.
- Identificador
Confira os requisitos de identificador para cada tipo de fluxo de dados:
Fluxo de dados da Web
É necessário definir pelo menos um dos campos a seguir ao enviar um evento com um ID da transação como uma fonte de dados adicional:
clientId: identificador exclusivo de uma instância de usuário de um cliente da Web. Consulte as instruções do Measurement Protocol do Google Analytics.userId: um identificador exclusivo de um usuário. Consulte Medir a atividade em várias plataformas com o User-ID para mais informações.
Para todos os outros eventos recomendados ou personalizados,
clientIdé obrigatório. Consulte as instruções do Measurement Protocol do Google Analytics.Fluxo de dados do app
appInstanceId: obrigatório. Definido como o identificador exclusivo da instância do usuário do cliente do app. Consulte as instruções do Measurement Protocol do Google Analytics.
destinationReferencesObrigatório se a lista
destinationsno nível da solicitação contiver mais de umDestinationdo Google Analytics. Adicione uma entrada adestinationReferencespara especificar qual destino do Google Analytics deve receber o evento. Consulte enviar eventos para vários destinos para mais informações sobre referências de destino.Se
destinationReferencesnão estiver definido ou tiver várias entradas que se referem a destinos do Google Analytics, a API Data Manager vai rejeitar o evento com o erroMULTIPLE_DESTINATIONS_FOR_GOOGLE_ANALYTICS_EVENT.cartDataObrigatório se indicado na especificação de evento do Google Analytics para o evento.
Saiba como adicionar dados do carrinho aos seus eventos.
userIdOpcional. O User-ID do usuário.
userPropertiesOpcional. Propriedades do usuário para o usuário. Adicione uma entrada separada à lista
additionalUserPropertiespara cada propriedade do usuário.additionalEventParametersOpcional, mas recomendado. Preencha essa lista com todos os parâmetros de evento do Google Analytics que não são capturados nos outros campos
Event. Eles podem incluir outros parâmetros recomendados para o evento ou qualquer outro parâmetro que você queira capturar. Use o nome do parâmetro do Google Analytics para oparameterNamedoEventParameter.Por exemplo, se você tiver os tributos associados a uma transação, adicione uma entrada a
additionalEventParameterscomparameterNamedefinido comotaxevaluedefinido como o custo dos tributos.Não recomendamos adicionar entradas a
additionalEventParameterspara os parâmetros de evento do Google AnalyticstransactionId,currencyouvalue. Em vez disso, preencha os campostransactionId,currencyeconversionValuedoEvent, que têm precedência sobre todas as entradas emadditionalEventParameters.
Adicionar dados do carrinho
Preencha o campo cartData do Event com informações sobre os itens associados ao evento. Use esse campo ao enviar eventos recomendados do Google Analytics, como purchase, que exigem uma lista items ou conversões do Google Ads com parâmetros de dados do carrinho.
Preencha os campos a seguir para CartData conforme necessário.
items- Obrigatório. Inclua pelo menos um item nessa lista.
Campos do Merchant Center
Os campos merchantId, merchantFeedLabel e merchantFeedLanguageCode são opcionais. Defina esses campos para conversões do Google Ads com dados do carrinho se os itens no evento incluírem produtos que existem em várias contas do Merchant Center.
merchantId- Opcional. O ID da conta do Merchant Center.
merchantFeedLabel- Opcional. O rótulo do feed do feed do Merchant Center. Com os rótulos do feed, é possível categorizar produtos para segmentação de campanhas. Por exemplo, você pode usar rótulos de feed para organizar produtos por idioma. Se a campanha segmentar produtos com base no país, use o código de país de duas letras no formato ISO-3166-1 alfa-2. Exemplo:
US. merchantFeedLanguageCode- Opcional. O código de idioma ISO 639-1 associado ao feed do Merchant Center
em que seus itens são enviados. Exemplo:
en.
Adicionar dados do carrinho no nível do item
Adicione um ou mais objetos Item à lista items de CartData. A lista items não pode estar vazia para conversões do Google Ads com dados do carrinho e eventos recomendados do Google Analytics, como purchase, que exigem uma lista items.
Preencha os seguintes campos para cada Item conforme necessário.
Google Analytics
Confira os requisitos para os eventos recomendados do Google Analytics, como purchase, que exigem uma lista items.
items.itemId- Obrigatório. Um identificador exclusivo do item.
items.unitPriceOpcional. O preço unitário excluindo tributos, frete e descontos no escopo do evento (no nível da transação) para este item.
Se o item tiver um desconto no escopo do item, use o preço unitário com desconto. Por exemplo, se um item tiver um preço unitário de
27.67e um desconto unitário de6.66, definaunitPricecomo21.01.items.quantityObrigatório. A quantidade de unidades deste item específico.
items.additionalItemParametersOpcional. Preencha essa lista com parâmetros no escopo do item que não são capturados nos outros campos
Item. Use o nome do parâmetro da documentação do Google Analytics como oparameterNamedoItemParameter.Por exemplo, no Google Analytics, se você tiver a marca e a categoria de um item para um evento
purchase, adicione uma entrada aoadditionalItemParametersdo item comparameterNamedefinido comoitem_brandevaluedefinido como o nome da marca.Não recomendamos adicionar entradas a
additionalItemParameterspara os parâmetros de itemquantity,priceouitem_id. Em vez disso, preencha os campositemId,unitPriceequantitydoItem, que têm precedência sobre todas as entradas emadditionalItemParameters.
Vendas na loja do Google Ads
Confira os requisitos para conversões de vendas na loja do Google Ads.
items.itemId- Opcional. Um identificador exclusivo do item.
items.merchantProductId- Opcional. O ID do produto na conta do Merchant Center.
items.quantity- Opcional. A quantidade de unidades compradas para este item específico.
O padrão é
1se não for definido. items.unitPriceOpcional. Definido como o preço unitário, excluindo tributos, frete e descontos no escopo do evento (nível da transação) para este item.
Se o item tiver um desconto no escopo do item, use o preço unitário com desconto. Por exemplo, se um item tiver um preço unitário de
27.67e um desconto unitário de6.66, definaunitPricecomo21.01.items.conversionValueOpcional. O valor de conversão de uma única unidade deste item. Por exemplo, se duas unidades de um item forem compradas por R $10,00 cada, defina
items.conversionValuecomo10.00eitems.quantitycomo2. Observe que, embora esse campo seja opcional para itens individuais, o campoconversionValueno nível do evento é obrigatório ao fazer upload de conversões de vendas na loja.items.customVariablesOpcional. Outros dados de par de chave-valor a serem enviados para a ação de conversão do Google Ads, especificados como uma lista de objetos
CustomVariable.
Google Ads com dados do carrinho
Confira os requisitos para enviar dados do carrinho com conversões do Google Ads que não são de vendas na loja. Se os eventos forem conversões de vendas na loja do Google Ads, consulte os requisitos de vendas na loja do Google Ads.
items.itemId- Obrigatório. Um identificador exclusivo do item.
items.merchantProductId- Obrigatório. O ID do produto na conta do Merchant Center.
items.unitPriceObrigatório. O preço unitário excluindo tributos, frete e descontos no escopo do evento (no nível da transação) para este item.
Se o item tiver um desconto no escopo do item, use o preço unitário com desconto. Por exemplo, se um item tiver um preço unitário de
27.67e um desconto unitário de6.66, definaunitPricecomo21.01.items.quantityObrigatório. A quantidade de unidades compradas para este item específico.
Exemplos de solicitação
Google Analytics
Confira um exemplo de Event para os dados formatados, com hash e codificados do segundo evento, com dados adicionais para o 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"
}
]
}
]
}
}
Vendas na loja do Google Ads
Confira um exemplo de Event para os dados formatados, com hash e codificados do terceiro evento, como um exemplo de conversão de vendas na loja do Google Ads:
{
"conversionValue": 450.0,
"currency": "USD",
"eventTimestamp": "2025-06-11T12:05:00-04:00",
"transactionId": "XYZ987654321",
"eventSource": "IN_STORE",
"eventLocation": {
"storeId": "STORE_NY_001"
},
"userData": {
"userIdentifiers": [
{
"address": {
"givenName": "B9DD960C1753459A78115D3CB845A57D924B6877E805B08BD01086CCDF34433C",
"familyName": "DB98D2607EFFFA28AFF66975868BF54C075ECA7157E35064DCE08E20B85B1081",
"regionCode": "US",
"postalCode": "10011"
}
}
]
},
"cartData": {
"merchantId": "123",
"merchantFeedLabel": "US",
"merchantFeedLanguageCode": "en",
"items": [
{
"merchantProductId": "ITEM_SKU_987",
"quantity": 1,
"conversionValue": 450.0,
"customVariables": [
{
"variable": "DEPARTMENT",
"value": "ELECTRONICS"
}
]
}
]
}
}
Criar o corpo da solicitação
Para criar o corpo da solicitação, combine destinations e events, defina o campo encoding e adicione outros campos de solicitação que você quer incluir, como validateOnly e consent.
Os exemplos neste guia não usam criptografia, mas você pode seguir as instruções em Criptografar dados do usuário para adicionar criptografia ao seu processo.
Enviar a solicitação
Siga estas etapas para testar um pedido no navegador:
- Selecione a guia REST e clique em Abrir no API Explorer para abrir o API Explorer em uma nova guia ou janela.
- No corpo da solicitação no API Explorer, substitua cada string que começa com
REPLACE_WITH, comoREPLACE_WITH_OPERATING_ACCOUNT_TYPE, pelo valor relevante. - Clique em Executar na parte de baixo da página do API Explorer e conclua os prompts de autorização para enviar a solicitação.
- Defina
validateOnlycomotruepara validar a solicitação sem aplicar as mudanças. Quando estiver tudo pronto para aplicar as mudanças, definavalidateOnlycomofalse.
Se você instalou uma biblioteca de cliente, selecione a guia da linguagem de programação escolhida para conferir um exemplo de código completo de como criar e enviar uma solicitação.
REST
Advertiser
{ "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 }
Parceiro de dados
{ "destinations": [ { "operatingAccount": { "accountType": "OPERATING_ACCOUNT_TYPE", "accountId": "OPERATING_ACCOUNT_ID" }, "loginAccount": { "accountType": "DATA_PARTNER", "accountId": "DATA_PARTNER_ACCOUNT_ID" }, "linkedAccount": { "accountType": "LINKED_ACCOUNT_TYPE", "accountId": "LINKED_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 }
.NET
// 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); } } }
Nó
#!/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, )
Respostas de sucesso
Uma solicitação bem-sucedida retorna uma resposta com um objeto que contém um requestId.
{
"requestId": "126365e1-16d0-4c81-9de9-f362711e250a"
}
Registre o requestId retornado para recuperar diagnósticos
à medida que cada destino na solicitação é processado.
Respostas de falha
Uma solicitação com falha resulta em um código de status de resposta de erro, como 400 Bad
Request, e uma resposta com detalhes do erro.
Por exemplo, um emailAddress que contém uma string de texto simples em vez de um valor codificado em hexadecimal produz a seguinte resposta:
{
"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"
}
]
}
]
}
}
Um emailAddress que não é hash e é codificado apenas em hexadecimal produz a seguinte resposta:
{
"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"
}
]
}
]
}
}
Enviar eventos para vários destinos
Se os dados tiverem eventos para destinos diferentes, você poderá enviá-los na mesma solicitação usando referências de destino. Consulte Limites e cotas para saber o número máximo de destinos por solicitação.
Por exemplo, se você tiver um evento para o ID da ação de conversão 123456789 e outro para o ID 777111122, envie os dois eventos em uma única solicitação definindo o reference de cada Destination. O reference é
definido pelo usuário. O único requisito é que cada Destination tenha um
reference exclusivo. Esta é a lista destinations modificada para a solicitação:
Advertiser
"destinations": [
{
"operatingAccount": {
"accountType": "OPERATING_ACCOUNT_TYPE",
"accountId": "OPERATING_ACCOUNT_ID"
},
"loginAccount": {
"accountType": "LOGIN_ACCOUNT_TYPE",
"accountId": "LOGIN_ACCOUNT_ID"
},
"productDestinationId": "123456789",
"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"
}
]
Parceiro de dados
"destinations": [
{
"operatingAccount": {
"accountType": "OPERATING_ACCOUNT_TYPE",
"accountId": "OPERATING_ACCOUNT_ID"
},
"loginAccount": {
"accountType": "DATA_PARTNER",
"accountId": "DATA_PARTNER_ACCOUNT_ID"
},
"linkedAccount": {
"accountType": "LINKED_ACCOUNT_TYPE",
"accountId": "LINKED_ACCOUNT_ID"
},
"productDestinationId": "123456789",
"reference": "destination_a"
},
{
"operatingAccount": {
"accountType": "OPERATING_ACCOUNT_2_TYPE",
"accountId": "OPERATING_ACCOUNT_2_ID"
},
"loginAccount": {
"accountType": "DATA_PARTNER",
"accountId": "DATA_PARTNER_ACCOUNT_2_ID"
},
"linkedAccount": {
"accountType": "LINKED_ACCOUNT_2_TYPE",
"accountId": "LINKED_ACCOUNT_2_ID"
},
"productDestinationId": "777111122",
"reference": "destination_b"
}
]
Defina o destinationReferences de cada Event para enviar a um ou mais destinos específicos. Por exemplo, este é um Event que é apenas para o primeiro
Destination. Portanto, a lista destinationReferences contém apenas o
reference do primeiro Destination:
{
"adIdentifiers": {
"gclid": "GCLID_1"
},
"conversionValue": 1.99,
"currency": "USD",
"eventTimestamp": "2025-06-10T20:07:01Z",
"transactionId": "ABC798654321",
"eventSource": "WEB",
"destinationReferences": [
"destination_a"
]
}
O campo destinationReferences é uma lista. Portanto, é possível especificar vários destinos para um evento. Se você não definir o destinationReferences de um
Event, a API Data Manager enviará o evento para todos os destinos na
solicitação.
Se um evento tiver vários destinos, a API Data Manager enviará os campos relevantes para cada um deles. Por exemplo, se um evento tiver um destino do Google Ads e um do Google Analytics, a API vai incluir campos do Google Analytics, como clientId, appInstanceId ou eventName, ao enviar o evento para o destino do Google Analytics, e campos do Google Ads, como customVariables, ao enviar o evento para o destino do Google Ads.
Próximas etapas
- Configure a autenticação e configure seu ambiente com uma biblioteca de cliente.
- Saiba mais sobre os requisitos de formatação, hash e codificação para cada tipo de dado.
- Saiba como criptografar dados do usuário.
- Saiba como recuperar diagnósticos para suas solicitações.
- Saiba mais sobre as práticas recomendadas.
- Saiba mais sobre limites e cotas.