Holiday reminder about IDs

Wednesday, December 29, 2010


Whether you recently updated your code or just started with the AdWords API, please keep in mind that IDs of all entities are represented as xsd:long (64 bit signed integer) values. It is important to note that if your code stores ID values in a data type with lower range, the value may suffer from integer overflow.

As always, please post any questions to the AdWords API Forum.

-- Stan Grinberg, AdWords API Team

Dive deeper with the reference documentation sidebar

Friday, December 17, 2010


There are a wide variety of resources available for those learning about the AdWords API, but since they’re spread out across multiple sites it’s not always easy to find the information you’re looking for. While we acknowledge the need for these multiple sources, we knew we could do more to bring together our content from Blogger, YouTube, Google Code, and Twitter. We saw the official reference documentation as a good home for the information, and so we’ve added a new sidebar to the service-level pages that displays related material.


The code examples are pulled from our client libraries, and they provide concrete examples of the services in action. The blog posts are pulled from this blog, and contain deep dives into the services, explaining the behaviors and use cases they are suited for. The videos are pulled from our YouTube playlist, and feature recordings from our developer workshops across the world. The tweets are pulled from our @adwordsapi account, and often contain short tips on how to use the services more efficiently.

The sidebar uses the Google Feeds API to pull data from our multiple repositories, meaning that new content will be automatically displayed as soon as it becomes available. We hope that this sidebar leads to more efficient and effective development against the AdWords API, and if you have feedback or further ideas please reach out to us on the forum.

- Eric Koleda, AdWords API Team

Even more dynamic ads: Announcing Ad parameters enhancements

Monday, December 13, 2010


After we launched version v2009 of the AdWords API we announced the addition of a new feature called Ad parameters. Since then, many of you have used Ad parameters to insert dynamic numeric values into your text ads while retaining the ads’ historical performance information. We’ve heard feedback from you about functionality you’d like to see included in Ad parameters so we've released some enhancements to the feature.

Here’s what’s new:

  • You can now dynamically insert the percent sign (%) for values that include percentage discounts such as “Mobile Phone Accessories Now 20% Off”
  • We’ve added support for additional currency symbols and codes so you can include the currency symbol in the dynamic parameter to create ad text like “Flights to London Starting at €250”
  • You’re now able to use the forward slash character (/) for dynamic replacements with fraction discounts like “1/2 Off Our Entire Inventory Only This Weekend”
  • The plus (+) and minus (-) signs are now supported so you can advertise “NASDAQ +20 Points Today, Speak With a Broker Now.”
We hope these enhancements enable you to create even more dynamic ads with Ad parameters. Please continue to share your feedback and questions with us on the developer forum.

--Jason Shafton, Product Marketing Manager

Harness the power of predicates in your reports

Friday, December 10, 2010


The reporting services of the AdWords API are designed to allow you to download large amounts of data, but it can often be useful to filter out certain rows from the start to cut down on processing and transfer time. DefinedReportJobs created using the v13 ReportService supported a limited set of filtering options, primarily on IDs and statuses. Filtering on a more complex set of criteria would require post processing the report client-side. The ReportDefinitionService introduced in v201003 supports Predicates, a more flexible system of filters that can be used to create very targeted reports.

A predicate is composed of three parts: the field, the operator, and the values. Predicates can be created for almost every field available, indicated by canFilter set to "true" in the results returned from getReportFields().

<rval>
<fieldName>Clicks</fieldName>
<displayFieldName>Clicks</displayFieldName>
<xmlAttributeName>clicks</xmlAttributeName>
<fieldType>Long</fieldType>
<canSelect>true</canSelect>
<canFilter>true</canFilter>
</rval>


There are a fixed set of operators that can be used to compare the value of the field selected. EQUALS can be used to filter for an exact match, while IN can be used to match any of a set of values. GREATER_THAN and LESS_THAN are available for comparing numerical values, and CONTAINS is useful when working with strings. Only one operator can be used per predicate, but multiple predicates can be created for the same field to create more complex logic.

The values used in the predicate depend heavily on the field being operated on. Numerical values should be used for Long, Double, and Integer fields, and arbitrary strings can be used for String fields. The values for Money and Bid fields must always be specified in micros, even if the report is eventually downloaded with whole currency amounts. Although percentage fields like Ctr are returned as whole numbers, the values used in predicates should be the decimal equivalents. Predicates on Enum fields must only use the values of the enum, as outlined in the documentation and in the enumValues property returned by getReportFields().

<rval>
<fieldName>CampaignStatus</fieldName>
<displayFieldName>Campaign state</displayFieldName>
<xmlAttributeName>campaignState</xmlAttributeName>
<fieldType>CampaignStatus</fieldType>
<enumValues>ACTIVE</enumValues>
<enumValues>DELETED</enumValues>
<enumValues>PAUSED</enumValues>
<canSelect>true</canSelect>
<canFilter>true</canFilter>
</rval>


If multiple predicates are used within a Selector they will be combined using AND logic, so that the resulting report will only contain rows that satisfies all of the predicates. It’s not possible to combine predicates using OR logic at this time, but some common use cases can be handled using a single predicate and the IN operator. For example, to match keywords with either the PHRASE or EXACT match type you could use the following predicate:

<predicates>
<field>KeywordMatchType</field>
<operator>IN</operator>
<values>PHRASE</values>
<values>EXACT</values>
</predicates>


When combined together, predicates can be quite powerful. Consider the following example: I own an electronics shop that is starting to sell a wider array of tablet computers, and I want to drive more traffic to that section of my website. I’d like to start by finding all my current keywords that include the word "tablet" and have a high CTR, and then use them to generate new keyword ideas using the TargetingIdeaService. I’m only interested in keywords with greater than a 10% CTR, that have at least 100 impressions, and that are in enabled or paused ad groups.

Using predicates I can create a KEYWORDS_PERFORMANCE_REPORT that only returns me the exact data I am interested in:

<predicates>
<field>CampaignId</field>
<operator>EQUALS</operator>
<values>123456789</values>
</predicates>
<predicates>
<field>AdGroupStatus</field>
<operator>IN</operator>
<values>ENABLED</values>
<values>PAUSED</values>
</predicates>
<predicates>
<field>KeywordText</field>
<operator>CONTAINS</operator>
<values>tablet</values>
</predicates>
<predicates>
<field>Ctr</field>
<operator>GREATER_THAN</operator>
<values>0.10</values>
</predicates>
<predicates>
<field>Impressions</field>
<operator>GREATER_THAN_EQUALS</operator>
<values>100</values>
</predicates>


Predicates are one of many new improvements introduced in the ReportDefinitionService, and if you aren’t using the service yet, now is a great time to start looking into migrating. The service is fully supported in all our client libraries, and we’re available on the forum to answer any questions you may have.

