Rascunhos e experiências de campanha

Você já se perguntou: "Se eu alterar a página de destino dos meus anúncios, terei mais conversões?" ou "E se eu reformular meu anúncio dessa forma? Ajudaria a gerar tráfego?"? A configuração de um teste para isolar esse tipo de variável específica pode ser monótona, mas usando rascunhos e experiências de campanha, todo o trabalho pesado (cópia de dados, configuração etc.) é feito por você.

É fácil e rápido configurar novas experiências de campanha por meio do DraftService e do TrialService. Veja as etapas a serem seguidas para a configuração:

  1. Crie um rascunho com base em uma campanha existente. Rascunho é um espelho de uma campanha existente que não veicula anúncios.
  2. Modifique a campanha de rascunho de acordo com a necessidade da sua experiência.
  3. Crie um teste do seu rascunho. O teste concede acesso a uma nova campanha de teste que começa com uma cópia da campanha de rascunho e divide o tráfego entre a campanha de teste e a campanha base. Essas campanhas de teste também são conhecidas como experiências.
  4. Compare as estatísticas da sua campanha de teste com as da campanha base para ver qual tem o melhor desempenho.

Essas etapas representam o fluxo de trabalho mais comum, mas o DraftService e o TrialService são flexíveis e podem ser usados de outras formas. Por exemplo, é possível usar um rascunho para reproduzir as alterações em uma campanha base e depois integrá-las novamente na campanha sem usar um teste. Or, if you do use a trial and like the performance, you have the option of promoting its attributes back to the base campaign or graduating it into its own full–fledged campaign alongside the base campaign.

Este fluxograma mostra os fluxos de trabalho que podem ser empregados usando rascunhos e experiências de campanha:

Rascunhos

Rascunho é um objeto que mantém a associação entre uma campanha de rascunho e uma campanha base. Um rascunho é criado no DraftService fornecendo o ID de uma campanha existente para servir como base. O DraftService cria automaticamente uma nova campanha de rascunho e a associa com o rascunho. Um objeto "Rascunho" não é, por si só, a campanha de rascunho. Ele simplesmente relaciona uma campanha de rascunho à sua campanha base. A campanha de rascunho existe como um objeto Campanha real. O campaignId da campanha de rascunho está disponível como draftCampaignId do rascunho. É possível modificar a campanha de rascunho como faria com uma campanha real, como alterar seus critérios, grupos de anúncios, lances e anúncios. No entanto, uma campanha de rascunho não veicula anúncios.

Para que uma campanha existente sirva como base para um rascunho, é necessário que ela atenda a alguns requisitos. Ela precisa ser uma campanha da rede de pesquisa ou da rede de pesquisa com exibição em Display e ter um orçamento não compartilhado (isExplicitlyShared no orçamento da campanha precisa ser falso). Embora as experiências sejam compatíveis com grande parte dos recursos de campanhas, há algumas exceções.

Criação de um rascunho

Para criar um rascunho, defina um baseCampaignId e nomeie o rascunho. O nome deve ser diferente de todos os rascunhos da sua conta. Veja este exemplo no Ruby:

Java

// Get the DraftService.
DraftServiceInterface draftService = adWordsServices.get(session, DraftServiceInterface.class);
Draft draft = new Draft();
draft.setBaseCampaignId(baseCampaignId);
draft.setDraftName("Test Draft #" + System.currentTimeMillis());

DraftOperation draftOperation = new DraftOperation();
draftOperation.setOperator(Operator.ADD);
draftOperation.setOperand(draft);

draft = draftService.mutate(new DraftOperation[] {draftOperation}).getValue(0);

VB

Dim draft As New Draft()
draft.baseCampaignId = baseCampaignId
draft.draftName = "Test Draft #" + ExampleUtilities.GetRandomString()

Dim draftOperation As New DraftOperation()
draftOperation.operator = [Operator].ADD
draftOperation.operand = draft

C#

Draft draft = new Draft() {
  baseCampaignId = baseCampaignId,
  draftName = "Test Draft #" + ExampleUtilities.GetRandomString()
};

DraftOperation draftOperation = new DraftOperation() {
  @operator = Operator.ADD,
  operand = draft
};

PHP

// Get the DraftService, which loads the required classes.
$draftService = $user->GetService('DraftService', ADWORDS_VERSION);

// Create a draft.
$draft = new Draft();
$draft->baseCampaignId = $baseCampaignId;
$draft->draftName = 'Test Draft #' . uniqid();

// Create an operation.
$operation = new DraftOperation();
$operation->operand = $draft;
$operation->operator = 'ADD';
$operations[] = $operation;

// Make the mutate request.
$result = $draftService->mutate($operations);
$draft = $result->value[0];

Perl

my $draft = Google::Ads::AdWords::v201609::Draft->new({
    baseCampaignId => $base_campaign_id,
    draftName      => sprintf("Test Draft #%s", uniqid())});

# Create operation.
my $draft_operation = Google::Ads::AdWords::v201609::DraftOperation->new({
    operator => "ADD",
    operand  => $draft
});

Python

draft_service = client.GetService('DraftService', version='v201609')

draft = {
    'baseCampaignId': base_campaign_id,
    'draftName': 'Test Draft #%s' % uuid.uuid4()
}

draft_operation = {'operator': 'ADD', 'operand': draft}
draft = draft_service.mutate([draft_operation])['value'][0]
draft_campaign_id = draft['draftCampaignId']

Ruby

draft_srv = adwords.service(:DraftService, API_VERSION)