- Eric Koleda, AdWords API Team

Discover v201008: Partial Failure for AdGroupCriterionService

Tuesday, December 07, 2010


With v201008 of the AdWords API we have introduced a new beta feature for AdGroupCriterionService: Partial Failure. Typically all requests to the AdWords API are atomic, so each of them either fails or succeeds in full. While this can be helpful to maintain consistency, we’ve heard from you that, in some cases, you would prefer that some operations fail while other changes are applied.

AdGroupCriterionService requests often contain many Criteria to add or update. Having an error in just one of them will force you to resend the whole set of objects again. We’ve introduced Partial Failure to enable you to get back a list of failed and successful operations and retry the failed ones only.

To utilize the new feature you’ll need to set this extra SOAP header:
partialFailure = true

Here’s an example from the Java client library:
// usual initialization code
AdWordsUser user = new AdWordsUser();
// Enable partial failure
user.setUsePartialFailure(true);
// Get the AdGroupCriterionService
AdGroupCriterionServiceInterface adGroupCriterionService =
  user.getService(AdWordsService.V201008.ADGROUP_CRITERION_SERVICE);
// Set up operations and operands here
List<AdGroupCriterionOperation> operations = new ArrayList<AdGroupCriterionOperation>();
// [...]

// Execute operations (add ad group criteria)
AdGroupCriterionReturnValue result =
  adGroupCriterionService.mutate(operations.toArray(
      new AdGroupCriterionOperation[] {}));

Now processing succeeded results:
// Display results
if ((result != null) && (result.getValue() != null)) {
  // A result is returned for every operation requested
  for (AdGroupCriterion adGroupCriterionResult : result.getValue()) {
    // Successful operation result will contain a non-null Criterion
    if (adGroupCriterionResult.getCriterion() != null) {
      System.out.printf("Ad group criterion with ad group id '%d', and " +
          "criterion id '%d', and keyword '%s' was added.\n",
          adGroupCriterionResult.getAdGroupId(),
          adGroupCriterionResult.getCriterion().getId(),
          ((Keyword) adGroupCriterionResult.getCriterion()).getText());
    }
  }
} else {
  System.out.println("No ad group criteria were added.");
}

Here is how to handle the failed operations:
// Is there any Partial Failure errors in the results?
if ((result != null) && (result.getPartialFailureErrors() != null)) {
  // Retrieving ApiError object for each of failures
  for (ApiError apiError : result.getPartialFailureErrors()) {
    // The order of the fields might be different to the order of operations in the
    // request, so we are getting the corresponding operation index from the fieldPath.
    Matcher matcher = operationIndexPattern.matcher(apiError.getFieldPath());
    if (matcher.matches()) {
      int operationIndex = Integer.parseInt(matcher.group(1));
      AdGroupCriterion adGroupCriterion =
          operations.get(operationIndex).getOperand();
      System.out.printf("Ad group criterion with ad group id '%d' and " +
          "keyword '%s' triggered a failure for the following reason: '%s'.\n",
          adGroupCriterion.getAdGroupId(),
          ((Keyword) adGroupCriterion.getCriterion()).getText(),
          apiError.getErrorString());
    } else {
      System.out.printf(
          "A failure for the following reason: '%s' has occurred.\n",
          apiError.getErrorString());
    }
  }
}


Partial Failure is fully supported in our client libraries. If you have any questions please post them on the AdWords API forum. This topic is also covered in a video presentation.

-- Danial Klimkin, AdWords API Team.

Discover v201008 - Track your account notifications with AlertService

Monday, November 22, 2010


When you’re managing a large number of accounts using the AdWords API you may want to retrieve account alerts. This was previously possible with AccountService in v13. The v201008 version introduces the AlertService, which brings similar functionality to the new AdWords API. This blog post discusses the differences between how alerts are retrieved using these two services.

Retrieving client account alerts

In v13, you could retrieve the alerts associated with list of client accounts linked to a My Client Center (MCC) using the getMccAlerts method of AccountService. In v201008, you can use the get method of AlertService to get the same results. The following code shows the service in action:


// Get the AlertService.
AlertService alertService = (AlertService) user.GetService(
    AdWordsService.v201008.AlertService);
 
// Create the alert query.
AlertQuery query = new AlertQuery();
query.filterSpec = FilterSpec.ALL;
query.clientSpec = ClientSpec.ALL;
query.triggerTimeSpec = TriggerTimeSpec.ALL_TIME;
query.severities = new AlertSeverity[] {AlertSeverity.GREEN,
    AlertSeverity.YELLOW, AlertSeverity.RED};

query.types = new AlertType[] {AlertType.CAMPAIGN_ENDING, 
    AlertType.CAMPAIGN_ENDED};
 
// Create the selector.
AlertSelector selector = new AlertSelector();
selector.query = query;
selector.paging = new Paging();
selector.paging.startIndex = 0;
selector.paging.numberResults = 10;

AlertPage page = alertService.get(selector);

Once you retrieve the alerts, you can display them by enumerating the page entries.

if (page != null && page.entries != null && page.entries.Length > 0) {
  Console.WriteLine("Retrieved {0} alerts out of {1}.",
      page.entries.Length, page.totalNumEntries);

  for (int i = 0; i < page.entries.Length; i++) {
    Alert alert = page.entries[i];
    Console.WriteLine("{0}) Customer Id is {1:###-###-####}, " +
        "Alert type is '{2}',Severity is {3}", i + 1, 
        alert.clientCustomerId, alert.alertType, alert.alertSeverity);
    for (int j = 0; j < alert.details.Length; j++) {
           Console.WriteLine("  - Triggered at {0}", alert.details[j].triggerTime);
    }
  }
} else {
  Console.WriteLine("No alerts were found.");
}

Differences between AccountService.getAllMccAlerts and AlertService.get

The main differences between the v13 AccountService.getAllMccAlerts and v201008 AlertService.get are in the table below.



AccountService.getAllMccAlerts AlertService.get
Returns alerts for all the client accounts immediately under the MCC. You can choose to retrieve alerts for all the child accounts, for immediate child accounts only, or for a specific list of customers by using the clientSpec and clientCustomerIds fields of AlertQuery.
Returns all available types of alerts for your client accounts. Returns only the types of alerts you request in your AlertQuery.
MccAlert provides multiple fields like clientLogin, clientName and clientCustomerId to associate an alert with a customer. Alert provides only clientCustomerId field to associate the alert with a customer.
Gives you one MccAlert object for each occurrence of the alert. Groups alerts of the same type for a given customer as a single Alert object. You can access the alert details from the details field of the alert object.
Alert priority can be low or high. Alert severity can be red, yellow or green.

The v201008 version of AlertService.get allows you to retrieve alerts only by predefined trigger time ranges. We plan to include support for filtering by custom date ranges in a future version of the AdWords API.