draft = {
  :base_campaign_id => base_campaign_id,
  :draft_name => 'Test Draft #%d' % (Time.new.to_f * 1000).to_i
}
draft_operation = {:operator => 'ADD', :operand => draft}

draft_result = draft_srv.mutate([draft_operation])

draft = draft_result[:value].first
draft_id = draft[:draft_id]
draft_campaign_id = draft[:draft_campaign_id]

O rascunho inclui detalhes como draftCampaignId, draftId e baseCampaignId, todos importantes. draftCampaignId é usado para modificar a campanha de rascunho e seus grupos de anúncios, critérios e anúncios. draftId e baseCampaignId servem como referência para o rascunho ao criar um teste ou promover o rascunho.

Personalização da campanha de rascunho

O campo draftCampaignId pode ser usado como código da campanha real usando qualquer serviço que possa precisar de um código (CampaignService, AdGroupService, CampaignCriterionService etc.). Por exemplo, para adicionar um novo critério de idioma à campanha, basta usar o draftCampaignId do rascunho como seu campaignId:

Java

CampaignCriterionServiceInterface campaignCriterionService =
    adWordsServices.get(session, CampaignCriterionServiceInterface.class);

Language language = new Language();
language.setId(1003L); // Spanish

// Make sure to use the draftCampaignId when modifying the virtual draft campaign.
CampaignCriterion campaignCriterion = new CampaignCriterion();
campaignCriterion.setCampaignId(draft.getDraftCampaignId());
campaignCriterion.setCriterion(language);

CampaignCriterionOperation criterionOperation = new CampaignCriterionOperation();
criterionOperation.setOperator(Operator.ADD);
criterionOperation.setOperand(campaignCriterion);

campaignCriterion =
    campaignCriterionService
        .mutate(new CampaignCriterionOperation[] {criterionOperation})
        .getValue(0);

VB

campaign_criterion_srv =
    adwords.service(:CampaignCriterionService, API_VERSION)

criterion = {
  :xsi_type => 'Language',
  :id => 1003 # Spanish
}

criterion_operation = {
  # Make sure to use the draft_campaign_id when modifying the virtual draft
  # campaign.
  :operator => 'ADD',
  :operand => {
    :campaign_id => draft_campaign_id,
    :criterion => criterion
  }
}

criterion_result = campaign_criterion_srv.mutate([criterion_operation])

C#

Language language = new Language() {
  id = 1003L // Spanish
};

// Make sure to use the draftCampaignId when modifying the virtual draft
// campaign.
CampaignCriterion campaignCriterion = new CampaignCriterion() {
  campaignId = draft.draftCampaignId,
  criterion = language
};

CampaignCriterionOperation criterionOperation = new CampaignCriterionOperation() {
  @operator = Operator.ADD,
  operand = campaignCriterion
};

campaignCriterion = campaignCriterionService.mutate(
    new CampaignCriterionOperation[] {criterionOperation}).value[0];

PHP

$campaignCriterionService =
    $user->GetService('CampaignCriterionService', ADWORDS_VERSION);

// Create a criterion.
$language = new Language();
$language->id = 1003; // Spanish
$campaignCriterion = new CampaignCriterion();
$campaignCriterion->campaignId = $draft->draftCampaignId;
$campaignCriterion->criterion = $language;

// Create an operation.
$operations = array();
$operation = new CampaignCriterionOperation();
$operation->operand = $campaignCriterion;
$operation->operator = 'ADD';
$operations[] = $operation;

// Make the mutate request.
$result = $campaignCriterionService->mutate($operations);
$campaignCriterion = $result->value[0];

Perl

my $criterion = Google::Ads::AdWords::v201609::Language->new({
    id => 1003    # Spanish
});

my $operation =
  Google::Ads::AdWords::v201609::CampaignCriterionOperation->new({
    operator => "ADD",
    operand  => Google::Ads::AdWords::v201609::CampaignCriterion->new({
        campaignId => $draft_campaign_id,
        criterion  => $criterion
      })});

$result =
  $client->CampaignCriterionService()->mutate({operations => [$operation]});

$criterion = $result->get_value()->[0];

Python

campaign_criterion_service = client.GetService('CampaignCriterionService',
                                               version='v201609')

criterion = {
    'xsi_type': 'Language',
    'id': 1003  # Spanish
}

criterion_operation = {
    # Make sure to use the draftCampaignId when modifying the virtual draft
    # campaign.
    'operator': 'ADD',
    'operand': {
        'campaignId': draft_campaign_id,
        'criterion': criterion
    }
}

criterion = campaign_criterion_service.mutate([criterion_operation])[
    'value'][0]

Ruby

campaign_criterion_srv =
    adwords.service(:CampaignCriterionService, API_VERSION)

criterion = {
  :xsi_type => 'Language',
  :id => 1003 # Spanish
}

criterion_operation = {
  # Make sure to use the draft_campaign_id when modifying the virtual draft
  # campaign.
  :operator => 'ADD',
  :operand => {
    :campaign_id => draft_campaign_id,
    :criterion => criterion
  }
}

criterion_result = campaign_criterion_srv.mutate([criterion_operation])

Operações semelhantes podem ser realizadas em grupos de anúncios da campanha de rascunho. Por exemplo, é possível buscar grupos de anúncios de uma campanha de rascunho usando um filtro:

Java

// Get the AdGroupService.
AdGroupServiceInterface adGroupService =
    adWordsServices.get(session, AdGroupServiceInterface.class);

// Create a selector that limits to ad groups in the draft campaign.
Selector selector =
    new SelectorBuilder()
        .fields(AdGroupField.Id)
        .equals(AdGroupField.CampaignId, Long.toString(draftCampaignId))
        .limit(100)
        .build();

// Make a 'get' request.
AdGroupPage adGroupPage = adGroupService.get(selector);

// Display the results.
if (adGroupPage.getEntries() != null && adGroupPage.getEntries().length > 0) {
  System.out.printf(
      "Found %d of %d ad groups.%n",
      adGroupPage.getEntries().length, adGroupPage.getTotalNumEntries());
} else {
  System.out.println("No ad groups found.");
}

VB

' Get the AdGroupService.
Dim adGroupService As AdGroupService = CType(user.GetService( _
    AdWordsService.v201609.AdGroupService), AdGroupService)

' Create a selector that limits to ad groups in the draft campaign.
Dim selector As New Selector
selector.fields = New String() {
  AdGroup.Fields.Id
}
selector.predicates = New Predicate() {
  Predicate.Equals(AdGroup.Fields.CampaignId, draftCampaignId)
}
selector.paging = Paging.Default

Dim page As AdGroupPage = adGroupService.get(selector)

' Display the results.
If ((Not page Is Nothing) AndAlso (Not page.entries Is Nothing)) Then
  Console.WriteLine("Fetched {0} of {1} ad groups.", page.entries.Length,
      page.totalNumEntries)
End If

PHP

// Get the AdGroupService.
$adGroupService = $user->GetService('AdGroupService', ADWORDS_VERSION);

// Create selector.
$selector = new Selector();
$selector->fields = array('Id');

// Create predicates.
$selector->predicates[] =
    new Predicate('CampaignId', 'EQUALS', $draftCampaignId);

// Create paging controls.
$selector->paging = new Paging(0, AdWordsConstants::RECOMMENDED_PAGE_SIZE);

// Make the get request.
$page = $adGroupService->get($selector);

// Display results.
if ($page->totalNumEntries !== null && $page->totalNumEntries > 0) {
  printf("Found %d ad groups.\n", $page->totalNumEntries);
} else {
  print "No ad groups were found.\n";
}

Perl

# Create predicates.
my $campaign_predicate = Google::Ads::AdWords::v201609::Predicate->new({
    field    => "CampaignId",
    operator => "EQUALS",
    values   => [$draft_campaign_id]});

# Create selector.
my $paging = Google::Ads::AdWords::v201609::Paging->new({
  startIndex    => 0,
  numberResults => PAGE_SIZE
});
my $selector = Google::Ads::AdWords::v201609::Selector->new({
  fields     => ["Id"],
  predicates => [$campaign_predicate],
  paging     => $paging
});

my $ad_group_page = $client->AdGroupService()->get({
  serviceSelector => $selector
});
if ($ad_group_page->get_entries()) {
  printf(
    "Found %d of %d ad groups.\n",
    scalar(@{$ad_group_page->get_entries()}),
    $ad_group_page->get_totalNumEntries());
} else {
  printf("No ad groups found.\n");
}

Python

ad_group_service = client.GetService('AdGroupService', version='v201609')

selector = {
    'fields': ['Id'],
    'paging': {
        'startIndex': str(0),
        'numberResults': str(PAGE_SIZE)
    },
    'predicates': [{
        'field': 'CampaignId',
        'operator': 'IN',
        'values': [draft_campaign_id]
    }]
}

response = ad_group_service.get(selector)
draft_ad_groups = response['entries'] if 'entries' in response else []

Ruby

ad_group_srv = adwords.service(:AdGroupService, API_VERSION)

selector = {
  :fields => ['Id'],
  :predicates => [{
    :field => 'CampaignId',
    :operator => 'IN',
    :values => [draft_campaign_id]
  }],
  :paging => {
    :start_index => 0,
    :number_results => 100
  }
}

ad_group_page = ad_group_srv.get(selector)

unless ad_group_page[:entries].nil?
  puts "Found %d of %d ad groups." %
      [ad_group_page[:entries].size, ad_group_page[:total_num_entries]]
else
  puts "No ad groups found."
end

São realizadas verificações de política em anúncios de campanhas de rascunho, assim como em campanhas normais. Violações da política ocorridas somente após a veiculação do anúncio não resultarão em outros anúncios de rascunho, mas em um erro se e quando um teste desse rascunho for executado.

Quando terminar de fazer as alterações na campanha de rascunho, você terá duas opções:

  1. Promover as alterações da campanha de rascunho diretamente na campanha base.
  2. Criar uma campanha de teste para ser exibida ao mesmo tempo que campanha base.

Promoção da campanha de rascunho

É possível usar rascunhos como uma simples área de teste para alterações pendentes da sua campanha real sem criar um teste. Quando todas as alterações forem testadas na campanha de rascunho, bastará promover o rascunho, fazendo com que essas alterações sejam aplicadas à campanha base. Para promover um rascunho, envie uma operação de mutação que define o status para PROMOTING. Como isso envolve a cópia de todas as alterações da campanha de rascunho de volta na campanha base, a promoção é realizada de forma assíncrona e é irreversível. A configuração do status inicia o processo assíncrono.

É possível pesquisar o rascunho usando draftId e baseCampaignId para monitorar o campo de status. Quando o status muda de PROMOTING, a operação está concluída. PROMOTED indica sucesso e PROMOTE_FAILED indica que um erro foi encontrado. Falaremos mais sobre esquemas de pesquisa na seção Testes.

Se ocorrer um erro ao tentar promover o rascunho, será possível ver mais detalhes sobre erros específicos fornecendo o draftId e o baseCampaignId ao DraftAsyncErrorService. Consulte a seção Erros para ver um exemplo.