We have added support for AlertService in all of our client libraries, so please take advantage of this service and share your feedback with us on the forum.

-- Anash P. Oommen, AdWords API Team

Changes to managed placement bidding on the Display Network

Tuesday, November 16, 2010


Currently, ad groups using manual CPC bidding which contain managed placements are required to have a managed placements bid (siteMaxCpc). Today, we’re announcing that we’ll be sunsetting siteMaxCpc on March 15, 2011 to help simplify bid management on the Google Display Network (GDN).

We’re making this change in response to feedback that having multiple ad group bids for the GDN complicates bid management. Most of you using managed placements are already using placement-level bidding to control your costs on the GDN, and you’ll be able to continue to do this. In addition, the content bid (keywordContentMaxCpc) will continue to be available, offering control at the ad group level.

Here’s how this change will affect you: after March 15, we’ll stop accepting changes to ManualCpcAdGroupBids that modify siteMaxCpc to set it to anything other than 0. After that date, calls that attempt to set siteMaxCpc will return the error BiddingError.Reason.CANNOT_SET_SITE_MAX_CPC.

We’ll be announcing this change to all AdWords users early next year but wanted to let you know early so that you can prepare to modify your applications to stop accepting new siteMaxCpc bids. When we make the broader announcement, we’ll make it possible to “self-migrate” existing managed placements bids by setting them to zero and modifying their other bids as appropriate. In March, when we stop accepting changes to these bids entirely, we’ll migrate any remaining bids to the placement level, according to clear rules which we’ll share with you in a blog post in January.

If you have any questions about this change, we’ll be happy to answer them on our forum.

Posted by Prabhu Balasubramanian, Product Manager

AdWords Downtime: November 13, 10am-2pm PST

Tuesday, November 09, 2010


We'll be performing routine system maintenance on Saturday, November 13 from approximately 10:00am to 2:00pm PDT. You won't be able to access AdWords or the API during this time frame, but your ads will continue to run as normal.

Best,
- Eric Koleda, AdWords API Team 

Discover v201008: Remove incomplete BulkMutateJobs

Monday, November 08, 2010


The v201008 release of the AdWords API added a new feature to the BulkMutateJobService: the ability to remove incomplete jobs. This allows you to clean out your job queue after an application crash or other failure. In this post we’ll demonstrate how to use the feature and discuss some of its limitations.

When a BulkMutateJob is created the field numRequestParts is set, which determines how many BulkMutateRequest objects will be added to the job. Once the final request part has been added, the job enters into the account’s job queue, and when it reaches the front of the queue it begins processing. If the application crashes while the parts are being uploaded, the job will never start and remain stranded in the account.

Jobs that are incomplete and those that are complete but waiting in the queue behind a processing job will both have a PENDING status. An account can only have 10 pending jobs at a time, so stranded jobs can begin to fill up the account, until eventually no new jobs can be added. Prior to v201008 the only way to remove stranded jobs was to add the missing request parts so that the job would start. If the job had been stranded for a while though, the operations that were previously uploaded may make undesired changes to the account.

In v201008 the REMOVE operator can be used to delete incomplete jobs. The following code does this using the PHP client library.

$bulkMutateJobId = (float) 'INSERT_BULK_MUTATE_JOB_ID_HERE';

// Create BulkMutateJob.
$bulkMutateJob = new BulkMutateJob();
$bulkMutateJob->id = $bulkMutateJobId;

// Create operation.
$operation = new JobOperation();
$operation->operand = $bulkMutateJob;
$operation->operator = 'REMOVE';

// Delete bulk mutate job.
$bulkMutateJob = $bulkMutateJobService->mutate($operation);


After being removed, the job will be in the FAILED state and the failureReason field will contain JobError.USER_CANCELED_JOB.

A limitation of the REMOVE operator is that it only works on jobs that are in the PENDING state and that are incomplete (numRequestParts > numRequestPartsReceived). Jobs that are complete and have already entered the job queue, whether they be in the PENDING or PROCESSING state, cannot be deleted.

Code examples that show how to delete jobs are included in all of the client libraries. If you have any questions about this functionality or the BulkMutateJobService, ask us on the forum.

- Eric Koleda, AdWords API Team

A change to currency formatting in report downloads

Tuesday, November 02, 2010


If you're using the new AdWords API ReportDefinitionService, you may have noticed that monetary values in reports are returned as conventional currency instead of micros. At the request of the developer community, we'll be changing the format to micros on February 1, 2011 to make reporting more consistent with other AdWords API services.

A micro value is equal to one million times the conventional currency value, and is a standard we use throughout the AdWords API (including v13 reports) to represent money. For example, values currently returned as "1.50" (which would represent $1.50 for a USD account) will be returned as 1500000.

To help you with this change, we've introduced a new HTTP header flag (available immediately) that allows you to explicitly request the micros format. If you've written code that expects conventional currency values in reports, it's important that you update your code to expect micros and set the HTTP header "returnMoneyInMicros: true" when requesting a report download. Additionally, if you're just beginning to migrate from v13 reports, you should set this header on all your download requests.

To learn more about the new way to run reports via the API, see our blog post from this summer. As always, please post questions to the AdWords API Forum.

- Eric Koleda, AdWords API Team

Discover 201008: CustomerSyncService - So what’s new?

Wednesday, October 27, 2010


We recently released the second part of the v201008 version of the AdWords API and with it the CustomerSyncService - the AdWords API’s first service to determine the entities that were changed in your account. In this blog post, I’ll discuss our goals for the new service and provide a few examples illustrating how to integrate it with your system.

At our developer workshops, we received lots of great feedback about what you’d eventually like to see in this service. Please keep sharing your feedback with us on our forum.

Getting started

Central to the service is the CustomerSyncService.get method. You’ll use this method to retrieve all entities that have changed within the account for a given date time range and set of campaign IDs. As an example, to select all changes to campaign with id 12345 yesterday (today being October 27th), you would do something like:


// Create date time range.
DateTimeRange dateTimeRange = new DateTimeRange();
dateTimeRange.setMin(“20101026 000000”);
dateTimeRange.setMax(“20101027 000000”);

// Create selector.
CustomerSyncSelector selector = new CustomerSyncSelector();
selector.setDateTimeRange(dateTimeRange);
selector.setCampaignIds(new long[] {12345L});

// Get all account changes for campaign.
CustomerChangeData accountChanges = customerSyncService.get(selector);
The result will be a CustomerChangeData object with the entities that changed over the last day for the campaign specified; for the date time range you specified and the single campaign ID, you would only get one CampaignChangeData object back from within the accountChanges variable. If you had specified more than one campaign ID, you would get back one CampaignChangeData object per campaign.

Syncing up

The general way you can use the service is to:
  1. Get a full list of all current campaigns by performing a CampaignService.get with an empty selector and collect the IDs.
  2. Choose a date time range. This could depend on how granular you want your results, or when you last ran the service.
  3. Create a CustomerSyncSelector incorporating all of the campaign IDs and the date time range.
  4. Run CustomerSyncService.get with the selector.
  5. Process the results by traversing the CustomerChangeData object.
  6. Fetch the new data of the entties for all of the IDs within the CustomerChangeData hierarchy using their respective services
The goal of the CustomerSyncService is to give you a consolidated overview of what has changed over a time period; changes will be grouped together based on their overall outcome. Because of this, the dateTimeRange property will largely determine how your results are consolidated.

If you added a campaign to your account on the previous day, for example, you would receive a CampaignChangeData object with the campaignChangedStatus field set to NEW. Imagine now that you changed the campaign later that day as well. If the service returned a single CustomerChangeData object for each change, there would be two objects within the CustomerChangeData - one for the “new” event and one for the “modified” event. Instead, the two events will be consolidated into one object with the status set to NEW, not FIELDS_CHANGED. If you, however, split the service call into two date time ranges, one before the modification and one after, the first call would return a CampaignChangeData object with the status of NEW, and the second call would return a CampaignChangeData object with the status of FIELDS_CHANGED.

The same consolidation principle applies to child objects also. As an example, imagine you create a campaign, modify it later that day, then create an ad group, and then also modify that ad group later that day. The resulting CustomerChangeData object would resemble:

<rval>
...
  <changedCampaigns>
    <status>NEW</status>
    ...
  </changedCampaigns>
</rval>
Notice that not only is the status field is set to NEW, not FIELDS_CHANGED, but  also ad groups have been omitted even though they were also modified that day. Since the campaign is new, all of the entities within that object would also be new. Because of this, you would only get 1 CustomerChangeData object with no AdGroupChangeData within in it.

As a final example, imagine that you create an ad group into an existing campaign and modify the budget of the campaign during the same time period. The resulting CustomerChangeData object would be:

<rval>
...
  <changedCampaigns>
    <status>FIELDS_MODIFIED</status>
    ...
    <changedAdGroups>
        <status>NEW</status>
        ...
    </changedAdGroups>
  </changedCampaigns>
</rval>
Notice here that since the campaign already existed and the budget was changed, the status is FIELDS_MODIFIED. If the ad group had just been added, and the campaign budget left alone, the status for the campaign would be FIELDS_UNMODIFIED. Because you see the status is not FIELDS_UNMODIFIED, you know to fetch the campaign from the API, as well as the ad group, while syncing.

We believe the CustomerSyncService provides a great new way to save you time from scouring your account for changes. We’re looking forward to your feedback on our forum and look forward to seeing what you create with it.

-- Adam Rogal, AdWords API Team

Discover v201008: Remarketing

Thursday, October 21, 2010


Version v201008 of the AdWords API introduces the UserListService and the CriterionUserList which give you API access to the features available in the ‘Audiences’ tab in the AdWords interface. To learn more about remarketing, visit the AdWords Help Center.

You can set up remarketing using the AdWords API in two steps:

  1. Create a remarketing list.
  2. Create a CriterionUserList to tie your list to an AdGroup.

We’ve also included short code snippets showing you how to manage LogicalUserLists, also known as custom combination lists, and how to monitor your user list size.

Create a remarketing list

Creating a remarketing list involves the creation of two separate entities: the RemarketingList itself and its associated UserListConversionTypes also known as remarketing tags. The following code shows how to create a remarketing list.

// Get the UserListService.
UserListServiceInterface userListService =
    user.getService(AdWordsService.V201008.USER_LIST_SERVICE);

// Create conversion type (remarketing tag).
UserListConversionType conversionType = new UserListConversionType();
conversionType.setName("Mars cruise customers #" + System.currentTimeMillis());

// Create remarketing user list.
RemarketingUserList userList = new RemarketingUserList();
userList.setName("Mars cruise customers #" + System.currentTimeMillis());
userList.setDescription("A list of mars cruise customers in the last year");
userList.setMembershipLifeSpan(365L);
userList.setConversionTypes(new UserListConversionType[] {conversionType});

// Create operations.
UserListOperation operation = new UserListOperation();
operation.setOperand(userList);
operation.setOperator(Operator.ADD);

UserListOperation[] operations = new UserListOperation[] {operation};

// Add user list.
userList = userListService.mutate(operations).getValue()[0];

Tie a remarketing list to an AdGroup

A new type of criteria object called CriterionUserList is now part of v201008. Through this type of criteria you are able to tie a UserList to an AdGroup. As with other types of criteria, this type is also managed through the AdGroupCriterionService. The following code shows you how to create a CriterionUserList and tie it to an existing AdGroup.

// Create user list criteria.
CriterionUserList userListCriteria = new CriterionUserList();
userListCriteria.setUserListId(userList.getId());

// Create biddable ad group criterion.
BiddableAdGroupCriterion userListBiddableAdGroupCriterion = new BiddableAdGroupCriterion();
userListBiddableAdGroupCriterion.setAdGroupId(adGroupId);
userListBiddableAdGroupCriterion.setCriterion(userListCriteria);

// Create operation.
AdGroupCriterionOperation userListAdGroupCriterionOperation = 
    new AdGroupCriterionOperation();
userListAdGroupCriterionOperation.setOperand(userListBiddableAdGroupCriterion);
userListAdGroupCriterionOperation.setOperator(Operator.ADD);

AdGroupCriterionOperation[] criteriaOperations =
    new AdGroupCriterionOperation[] {userListAdGroupCriterionOperation};

// Add ad group criteria.
adGroupCriterionService.mutate(criteriaOperations);

Custom combination list

It’s also possible through the API to create LogicalUserLists, also known as custom combination lists in the AdWords interface. A LogicalUserList lets you group together other UserLists, which includes RemarketingUserLists and other LogicalUserLists, through a series of UserListLogicalRules. The following code shows you how to create a simple LogicalUserList that combines two other remarketing lists, but it’s possible to create more complex combinations using this type of list.

// Remarketing user lists to be referenced.
UserList list1 = new RemarketingUserList();
list1.setId(remarketingUserListId1);

UserList list2 = new RemarketingUserList();
list2.setId(remarketingUserListId2);

// Create logical user list.
LogicalUserList logicalList = new LogicalUserList();
logicalList.setName("Logical list #" + System.currentTimeMillis());
logicalList.setDescription("A list of two other lists");
logicalList.setMembershipLifeSpan(365L);
logicalList.setRules(new UserListLogicalRule[] {
    new UserListLogicalRule(UserListLogicalRuleOperator.ALL,
       new LogicalUserListOperand[] {
            new LogicalUserListOperand(null, list1),
            new LogicalUserListOperand(null, list2),
    })
});