Campanhas de rascunho são identificáveis porque o campaignTrialType delas é DRAFT. Também é possível pesquisar o ID da campanha base em uma campanha de rascunho usando o campo baseCampaignId. Para campanhas normais (não criadas com um rascunho ou teste), o campaignTrialType é BASE e o campo baseCampaignId é o próprio ID da campanha.

Por padrão, entidades de rascunho não são incluídas nos resultados gerados de outros serviços, como CampaignService ou AdGroupService. Para buscar uma entidade de rascunho, como uma campanha de rascunho ou um grupo de anúncios de rascunho, é necessário especificar o campaignTrialType explicitamente como DRAFT nos atributos ou filtrar um ID de uma entidade de rascunho conhecida, como draftCampaignId.

Testes

Teste é uma entidade que ajuda a gerenciar uma campanha de teste sendo exibida junto a uma campanha base, com parte do tráfego e orçamento, para testar o que é configurado no rascunho. Um teste produz uma campanha em tempo real (também denominada experiência) que veicula anúncios como uma campanha normal. Ele coleta estatísticas separadamente da sua campanha base. Entretanto, um teste é considerado no limite do número de campanhas, grupos de anúncios etc. que você pode ter na sua conta em dado momento.

Criação de um teste

Para criar um teste, forneça o draftId e o baseCampaignId para identificar o rascunho com base no qual você está começando o teste, um nome exclusivo e a porcentagem do tráfego que deseja gerar no teste. A criação de um teste automaticamente cria uma campanha de teste recém-associada.

Testes são identificados exclusivamente pelo seu código, ao contrário de rascunhos. Portanto, quando um teste é criado, não é necessário usar o baseCampaignId para se referir a ele.

Java

// Get the TrialService.
TrialServiceInterface trialService = adWordsServices.get(session, TrialServiceInterface.class);

Trial trial = new Trial();
trial.setDraftId(draftId);
trial.setBaseCampaignId(baseCampaignId);
trial.setName("Test Trial #" + System.currentTimeMillis());
trial.setTrafficSplitPercent(50);

TrialOperation trialOperation = new TrialOperation();
trialOperation.setOperator(Operator.ADD);
trialOperation.setOperand(trial);

long trialId = trialService.mutate(new TrialOperation[] {trialOperation}).getValue(0).getId();

VB

Dim newTrial As New Trial
newTrial.draftId = draftId
newTrial.baseCampaignId = baseCampaignId
newTrial.name = "Test Trial #" & ExampleUtilities.GetRandomString()
newTrial.trafficSplitPercent = 50

Dim trialOperation As New TrialOperation()
trialOperation.operator = [Operator].ADD
trialOperation.operand = newTrial

C#

Trial trial = new Trial() {
  draftId = draftId,
  baseCampaignId = baseCampaignId,
  name = "Test Trial #" + ExampleUtilities.GetRandomString(),
  trafficSplitPercent = 50
};

TrialOperation trialOperation = new TrialOperation() {
  @operator = Operator.ADD,
  operand = trial
};

PHP

// Get the TrialService, which loads the required classes.
$trialService = $user->GetService('TrialService', ADWORDS_VERSION);
$trialAsynErrorService =
    $user->GetService('TrialAsyncErrorService', ADWORDS_VERSION);

// Create a trial.
$trial = new Trial();
$trial->draftId = $draftId;
$trial->baseCampaignId = $baseCampaignId;
$trial->name = 'Test Trial #' . uniqid();
$trial->trafficSplitPercent = 50;

// Create an operation.
$operation = new TrialOperation();
$operation->operand = $trial;
$operation->operator = 'ADD';
$operations[] = $operation;

// Make the mutate request.
$result = $trialService->mutate($operations);
$trial = $result->value[0];

Perl

my $trial = Google::Ads::AdWords::v201609::Trial->new({
    draftId             => $draft_id,
    baseCampaignId      => $base_campaign_id,
    name                => sprintf("Test Trial #%s", uniqid()),
    trafficSplitPercent => 50,
});

# Create operation.
my $trial_operation = Google::Ads::AdWords::v201609::TrialOperation->new({
    operator => "ADD",
    operand  => $trial
});

# Add trial.
my $result =
  $client->TrialService()->mutate({operations => [$trial_operation]});

Python

trial_service = client.GetService('TrialService', version='v201609')
trial_async_error_service = client.GetService('TrialAsyncErrorService',
                                              version='v201609')

trial = {
    'draftId': draft_id,
    'baseCampaignId': base_campaign_id,
    'name': 'Test Trial #%d' % uuid.uuid4(),
    'trafficSplitPercent': 50
}

trial_operation = {'operator': 'ADD', 'operand': trial}
trial_id = trial_service.mutate([trial_operation])['value'][0]['id']

Ruby

trial_srv = adwords.service(:TrialService, API_VERSION)
trial_async_error_srv = adwords.service(:TrialAsyncErrorService, API_VERSION)

trial = {
  :draft_id => draft_id,
  :base_campaign_id => base_campaign_id,
  :name => 'Test Trial #%d' % (Time.new.to_f * 1000).to_i,
  :traffic_split_percent => 50
}
trial_operation = {:operator => 'ADD', :operand => trial}

trial_result = trial_srv.mutate([trial_operation])

trial_id = trial_result[:value].first[:id]

Assim como na criação de rascunhos, anote o trialCampaignId do teste para uso em operações futuras.

A criação de um teste é uma operação assíncrona (ao contrário da criação de rascunho). Um novo teste tem o status CREATING. Pesquise até ele ser alterado para outro status antes de continuar:

Java