// Create operation.
UserListOperation operation = new UserListOperation();
operation.setOperand(logicalList);
operation.setOperator(Operator.ADD);

UserListOperation[] operations = new UserListOperation[] {operation};

// Add user list.
UserListReturnValue result = userListService.mutate(operations);

Monitor the size of your list

You also might be interested in monitoring the growth of your list. You can accomplish this by making a simple get() call to the UserListService to retrieve this kind of information. The following code shows you how to retrieve information about all of your user lists.

// Create selector.
UserListSelector selector = new UserListSelector();

// Get all user lists.
UserListPage page = userListService.get(selector);

// Display user lists information.
if (page.getEntries() != null) {
  for (UserList userList : page.getEntries()) {
    System.out.printf("User list with name '%s' has an estimate size of '%d' users.\n",
        userList.getName(), userList.getSize());
  }
}

All code snippets included in this post are based on the AdWords API Java Client Library, other client libraries also include code examples and support for remarketing.

As always, please post your questions about how to use this new service on the forum.

Posted by David Torres, AdWords API Team

Enjoy more API units this holiday season

Wednesday, October 20, 2010


Last year, we provided you with a bonus of 20% more AdWords API units at no additional cost. We’re pleased to announce that we’re bringing back our holiday bonus this year. Starting now and extending through January 15, 2011, you’ll receive 20% more API units at no additional cost.

Here are the details:

  • Developers can purchase AdWords API units at the rate of 1250 for 0.25 USD, up from 1000 for 0.25 USD. Don’t forget that you can apply for preferred AdWords API pricing to receive free units.
  • Advertisers who are eligible for free units will receive credit at the rate of 300 units for every 1 USD of AdWords spend, up from 250 for every 1 USD of AdWords spend. They will be credited the holiday bonus based on their spend in previous months
You can view your AdWords API usage in your MCC account, by clicking the AdWords API Center link under the My Account tab.

You can use this as an opportunity to try out and take advantage of the many new features just released in version v201008. As always, we encourage you to use the AdWords API as efficiently as possible.

Happy Holidays,
Jason Shafton, Product Marketing Manager
on behalf of the entire AdWords API Team

Introducing two new ValueTrack parameters

Tuesday, October 19, 2010


We just announced two new ValueTrack parameters on the Inside AdWords blog. ValueTrack is a feature that helps you pass dynamic AdWords ad click information in your Destination URL. The new {matchtype} parameter provides you with the matchtype of the keyword for search ads (“b” for broad, “p” for phrase, and “e” for exact). The new {network} parameter gives you information about where the ad appeared (“g” for Google search, “s” for search partners, and “d” for display partners).

To learn more about how to implement all 13 ValueTrack parameters, visit the AdWords Help Center.

-- Jason Shafton, Product Marketing Manager

AdWords Downtime: October 16, 10am-2pm PDT

Thursday, October 14, 2010


We'll be performing routine system maintenance on Saturday, October 16 from approximately 10:00am to 2:00pm PDT. You won't be able to access AdWords or the API during this time frame, but your ads will continue to run as normal.

Best,
- Eric Koleda, AdWords API Team 

Discover v201008 - ServicedAccountService

Tuesday, October 12, 2010


The ability to retrieve a list of client accounts and their details is useful when you’re managing a large number of accounts using the AdWords API. This was previously possible only with AccountService in the legacy v13 version of the AdWords API. The v201008 version introduces the ServicedAccountService, which brings similar functionality to the new AdWords API. This blog post discusses the differences between the old AccountService and the new ServicedAccountService.

Retrieving client account hierarchy

In v13, you could retrieve the list of client accounts linked to an MCC using the getClientAccounts or getClientAccountInfos methods of AccountService. In the v201008 version of the AdWords API, you can use the get method of ServicedAccountService to get the same results. The following code shows the service in action.

// Get the ServicedAccountService.
ServicedAccountService servicedAccountService =
    (ServicedAccountService) user.GetService(AdWordsService.v201008.
        ServicedAccountService);
 
ServicedAccountSelector selector = new ServicedAccountSelector();
 
try {
  ServicedAccountGraph graph = servicedAccountService.get(selector);
 
  if (graph != null && graph.accounts != null) {
    // Display the accounts.
    Console.WriteLine("There are {0} customers under this account" +
        " hierarchy.", graph.accounts.Length);
    for (int i = 0; i < graph.accounts.Length; i++) {
      Console.WriteLine("{0}) Customer id: {1}\nLogin email: {2}\n" +
          "Company name: {3}\nIsMCC: {4}\n", i + 1,
          graph.accounts[i].customerId, graph.accounts[i].login,
          graph.accounts[i].companyName,
          graph.accounts[i].canManageClients);
    }
  }
} catch (Exception ex) {
  Console.WriteLine("Failed to retrieve accounts. Exception says \"{0}\"",
      ex.Message);
}

ServicedAccountService also allows you to retrieve links that exist between the accounts. To retrieve the links, set the enablePaging field of ServicedAccountSelector to false. The following code shows how to retrieve account links:

selector.enablePaging = false;
selector.serviceTypes = new ServiceType[] { ServiceType.UI_AND_API,
    ServiceType.API_ONLY };
...
...
if (graph != null && graph.accounts != null) {
  // Display the accounts.
  ...
  ...
  // Display the links.
  foreach (Link link in graph.links) {
      Console.WriteLine("There is a {0} link of type {1} from" +
         "{2:###-###-####} to {3:###-###-####}", link.typeOfLink,
         link.serviceType, link.managerId.id, link.clientId.id);
  }
}

An important difference between AccountService.getClientAccounts and ServicedAccountService.get is that getClientAccounts returns only the immediate child accounts, whereas ServicedAccountService.get() returns all of the accounts in the hierarchy. This was harder to do in v13 as you first had to call getClientAccountInfos to find out whether or not a child account is a manager, and then recursively call getClientAccounts to navigate the entire account hierarchy.

To retrieve only the immediate child accounts of your MCC to mimic the behavior of v13’s getClientAccounts method, you can select the relevant accounts as follows:

private Account[] GetClientAccountsForMCC(ServicedAccountGraph graph,
    long mccId) {
  List retval = new List();
  foreach (Link link in graph.links) {
    if (link.managerId.id == mccId) {
      foreach (Account account in graph.accounts) {
        if (account.customerId == link.clientId.id) {
          retval.Add(account);
        }
      }
    }
  }
  return retval.ToArray();
}

Retrieving and updating account information

In v13, you can retrieve and update some fields of a client account using getAccountInfo and updateAccountInfo. This functionality isn’t yet available in v201008, but will be available in a future version of the AdWords API.

Retrieving MCC alerts

In v13, you could retrieve the MCC alerts about your child accounts using getMccAlerts. This functionality is now available through AlertService, another new service introduced in v201008. We will write more about AlertService in a future post.

Please check out this service and share your feedback with us on the forum.

-- Anash P. Oommen, AdWords API Team

More features added to version v201008

Wednesday, September 29, 2010


Last week, we announced version v201008 of the AdWords API. Today, we’re releasing more new features as part of this version that we think you’ll be excited to start using.

Here’s what we’ve added:

  • Remarketing: Reach customers who have shown an interest in your product or service by visiting your site, and show them relevant ads across the Google Display Network.  Note that with this release, you can create new remarketing lists only by generating new remarketing tags.  We’ll add support for creating lists with existing remarketing and conversion tags in the next version of the API. Learn more about remarketing
  • My Client Center (MCC) features: Retrieve your MCC account hierarchy with the new ServicedAccountService
  • AlertService: Retrieve alerts for the AdWords accounts under your MCC account
  • Change History (beta): Use the new CustomerSyncService beta to get a list of entities that have changed over a specific period of time
You might have noticed that we’re adding support for new features faster than in the past. The new AdWords API architecture -- starting with version v200909 -- enables us to add features much more quickly. As we transition the remaining functionality of the old architecture (v13) to the new architecture, you can expect to see more frequent releases, and features available in the AdWords interface will be available in the API much sooner.

Please check out these new features and as always, share your feedback on the developer forum.

Posted by Jason Shafton, Product Marketing Manager

Discover v201008: ExperimentService

Tuesday, September 28, 2010


Access to manage AdWords Campaign Experiments (ACE) has been introduced through the ExperimentService as part of the v201008 version of the AdWords API. Now you are able to configure split test experiments for a campaign through the API. A split test experiment lets you more precisely measure the impact of changes to keywords, bids, ad groups, and placements before you apply them to all auctions. This reduces guesswork and lowers the potential risk. For common usage scenarios and more details, check out this blog post and the AdWords Help Center.

Lets get hands on with the code and see how to use ACE via the AdWords API. The following code snippets are available as part of the Perl client library.

Creating your experiment

You start by defining your experiment, which includes creating the experiment, attaching it to a campaign, setting its start time, end time, and percentage split. You then send an add operation through a service mutate call.

# Create experiment.
my $experiment = Google::Ads::AdWords::v201008::Experiment->new({
  campaignId => $campaign_id,
  name => "Interplanetary Experiment #" . POSIX::strftime("%s", localtime),
  queryPercentage => 10,
  startDateTime => POSIX::strftime("%Y%m%d %H%M%S", localtime)
});

# Create operation.
my $experiment_operation =
    Google::Ads::AdWords::v201008::ExperimentOperation->new({
      operator => "ADD",
      operand => $experiment
    });

# Add experiment.
my $result = $client->ExperimentService()->mutate({
  operations => [$experiment_operation]
});

# Valuable for ValueTrack usage
my $experiment_id = $experiments->get_entries()->[0]->get_id();
my $control_id = $experiments->get_entries()->[0]->get_controlId();

In the last few lines of code, experiment id and control id are extracted from the just-created experiment. These values are important for use with the ValueTrack tag used for campaign tracking and analytics since they uniquely identify which split each click is coming from. For details on using the ValueTrack tag with ACE please consult the AdWords Help Center.

Defining your experimental changes

Now lets apply some bid changes to an existing ad group and assign it to both the experimental and the control splits by using the flag named experimentDeltaStatus. Applying changes to other parts of your campaign are very similar to the following example, refer to the AdGroupExperimentData and the BiddableAdGroupCriterionExperimentData objects in documentation for more information about the experimental changes that can be applied to ad groups and criteria respectively. It is also worth to mention that new ad groups and new criteria can be added to your campaign experiment executing add operations and setting the experimentData to your new objects. You can also check a variety of experiment examples which we included in client libraries.

# Set ad group for the experiment.
my $ad_group = Google::Ads::AdWords::v201008::AdGroup->new({
  id => $ad_group_id
});

# Create experiment bid multiplier rule that will modify ad group bid for the
# experiment.
my $bid_multiplier = 
    Google::Ads::AdWords::v201008::ManualCPCAdGroupExperimentBidMultipliers->
        new({
          maxCpcMultiplier => Google::Ads::AdWords::v201008::BidMultiplier->
              new({
                multiplier => 1.5
              })
        });

# Set experiment data to the ad group.
my $experiment_data =
    Google::Ads::AdWords::v201008::AdGroupExperimentData->new({
      experimentId => $experiment_id,
      experimentDeltaStatus => "MODIFIED",
      experimentBidMultipliers => $bid_multiplier
    });
$ad_group->set_experimentData($experiment_data);

# Create operation.
my $operation = Google::Ads::AdWords::v201008::AdGroupOperation->new({
  operand => $ad_group,
  operator => "SET"
});

# Update ad group.
$experiments = $client->AdGroupService()->mutate({
  operations => [$operation]
});

Deciding to promote or discard your experiment

After you’ve assessed the performance impact of the experimental changes, you can promote or delete the experiment. The following example shows you how to promote your experiment, effectively applying all experimental changes to 100% of your campaign traffic.

# Set experiment's status to PROMOTED.
my $experiment = Google::Ads::AdWords::v201008::Experiment->new({
  id => $experiment_id,
  status => "PROMOTED"
});

# Create operation.
my $experiment_operation =
    Google::Ads::AdWords::v201008::ExperimentOperation->new({
      operator => "SET",
      operand => $experiment
    });

# Update experiment.
my $result = $client->ExperimentService()->mutate({
  operations => [$experiment_operation]
});

Promoting an experiment applies all changes in the experimental split to your campaign. All control only elements become paused. Everything else running in both control and experiment splits continues running as normal.

If you don’t like the performance impact you see with the experiment, you can delete it by sending a mutate operation with the experiment status set as DELETED. Deleting an experiment will effectively discard any changes and additions assigned to the experimental split.

Like other products in beta, ACE has some core features still undergoing active development. Reporting is one area that’s getting special attention. Currently, getting performance data segmented by experiment and control splits is not supported through the AdWords API. Until it’s available, you can pull click and conversion data for each split from a tracking system using ValueTrack tags, as described above. Alternatively, experiments can be implemented so that every ad group is either control only or experiment only. You can then aggregate results for each campaign split using each ad group’s experimentDeltaStatus and check for statistically significant differences. A final interim solution is for users to log into the AdWords UI to check segmented performance stats.

If you have any questions about how to use this service we’ll be happy to address them on the forum. Have fun experimenting.

Best,
- David Torres, AdWords API Team

New codes and content in the documentation