Selector trialSelector =
    new SelectorBuilder()
        .fields(
            TrialField.Id,
            TrialField.Status,
            TrialField.BaseCampaignId,
            TrialField.TrialCampaignId)
        .equalsId(trialId)
        .build();

trial = null;
boolean isPending = true;
int pollAttempts = 0;
do {
  long sleepSeconds = (long) Math.scalb(30d, pollAttempts);
  System.out.printf("Sleeping for %d seconds.%n", sleepSeconds);
  Thread.sleep(sleepSeconds * 1000);
  trial = trialService.get(trialSelector).getEntries(0);

  System.out.printf("Trial ID %d has status '%s'.%n", trial.getId(), trial.getStatus());
  pollAttempts++;
  isPending = TrialStatus.CREATING.equals(trial.getStatus());
} while (isPending && pollAttempts < MAX_POLL_ATTEMPTS);

VB

' Since creating a trial is asynchronous, we have to poll it to wait
' for it to finish.
Dim trialSelector As New Selector()
trialSelector.fields = New string() {
    Trial.Fields.Id, Trial.Fields.Status, Trial.Fields.BaseCampaignId,
    Trial.Fields.TrialCampaignId
}
trialSelector.predicates = New Predicate() {
    Predicate.Equals(Trial.Fields.Id, trialId)
}
newTrial = Nothing
Dim isPending As Boolean = true
Dim pollAttempts As Integer = 0

Do
  Dim sleepMillis As Integer = CType(Math.Pow(2, pollAttempts) * _
      POLL_INTERVAL_SECONDS_BASE * 1000, Integer)
  Console.WriteLine("Sleeping {0} millis...", sleepMillis)
  Thread.Sleep(sleepMillis)

  newTrial = trialService.get(trialSelector).entries(0)

  Console.WriteLine("Trial ID {0} has status '{1}'.", newTrial.id, newTrial.status)
  pollAttempts = pollAttempts + 1
  isPending = (newTrial.status = TrialStatus.CREATING)
Loop while isPending AndAlso (pollAttempts <= MAX_RETRIES)

If newTrial.status = TrialStatus.ACTIVE Then
  ' The trial creation was successful.
  Console.WriteLine("Trial created with ID {0} and trial campaign ID {1}.",
      newTrial.id, newTrial.trialCampaignId)
Else If newTrial.status = TrialStatus.CREATION_FAILED Then
  ' The trial creation failed, and errors can be fetched from the
  ' TrialAsyncErrorService.
  Dim errorsSelector As New Selector()
  errorsSelector.fields = New string() {
      TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError
  }
  errorsSelector.predicates = New Predicate() {
    Predicate.Equals(TrialAsyncError.Fields.TrialId, newTrial.id)
  }

  Dim trialAsyncErrorService As TrialAsyncErrorService = _
      CType(user.GetService(AdWordsService.v201609.TrialAsyncErrorService),
          TrialAsyncErrorService)

  Dim trialAsyncErrorPage As TrialAsyncErrorPage = trialAsyncErrorService.get(
      errorsSelector)
  If trialAsyncErrorPage.entries Is Nothing OrElse _
      trialAsyncErrorPage.entries.Length = 0 Then
    Console.WriteLine("Could not retrieve errors for trial {0}.", newTrial.id)
  Else
    Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " & _
        "following errors:", trialId, draftId)
    Dim i As Integer = 1
    For Each err As TrialAsyncError In trialAsyncErrorPage.entries
      Dim asyncError As ApiError = err.asyncError
      Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', trigger='{3}'," & _
        " fieldPath='{4}'", i, asyncError.ApiErrorType, asyncError.errorString, _
        asyncError.trigger, asyncError.fieldPath)
      i += 1
    Next
  End If
Else
  ' Most likely, the trial is still being created. You can continue
  ' polling, but we have limited the number of attempts in the
  ' example.
  Console.WriteLine("Timed out waiting to create trial from draft ID {0} with " +
      "base campaign ID {1}.", draftId, baseCampaignId)
End If

C#

// Since creating a trial is asynchronous, we have to poll it to wait
// for it to finish.
Selector trialSelector = new Selector() {
  fields = new string[] {
    Trial.Fields.Id, Trial.Fields.Status, Trial.Fields.BaseCampaignId,
    Trial.Fields.TrialCampaignId
  },
  predicates = new Predicate[] {
    Predicate.Equals(Trial.Fields.Id, trialId)
  }
};

trial = null;
bool isPending = true;
int pollAttempts = 0;

do {
  int sleepMillis = (int) Math.Pow(2, pollAttempts) *
      POLL_INTERVAL_SECONDS_BASE * 1000;
  Console.WriteLine("Sleeping {0} millis...", sleepMillis);
  Thread.Sleep(sleepMillis);

  trial = trialService.get(trialSelector).entries[0];

  Console.WriteLine("Trial ID {0} has status '{1}'.", trial.id, trial.status);
  pollAttempts++;
  isPending = (trial.status == TrialStatus.CREATING);
} while (isPending && pollAttempts <= MAX_RETRIES);