Thursday, September 23, 2010


We’ve been hard at work updating the existing documentation and adding new resources. Here are some of the recent changes:

All of the code lists are available in CSV format to make it easier to integrate into your applications.

We’re always looking for new ways to improve our documentation, so if you have any ideas, let us know on the forum.

Best,
- Eric Koleda, AdWords API Team

Announcing Four AdWords API workshops

Friday, September 17, 2010


Join the AdWords API engineering team for a day of hacking on the latest features. Events will be held in the following cities:

  • London, September 28th
  • Hamburg, September 30th
  • San Francisco, October 7th
  • New York, October 12th
In addition to answering all of the questions you can ask us, we’ll be presenting technical deep dives of:
  • The new CustomerSyncService (Change History)
  • AdWords Campaign Experiments
  • The new TrafficEstimatorService
  • The completely rewritten (forthcoming) Java Client Library
  • Migrating from v13 to v2010 reports
  • New Ad Extensions and Product Ads
All events will have the same agenda, will run from 10:00 AM until 5:00 PM (local time), and will be geared towards software developers.

Seats are limited. For more information and to register, visit: https://sites.google.com/site/awapiworkshops/

-- Aaron Karp, AdWords API Team

Discover v201008: TrafficEstimatorService upgrade

Wednesday, September 15, 2010


The TargetingIdeaService is a powerful tool for generating new keyword ideas and getting statistics about how those keywords perform globally. However, for more personalized estimates of how a keyword may perform in your account, the TrafficEstimatorService can be used. While this service was previously only available in the legacy v13 version of the AdWords API, it’s now available in v201008 with some slight changes.

Estimate Requests

While the v13 service provided separate methods for getting keywords estimates with and without an ad group or campaign context, the v201008 version has been simplified to use just a single get() method. This means that KeywordEstimateRequest objects must always be wrapped in AdGroupEstimateRequest and CampaignEstimateRequest objects, even if you don’t want to use the features those objects offer. Keep in mind though that specifying contextual information in estimate requests, especially the ids of existing ad groups and campaigns, can lead to more accurate estimates and is recommended when possible.

The CampaignEstimateRequest object allows you to set or override the targeting options for the estimation using the targets field. While this field accepts an array of any target type only the following are currently supported: LanguageTarget and GeoTarget, which itself is limited to only CountryTarget, CityTarget, MetroTarget, and ProvinceTarget. More information about the behavior of targeting is available here.

The following PHP example code demonstrates how to build up the estimate requests for three new keywords in a new ad group with a default bid of $1, in a new campaign that targets users in the US who speak English.

  // Create keywords. Up to 2000 keywords can be passed in a request.
  $keywords = array();
  $keywords[] = new Keyword('mars cruise', 'BROAD');
  $keywords[] = new Keyword('cheap cruise', 'PHRASE');
  $keywords[] = new Keyword('cruise', 'EXACT');

  // Create a keyword estimate request for each keyword.
  $keywordEstimateRequests = array();
  foreach ($keywords as $keyword) {
    $keywordEstimateRequest = new KeywordEstimateRequest();
    $keywordEstimateRequest->keyword = $keyword;
    $keywordEstimateRequests[] = $keywordEstimateRequest;
  }

  // Create ad group estimate requests.
  $adGroupEstimateRequest = new AdGroupEstimateRequest();
  $adGroupEstimateRequest->keywordEstimateRequests = $keywordEstimateRequests;
  $adGroupEstimateRequest->maxCpc = new Money(1000000);
  $adGroupEstimateRequests = array($adGroupEstimateRequest);

  // Create campaign estimate requests.
  $campaignEstimateRequest = new CampaignEstimateRequest();
  $campaignEstimateRequest->adGroupEstimateRequests = 
      $adGroupEstimateRequests;
  $campaignEstimateRequest->targets = array(new CountryTarget('US'),
      new LanguageTarget('en'));
  $campaignEstimateRequests = array($campaignEstimateRequest);

  // Create selector.
  $selector = new TrafficEstimatorSelector();
  $selector->campaignEstimateRequests = $campaignEstimateRequests;

Estimated Statistics

While the v13 KeywordEstimate object contained both the minimum and maximum estimated statistics, it has been split into a min and max StatsEstimate object in the v201008 version. This object bundles together the estimated average CPC, average position, clicks and total cost. To get values similar to those returned by the Traffic Estimator web interface you will need to get the mean of the min and max estimates.

The following code shows how to get the estimates for the selector created above and display the results.

  // Get traffic estimates.
  $result = $trafficEstimatorService->get($selector);

  // Display traffic estimates.
  if (isset($result)) {
    $keywordEstimates =
        $result->campaignEstimates[0]->adGroupEstimates[0]->keywordEstimates;
    for ($i = 0; $i < sizeof($keywordEstimates); $i++) {
      $keyword = $keywordEstimateRequests[$i]->keyword;
      $keywordEstimate = $keywordEstimates[$i];

      // Find the mean of the min and max values.
      $meanAverageCpc = ($keywordEstimate->min->averageCpc->microAmount
          + $keywordEstimate->max->averageCpc->microAmount) / 2;
      $meanAveragePosition = ($keywordEstimate->min->averagePosition
          + $keywordEstimate->max->averagePosition) / 2;
      $meanClicks = ($keywordEstimate->min->clicks
          + $keywordEstimate->max->clicks) / 2;
      $meanTotalCost = ($keywordEstimate->min->totalCost->microAmount
          + $keywordEstimate->max->totalCost->microAmount) / 2;

      printf("Results for the keyword with text '%s' and match type '%s':\n",
          $keyword->text, $keyword->matchType);
      printf("  Estimated average CPC: %d\n", $meanAverageCpc);
      printf("  Estimated ad position: %.2f \n", $meanAveragePosition);
      printf("  Estimated daily clicks: %d\n", $meanClicks);
      printf("  Estimated daily cost: %d\n\n", $meanTotalCost);
    }
  } else {
    print "No traffic estimates were returned.\n";
  }

Checking Keyword Traffic

The v13 service included a checkKeywordTraffic() method that could be used to determine if a keyword would get any traffic at all. This method has been removed from the v201008 service, but information about the search volume for a keyword can be obtained by using the TargetingIdeaService and requesting attributes such as AVERAGE_TARGETED_MONTHLY_SEARCHES, GLOBAL_MONTHLY_SEARCHES, or TARGETED_MONTHLY_SEARCHES.

If you have any questions about how to use this service we’ll be happy to address them on the forum.

Best,
- Eric Koleda, AdWords API Team

Introducing AdWords API version v201008

Tuesday, September 14, 2010