if (trial.status == TrialStatus.ACTIVE) {
  // The trial creation was successful.
  Console.WriteLine("Trial created with ID {0} and trial campaign ID {1}.",
      trial.id, trial.trialCampaignId);
} else if (trial.status == TrialStatus.CREATION_FAILED) {
  // The trial creation failed, and errors can be fetched from the
  // TrialAsyncErrorService.
  Selector errorsSelector = new Selector() {
    fields = new string[] {
      TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError
    },
    predicates = new Predicate[] {
      Predicate.Equals(TrialAsyncError.Fields.TrialId, trial.id)
    }
  };

  TrialAsyncErrorService trialAsyncErrorService =
      (TrialAsyncErrorService) user.GetService(
          AdWordsService.v201609.TrialAsyncErrorService);

  TrialAsyncErrorPage trialAsyncErrorPage = trialAsyncErrorService.get(errorsSelector);
  if (trialAsyncErrorPage.entries == null || trialAsyncErrorPage.entries.Length == 0) {
    Console.WriteLine("Could not retrieve errors for trial {0}.", trial.id);
  } else {
    Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " +
        "following errors:", trial.id, draftId);
    int i = 0;
    foreach (TrialAsyncError error in trialAsyncErrorPage.entries) {
      ApiError asyncError = error.asyncError;
      Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', trigger='{3}'," +
        " fieldPath='{4}'", i++, asyncError.ApiErrorType, asyncError.errorString,
        asyncError.trigger, asyncError.fieldPath);
    }
  }
} else {
    // Most likely, the trial is still being created. You can continue
    // polling, but we have limited the number of attempts in the
    // example.
    Console.WriteLine("Timed out waiting to create trial from draft ID {0} with " +
        "base campaign ID {1}.", draftId, baseCampaignId);
}

PHP

$selector = new Selector();
$selector->fields =
    array('Id', 'Status', 'BaseCampaignId', 'TrialCampaignId');
$selector->predicates = new Predicate('Id', 'IN', array($trial->id));

// Since creating a trial is asynchronous, we have to poll it to wait for it
// to finish.
$pollAttempts = 0;
$isPending = true;
$trial = null;
do {
  $sleepSeconds = POLL_FREQUENCY_SECONDS * pow(2, $pollAttempts);
  printf("Sleeping %d seconds...\n", $sleepSeconds);
  sleep($sleepSeconds);

  $trial = $trialService->get($selector)->entries[0];
  printf("Trial ID %d has status '%s'.\n", $trial->id, $trial->status);

  $pollAttempts++;
  $isPending = ($trial->status === 'CREATING') ? true : false;
} while ($isPending && $pollAttempts <= MAX_POLL_ATTEMPTS);

Perl

my $trial_id = $result->get_value()->[0]->get_id()->get_value();

my $predicate = Google::Ads::AdWords::v201609::Predicate->new({
    field    => "Id",
    operator => "IN",
    values   => [$trial_id]});
my $paging = Google::Ads::AdWords::v201609::Paging->new({
    startIndex    => 0,
    numberResults => 1
});
my $selector = Google::Ads::AdWords::v201609::Selector->new({
    fields => ["Id", "Status", "BaseCampaignId", "TrialCampaignId"],
    predicates => [$predicate],
    paging     => $paging
});

# Since creating a trial is asynchronous, we have to poll it to wait for
# it to finish.
my $poll_attempts = 0;
my $is_pending    = 1;
my $end_time      = time + JOB_TIMEOUT_IN_MILLISECONDS;
do {
  # Check to see if the trial is still in the process of being created.
  my $result = $client->TrialService()->get({selector => $selector});
  $trial = $result->get_entries()->[0];
  my $waittime_in_milliseconds =
    JOB_BASE_WAITTIME_IN_MILLISECONDS * (2**$poll_attempts);
  if (((time + $waittime_in_milliseconds) < $end_time)
    and $trial->get_status() eq 'CREATING')
  {
    printf("Sleeping %d milliseconds...\n", $waittime_in_milliseconds);
    sleep($waittime_in_milliseconds / 1000);    # Convert to seconds.
    $poll_attempts++;
  }
} while (time < $end_time
  and $trial->get_status() eq 'CREATING');

Python

selector = {
    'fields': ['Id', 'Status', 'BaseCampaignId', 'TrialCampaignId'],
    'predicates': [{
        'field': 'Id',
        'operator': 'IN',
        'values': [trial_id]
    }]
}

# Since creating a trial is asynchronous, we have to poll it to wait for it to
# finish.
poll_attempts = 0
is_pending = True
trial = None

while is_pending and poll_attempts < MAX_POLL_ATTEMPTS:
  trial = trial_service.get(selector)['entries'][0]
  print 'Trial ID %d has status "%s"' % (trial['id'], trial['status'])
  poll_attempts += 1
  is_pending = trial['status'] == 'CREATING'

  if is_pending:
    sleep_seconds = 30 * (2 ** poll_attempts)
    print 'Sleeping for %d seconds.' % sleep_seconds
    time.sleep(sleep_seconds)

Ruby

selector = {
  :fields => ['Id', 'Status', 'BaseCampaignId', 'TrialCampaignId'],
  :predicates => [
    :field => 'Id', :operator => 'IN', :values => [trial_id]
  ]
}

poll_attempts = 0
is_pending = true
trial = nil
begin
  sleep_seconds = 30 * (2 ** poll_attempts)
  puts "Sleeping for %d seconds" % sleep_seconds
  sleep(sleep_seconds)

  trial = trial_srv.get(selector)[:entries].first

  puts "Trial ID %d has status '%s'" % [trial[:id], trial[:status]]

  poll_attempts += 1
  is_pending = (trial[:status] == 'CREATING')
end while is_pending and poll_attempts < MAX_POLL_ATTEMPTS

Depois de criar um teste, a campanha de teste associada funcionará basicamente como uma campanha normal. É possível ajustar a exibição do teste, exceto para os seguintes campos, que são ditados pelo teste e imutáveis na campanha de teste (ao contrário de uma campanha normal):

  • status
  • name
  • startDate
  • endDate
  • budget

Ao criar um teste, startDate e endDate serão por padrão as datas da campanha base, se não forem especificadas. Modificações em startDate, endDate e name do teste serão propagadas na campanha de teste. Modificações em status da campanha base também serão propagadas na campanha de teste.

Operações de teste

Testes têm duas operações assíncronas separadas: criação e promoção. Se ocorrer um erro ao tentar criar ou promover um teste, será possível ver detalhes sobre erros específicos encontrados fornecendo o código do teste em TrialAsyncErrorService. Consulte a seção Erros para ver um exemplo.

Também é possível interromper um teste imediatamente alterando o status para HALTED. Não é possível desfazer a interrupção de um teste para que ele comece a ser exibido junto à campanha base novamente, mas é possível promover, graduar ou arquivar um teste interrompido.

Campanhas de teste sempre compartilham um orçamento implicitamente com sua campanha base. Esse orçamento não pode ser compartilhado com outras campanhas e precisa ser especificamente marcado como não compartilhado definindo isExplicitlyShared como falso. A campanha de teste usará a porcentagem específica do orçamento da campanha base durante o período do teste. Assim que o teste terminar (porque o período terminou ou foi interrompido manualmente), 100% do tráfego voltará para a campanha base.

Campanhas de teste são identificáveis usando campaignTrialType, que será TRIAL. Assim como nos rascunhos, baseCampaignId apontará para a campanha base da qual o teste é copiado.

Promoção, graduação e arquivamento

Quando decidir se gosta dos resultados do teste, você terá algumas opções ao definir diferentes status do teste.

Se você não gostou no desempenho do teste, pode arquivá-lo definindo o status ARCHIVED. Isso imediatamente marca a campanha de teste como REMOVED e interrompe a experiência.

Por outro lado, se você gostou do desempenho do teste e ele ainda está ACTIVE, é possível implementar a campanha de teste permanentemente de duas formas. Uma delas é aplicar todas as modificações de volta na campanha base, chamado de promoção. A outra é permitir a existência da campanha de teste de forma independente do próprio teste, possibilitando que todos os campos normalmente imutáveis durante um teste sejam modificados novamente e que funcione como uma campanha completa. Esse último processo é conhecido como graduação.

A promoção é um processo assíncrono, como a promoção de um rascunho. Para começar, defina o status do teste como PROMOTING e pesquise-o até ele ser alterado para PROMOTED ou PROMOTE_FAILED. É possível verificar erros assíncronos com o TrialAsyncErrorService.

A graduação é um processo síncrono. Ao definir o teste como GRADUATED, a campanha estará imediatamente pronta para ser modificada e exibida. Ao graduar uma campanha de teste, também é necessário especificar um budgetId para um novo orçamento a ser usado pela campanha. Ela não pode continuar compartilhando o orçamento da campanha base depois da graduação.

Relatórios

A campanha de teste não copia estatísticas anteriores da sua campanha base. Ela começa do zero. Durante a exibição do teste, as estatísticas da campanha base e da campanha de teste são auferidas separadamente. Cada uma tem suas próprias impressões, cliques etc. Isso não muda na promoção e na graduação. As estatísticas permanecem onde estão e jamais são copiadas em uma entidade diferente.

Depois da promoção, a campanha base mantém todas as suas estatísticas antigas e segue com novas alterações copiadas na campanha base. As estatísticas da campanha de teste depois da promoção continuam nela.

Depois da graduação, a campanha base e a campanha de teste continuam existindo como entidades separadas, e cada uma delas mantém suas próprias estatísticas para relatórios.

É possível usar relatórios normais, como o Relatório de desempenho de campanhas, nessas entidades. O campo baseCampaignId nas campanhas representa a campanha base, e o campo campaignTrialType permite distinguir entre campanhas normais e de teste.

Erros

Tanto rascunhos quanto testes permitem determinados tipos de ação (promoção para rascunhos, e criação e promoção para testes) assíncrona. Nesses casos, é necessário pesquisar o serviço recuando exponencialmente até que a operação seja concluída ou haja um erro. Se houver um erro, o status da entidade o indicará, e você poderá consultar o AsyncErrorService adequado para ver detalhes.

Por exemplo, se houver falha na promoção de um teste, será possível solicitar detalhes sobre os erros, desta forma:

Java

Selector errorsSelector =
    new SelectorBuilder()
        .fields(TrialAsyncErrorField.TrialId, TrialAsyncErrorField.AsyncError)
        .equals(TrialAsyncErrorField.TrialId, trial.getId().toString())
        .build();

TrialAsyncErrorServiceInterface trialAsyncErrorService =
    adWordsServices.get(session, TrialAsyncErrorServiceInterface.class);