Today, we’re announcing a new version of the AdWords API: v201008. Following up on our release of v201003, this version includes many requested features, as well as TrafficEstimatorService and ReportDefinitionService (now out of beta). These two services will replace their v13 counterparts. While we’re not starting the sunset countdown for the v13 TrafficEstimatorService and ReportService just yet, we’d like to give you a heads-up that these services will be shut down in the first half of 2011. We’ll announce specific dates on this blog in the near future.

Here’s the list of what’s new in version v201008:

  • Reports: Now out of beta, the ReportDefinitionService service has what you need to receive detailed reporting for your AdWords accounts. With this, the rates for ReportDefinitionService have also been updated. Support for cross-client reports is expected in the next version of the API.
  • Traffic Estimates: An all-new version of the TrafficEstimatorService that will replace the existing v13 service.
  • Product Ads: Includes both Product Extensions and Product Listing Ads that feature rich product information, such as a product image, directly in your ads.  Note that you’ll need to use the AdWords web interface to link your campaigns with your Google Merchant Center account. We’ll add this feature to the API in an upcoming version. Also note that Product Listing Ads are not yet available to all AdWords users.
  • AdWords Campaign Experiments: Accurately test and measure changes to your keywords, bids, ad groups and placements by running split tests. Learn more
  • Ad group bid simulator (bid landscapes): Get bid simulator data at the ad group level with two sets of simulations: estimated results for changing the ad group default bid and estimated results for applying a single bid to all the keywords within that ad group. Learn more
  • Enhanced CPC: Dynamically raise or lower your Max. CPC bids to acquire more conversions at or below your current CPA. Learn more
  • Partial failure support for AdGroupCriterionService (beta): This functionality has been frequently requested by developers over the years and we’re excited to launch it in beta while we continue to develop and improve upon it. We’ll launch partial failure support for other key services in upcoming versions of the API.
We are also working on a couple of other new features that will be added to v201008 in the coming weeks.

The AdWords API team is working hard to bring you the features you’ve been asking for and we’re excited to hear what you think about the v201008 version of the API. We encourage you to start working with this new version and share your feedback with us on the developer forum.

Posted by Jason Shafton, Product Marketing Manager

Discover v201003: Using Ad Sitelinks

Thursday, September 09, 2010


Introduction
Ad Sitelinks is a feature for search-based ads that allows you to extend the value of your existing AdWords ads by providing additional links to specific, relevant content deep within your website. Rather than sending all users to the same landing page, Sitelinks can provide up to 4 additional links for the user to choose from, as shown below.

(View larger image)

By providing users with more options, you can create richer, more relevant ads that improve the value of your brand terms and other targeted keywords. You can learn more about Sitelinks in the AdWords Help Center.

Using Ad Sitelinks in AdWords API v201003
Sitelinks is supported starting with the v201003 version of the AdWords API. Sitelinks is implemented as campaign ad extensions, and can be managed using the CampaignAdExtensionService. Keep in mind that each campaign can have only one CampaignAdExtension containing a SiteLinkExtension. This means that prior to adding Sitelinks to your campaign, you should check if the campaign already has one, and remove it if does. The C# code snippet below shows how to retrieve the campaign ad extension containing active Sitelinks for a given campaign.

private long? GetActiveSitelinkExtension(long campaignId) {
 long? siteLinkExtensionId = null;

 // Get the campaign ad extension containing sitelinks.
 CampaignAdExtensionSelector selector = new CampaignAdExtensionSelector();
 selector.campaignIds = new long[] { campaignId };
 selector.statuses = new CampaignAdExtensionStatus[] {
CampaignAdExtensionStatus.ACTIVE };

 CampaignAdExtensionPage page = campaignExtensionService.get(selector);
 if (page != null && page.entries != null) {
   foreach (CampaignAdExtension extension in page.entries) {
      if (extension.adExtension is SitelinksExtension) {
         siteLinkExtensionId = extension.adExtension.id;
         break;
      }
   }
 }
 return siteLinkExtensionId;
}

If GetActiveSitelinkExtension returns null, then you can remove the existing SitelinkExtension as shown below.

private SitelinksExtension RemoveActiveSitelinkExtension(
   long campaignId, long siteLinkExtensionId) {
 CampaignAdExtension campaignAdExtension = new CampaignAdExtension();
 campaignAdExtension.campaignId = campaignId;
 campaignAdExtension.adExtension = new AdExtension();
 campaignAdExtension.adExtension.id = siteLinkExtensionId;

 CampaignAdExtensionOperation operation = new CampaignAdExtensionOperation();
 operation.@operator = Operator.REMOVE;
 operation.operand = campaignAdExtension;
 CampaignAdExtensionReturnValue retVal =
     campaignExtensionService.mutate(new CampaignAdExtensionOperation[] {
         operation });
 if (retVal != null && retVal.value != null && retVal.value.Length > 0 &&
     retVal.value[0].adExtension is SitelinksExtension) {
   return (SitelinksExtension) retVal.value[0].adExtension;
 } else {
   return null;
 }
}

You can now add new siteLinks as shown below.

private SitelinksExtension AddSitelinks(long campaignId, Sitelink[]
   siteLinks) {
 SitelinksExtension siteLinkExtension = new SitelinksExtension();
 siteLinkExtension.sitelinks = siteLinks;

 CampaignAdExtension campaignAdExtension = new CampaignAdExtension();
 campaignAdExtension.adExtension = siteLinkExtension;
 campaignAdExtension.campaignId = campaignId;

 CampaignAdExtensionOperation operation = new CampaignAdExtensionOperation();
 operation.@operator = Operator.ADD;
 operation.operand = campaignAdExtension;

 CampaignAdExtensionReturnValue retVal =
     campaignExtensionService.mutate(new CampaignAdExtensionOperation[] {
         operation });
 if (retVal != null && retVal.value != null && retVal.value.Length > 0) {
   return retVal.value[0].adExtension as SitelinksExtension;
 } else {
   return null;
 }
}

The following snippet puts it all together.

Sitelink siteLink1 = new Sitelink();
siteLink1.displayText = "Birthday Flowers";
siteLink1.destinationUrl = "http://www.flowers.com/birthday";

Sitelink siteLink2 = new Sitelink();
siteLink2.displayText = "Deal of the day";
siteLink2.destinationUrl = "http://www.flowers.com/deals/today";

long? linkId = GetActiveSitelinkExtension(campaignId);
if (linkId != 1) {   
 RemoveActiveSitelinkExtension(campaignId, linkId);
}
AddSitelinks(campaignId, new Sitelink[] { siteLink1, siteLink2});

As always, the latest API unit costs for adding, removing or or retrieving Sitelinks is available in the AdWords API rate sheet under the CampaignAdExtensionService.

We've included support for Sitelinks in each of our client libraries to help you get started, so please try it out and share your feedback with us on the forum.

-- Anash P. Oommen, AdWords API Team