TrialAsyncErrorPage trialAsyncErrorPage = trialAsyncErrorService.get(errorsSelector);
if (trialAsyncErrorPage.getEntries() == null
    || trialAsyncErrorPage.getEntries().length == 0) {
  System.out.printf(
      "Could not retrieve errors for trial ID %d for draft ID %d.%n", trial.getId(), draftId);
} else {
  System.out.printf(
      "Could not create trial ID %d for draft ID %d due to the following errors:%n",
      trial.getId(),
      draftId);
  int i = 0;
  for (TrialAsyncError error : trialAsyncErrorPage.getEntries()) {
    ApiError asyncError = error.getAsyncError();
    System.out.printf(
        "Error #%d: errorType='%s', errorString='%s', trigger='%s', fieldPath='%s'%n",
        i++,
        asyncError.getApiErrorType(),
        asyncError.getErrorString(),
        asyncError.getTrigger(),
        asyncError.getFieldPath());
  }

VB

Else If newTrial.status = TrialStatus.CREATION_FAILED Then
  ' The trial creation failed, and errors can be fetched from the
  ' TrialAsyncErrorService.
  Dim errorsSelector As New Selector()
  errorsSelector.fields = New string() {
      TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError
  }
  errorsSelector.predicates = New Predicate() {
    Predicate.Equals(TrialAsyncError.Fields.TrialId, newTrial.id)
  }

  Dim trialAsyncErrorService As TrialAsyncErrorService = _
      CType(user.GetService(AdWordsService.v201609.TrialAsyncErrorService),
          TrialAsyncErrorService)

  Dim trialAsyncErrorPage As TrialAsyncErrorPage = trialAsyncErrorService.get(
      errorsSelector)
  If trialAsyncErrorPage.entries Is Nothing OrElse _
      trialAsyncErrorPage.entries.Length = 0 Then
    Console.WriteLine("Could not retrieve errors for trial {0}.", newTrial.id)
  Else
    Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " & _
        "following errors:", trialId, draftId)
    Dim i As Integer = 1
    For Each err As TrialAsyncError In trialAsyncErrorPage.entries
      Dim asyncError As ApiError = err.asyncError
      Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', trigger='{3}'," & _
        " fieldPath='{4}'", i, asyncError.ApiErrorType, asyncError.errorString, _
        asyncError.trigger, asyncError.fieldPath)
      i += 1
    Next
  End If

C#

} else if (trial.status == TrialStatus.CREATION_FAILED) {
  // The trial creation failed, and errors can be fetched from the
  // TrialAsyncErrorService.
  Selector errorsSelector = new Selector() {
    fields = new string[] {
      TrialAsyncError.Fields.TrialId, TrialAsyncError.Fields.AsyncError
    },
    predicates = new Predicate[] {
      Predicate.Equals(TrialAsyncError.Fields.TrialId, trial.id)
    }
  };

  TrialAsyncErrorService trialAsyncErrorService =
      (TrialAsyncErrorService) user.GetService(
          AdWordsService.v201609.TrialAsyncErrorService);

  TrialAsyncErrorPage trialAsyncErrorPage = trialAsyncErrorService.get(errorsSelector);
  if (trialAsyncErrorPage.entries == null || trialAsyncErrorPage.entries.Length == 0) {
    Console.WriteLine("Could not retrieve errors for trial {0}.", trial.id);
  } else {
    Console.WriteLine("Could not create trial ID {0} for draft ID {1} due to the " +
        "following errors:", trial.id, draftId);
    int i = 0;
    foreach (TrialAsyncError error in trialAsyncErrorPage.entries) {
      ApiError asyncError = error.asyncError;
      Console.WriteLine("Error #{0}: errorType='{1}', errorString='{2}', trigger='{3}'," +
        " fieldPath='{4}'", i++, asyncError.ApiErrorType, asyncError.errorString,
        asyncError.trigger, asyncError.fieldPath);
    }
  }

PHP

$selector = new Selector();
$selector->fields = array('TrialId', 'AsyncError');
$selector->predicates = new Predicate('TrialId', 'IN', array($trial->id));

$errors = $trialAsynErrorService->get($selector)->entries;

if (count($errors) === 0) {
  printf("Could not retrieve errors for the trial with ID %d\n",
      $trial->id);
} else {
  printf("Could not create trial due to the following errors:\n");
  $i = 0;
  foreach ($errors as $error) {
    printf("Error #%d: %s\n", $i++, $error->asyncError);
  }
}

Perl

my $error_selector = Google::Ads::AdWords::v201609::Selector->new({
    fields     => ["TrialId", "AsyncError"],
    predicates => [
      Google::Ads::AdWords::v201609::Predicate->new({
          field    => "TrialId",
          operator => "IN",
          values   => [$trial_id]})]});

my $errors =
  $client->TrialAsyncErrorService->get({selector => $error_selector})
  ->get_entries();
if (!$errors) {
  printf("Could not retrieve errors for trial %d", $trial->get_id());
} else {
  printf("Could not create trial due to the following errors:");
  my $index = 0;
  for my $error ($errors) {
    printf("Error %d: %s", $index, $error->get_asyncError()
    ->get_errorString());
    $index++;
  }
}

Python

selector = {
    'fields': ['TrialId', 'AsyncError'],
    'predicates': [{
        'field': 'TrialId',
        'operator': 'IN',
        'values': [trial['id']]
    }]
}

errors = trial_async_error_service.get(selector)['entries']

if not errors:
  print 'Could not retrieve errors for trial %d' % trial['id']
else:
  print 'Could not create trial due to the following errors:'
  for error in errors:
    print 'Error: %s' % error['asyncError']

Ruby

selector = {
  :fields => ['TrialId', 'AsyncError'],
  :predicates => [
    {:field => 'TrialId', :operator => 'IN', :values => [trial[:id]]}
  ]
}

errors = trial_async_error_srv.get(selector)[:entries]

if errors.nil?
  puts "Could not retrieve errors for trial %d" % trial[:id]
else
  puts "Could not create trial due to the following errors:"
  errors.each_with_index do |error, i|
    puts "Error #%d: %s" % [i, error[:async_error]]
  end
end

Operações assíncronas normalmente continuam aplicando as alterações mesmo depois de encontrar um erro. Portanto, erros registrados poderão aumentar rapidamente se houver um número significativo de modificações incompatíveis. Possíveis causas incluem nomes duplicados de grupos de anúncios, estratégias de lance incompatíveis ou limites de conta ultrapassados.

Enviar comentários sobre…

Precisa de ajuda? Acesse nossa página de suporte.