Posted:
As part of our ongoing efforts to improve the performance of AdWords scripts, we are making some changes to the way we provide execution logs for scripts.

Starting May 15, 2014, we will only retain detailed execution logs for scripts that ran during the last 90 days. If you need to retrieve changes made by scripts from an earlier date, you can refer to your account’s change history.

If you have any questions about this change, let us know on the forum or on our Google Ads Developers Google+ page.

Posted:

Update (March 24, 2015): Per recent policy updates, interstitial ads on app-load are not recommended. See the AdMob help center for the latest interstitial policy guidance.

Previously, we showed you how to create a splash screen interstitial on iOS. Today we’ll discuss how to trigger an interstitial on an Android app launch.

The cleanest solution is to have an app launch Activity for showing the splash screen image and loading/showing the interstitial. This special splash screen activity should be the activity that launches when the user starts the app. In the splash screen activity’s onCreate method, the first task is to make an ImageView and set it as the content view.

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  ImageView image = new ImageView(this);
  // Assumes you have a resource with the name kitten.
  image.setImageResource(R.drawable.kitten);
  setContentView(image);

Next, create and load the interstitial ad. Setting an AdListener is needed in order to know when the ad succeeds or fails to load. If the interstitial loads within a reasonable time limit, we’ll show it. If it fails, we’ll move on to the main activity.

  interstitial = new InterstitialAd(this);
  interstitial.setAdUnitId(AD_UNIT_ID);
  interstitial.setAdListener(new AdListener() {
    @Override
    public void onAdLoaded() {
      if (!interstitialCanceled) {
        waitTimer.cancel();
        interstitial.show();
      }
    }

    @Override
    public void onAdFailedToLoad(int errorCode) {
      startMainActivity();
    }
  });

The “reasonable time limit” mentioned earlier is enforced by creating a timer to stop waiting for the interstitial. If the interstitial doesn’t load fast enough (in this case 5 seconds), we skip it and proceed to the main activity.

  waitTimer = new Timer();
  waitTimer.schedule(new TimerTask() {
    @Override
    public void run() {
      interstitialCanceled = true;
      SplashScreenActivity.this.runOnUiThread(new Runnable() {
        @Override
        public void run() {
          startMainActivity();
        }
      });
    }
  }, 5000);
} // end of onCreate implementation.

private void startMainActivity() {
  Intent intent = new Intent(this, MainActivity.class);
  startActivity(intent);
  finish();
}

Handing Early Exits

The code so far assumes that the user will stay in the activity for the duration of the interstitial loading. But what if the user hits the back or home button before the interstitial loads or the timer goes off? The interstitial will actually continue loading and could be shown on top of the home screen! That would be a poor user experience.

We account for this edge case by implementing the onPause and onResume methods from the activity lifecycle. In onPause, we stop the timer and set the interstitialCanceled flag so the interstitial doesn’t get immediately shown. In onResume, we show the interstitial if it’s ready when the user returns to the app again, otherwise we start the main activity.

@Override
public void onPause() {
  waitTimer.cancel();
  interstitialCanceled = true;
  super.onPause();
}

@Override
public void onResume() {
  super.onResume();
  if (interstitial.isLoaded()) {
    interstitial.show();
  } else if (interstitialCanceled) {
    startMainActivity();
  }
}

If you’re wondering why interstitialCanceled is checked again before starting the main application, it’s because onResume gets called immediately after onCreate the first time the app is loaded. And on the first app launch, we do want to wait for the interstitial to load.

A complete implementation is available in our googleads-mobile-android-examples repo on GitHub. Give us a shout on the forum if you have any questions about implementing the Google Mobile Ads SDK in your mobile applications. You can also find us on Google+.

Posted:
We are making a minor change to the values returned in the ClickType column in AdWords API reports. Starting the week of May 26th, 2014, AdWords API reports will return "Product listing ad" instead of "Offer" as the display value for click types corresponding to Product Listing Ads.

If you depend on the ClickType field, verify that your applications won't be negatively affected by the change in value.

If you have questions or feedback about this change, let us know on our forum or via the Google Ads Developers Google+ page.

Posted:

After successfully removing several older versions of the DFP API earlier this month, we’re continuing our 'Spring Cleaning' by reminding everyone that support for version v201302 or earlier of the DFP API will end on August 1st, 2014. If you are still using one of those versions after that date, all requests will fail and your application will cease to work until you migrate to a supported version. Please reference the release notes for all changes to the API when migrating to a newer version.

In addition, please note that ClientLogin support is being phased out and that you will have to migrate to OAuth 2.0 in order to authenticate starting with v201403. Please reference our OAuth 2.0 implementation guide for help with this process.

If you have any questions about this upcoming change or anything else related to the DFP API, please contact us on the forum or via our Google+ page.

Posted:
This is a reminder that the CONVERSION DURATION THRESHOLD placeholder is sunsetting starting on April 28th, 2014.

Starting on the sunset date, AdWords accounts will be migrated to use the new CONVERSION TYPE ID placeholder for a four-week time period. During this period, some AdWords accounts that you access via the AdWords API may be migrated, and some may not. Read on to find out how to determine if a feed has been migrated or not. Migration of all AdWords accounts is scheduled to complete by end of May, 2014.

How will an existing conversion duration threshold be migrated?

If you used CONVERSION DURATION THRESHOLD in a Feed Mapping prior to the sunset date, then we will automatically migrate those values after the sunset date - and they will be accessible via the AdWords API v201402 as follows:
  • New AdCallMetricsConversion will be automatically created for you with the existing conversion duration threshold.

    Note: If you have 3 feed items with call duration threshold, where two of them are set to 120 seconds, and one of them are set to 30 seconds, then only two AdCallMetricsConversion will be created - one with AdCallMetricsConversion.phoneCallDuration set to 120 seconds, and another one set to 30 seconds.

  • Feed attributes using CONVERSION DURATION THRESHOLD will remain, setting and reading those values will not produce errors. However, any associated feed attribute values will not be honored.

  • A new feed attribute will be automatically created in your existing feed, and it will be automatically be mapped to the new CONVERSION TYPE ID placeholder type.

  • The new conversion type ID feed attribute will be associated to the newly created AdCallMetricsConversion.

Note that in order to get the name and field attribute ID of the newly created conversion type ID field attribute, you should use the FeedMappingService to determine the feed attribute ID that is associated with the CONVERSION TYPE ID placeholder type.

How do I know if a Feed has been migrated?

If a feed has been migrated, you will be able to see that the Feed has an attribute associated to the CONVERSION TYPE ID placeholder type. You can use the FeedMappingService to determine this.

How do I set conversion duration threshold on a migrated Feed?

If you’d like to continue using conversion duration threshold, then you must upgrade to the AdWords API v201402.

In a migrated feed, the CONVERSION TYPE ID feed item attribute value refers to an AdCallMetricsConversion object by ID. To set the conversion duration threshold, you must update the associated AdCallMetricsConversion object’s phoneCallDuration field. Please note that doing this will impact other feed items that are associated to the same AdCallMetricsConversion object.

Alternatively, you can also associate the CONVERSION TYPE ID feed item attribute value to a different AdCallMetricsConversion:
  • Re-use an existing AdCallMetricsConversion that has the right phone call duration
  • Use the ConversionTrackerService to create new AdCallMetricsConversion with the phoneCallDuration field set
In a migrated feed, if you continue to set the attribute value associated with the CONVERSION DURATION THRESHOLD placeholder, then those values will be ignored.

Can I start using the new placeholder before the Feed gets migrated?

Yes. Starting from the sunset date, you will be able to use the new CONVERSION TYPE ID placeholder in your feed. If you are using the placeholder in your feed and set the attribute value to a valid AdCallMetricsConversion ID, then the CONVERSION TYPE ID value will be used, and the CONVERSION DURATION THRESHOLD value will be ignored.

If you choose to do this prior to the automatic migration - don’t worry. The automatic migration process will not overwrite your existing CONVERSION TYPE ID values with CONVERSION DURATION THRESHOLD values.

We are here to help

If you have any questions about this upcoming change or anything else related to the AdWords API, please contact us on the AdWords API forum or via the Google Ads Developers Google+ page.

Posted:
On May 5, 2014, in the spirit of spring cleaning, the IMA Flash SDK will be dropping support for several rarely-used formats and SDKs:

  • VAST 1 XML format. The IMA Flash SDK will continue to support VAST 2 and will continue to improve on its existing beta support for VAST 3. For more information about what versions of VAST, VPAID and VMAP the IMA Flash SDK supports, visit the Video Features and SDK Compatibility page.
  • Flash-in-Flash (F2F) creatives. VPAID, the industry standard for interactive video ads, is the recommended alternative for replacing F2F creatives. The IMA Flash SDK currently supports VPAID 1 and has beta support for VPAID 2. 
  • IMA Flash SDK v1. This version of the SDK will no longer be guaranteed to work and won't receive bug fixes, updates, or responses to support issues. V3 of the IMA Flash SDK will be the only fully supported version of the SDK.
  • Dart Shell SDK. This DoubleClick SDK will no longer be guaranteed to work and won't receive bug fixes, updates, or responses to support issues.

What will happen if VAST 1 or an F2F creative is sent to the SDK?

If a VAST 1 ad tag response or F2F creative is sent to the SDK, the SDK will raise an AdErrorEvent with an appropriate error. For example, a VAST 1.0 response will return an AdErrorCodes.VAST_PARSING_ERROR which can be handled with the following ActionScript modified from the Flash IMA SDK example app:

  // ...
  import com.google.ads.ima.api.AdErrorCodes;

  private function initAdsLoader():void {
    // ...
    adsLoader.addEventListener(AdErrorEvent.AD_ERROR, adsLoadErrorHandler);
  }

  private function adsLoadErrorHandler(event:AdErrorEvent):void {
    // ...
    if (event.error.errorCode == AdErrorCodes.VAST_PARSING_ERROR) {
      trace("Invalid VAST format."); 
      // Error handling logic...
    }
  }

Other questions?

Questions about these support changes or other questions about the Flash IMA SDK? Check out the IMA SDK Flash Quick Start Guide or drop us a line on the IMA SDK forum. Follow our Google+ page for other announcements and updates.

Posted:
In our previous blog post on exception handling in the DFP API, we went over the first of two major scenarios where adding exception handling can help you troubleshoot issues when they occur. In this blog post, we finish off by discussing how to handle exceptions that occur when retrieving entities.

Retrieving entities

When retrieving entities, you may have run into errors such as ServerError, QuotaError, or client library specific errors like RemoteException. Generally these errors are not caused by user error, but caused by the API server being busy, or by issues with the entities you are retrieving being too large or corrupted. If you’re already following our best practices of paging through your entities with our suggested page size and are still running into these errors from time-to-time, you can tackle these issues by writing code to handle the exceptions.

The general way we recommend you reduce the page size, wait a few seconds, and then retry the request to handle most errors you can receive. If it continues to fail, repeat this process up to five times. This will give you an idea of whether the failure is a temporary server error, or if there is a deeper issue. The following is an example that augments the GetAllLineItemsExample from the Google Ads API Java Client Library showing how to do this. It reduces the page size for each retry by halving it, and starts off the wait time between retries at two seconds, doubling it with each retry.
  private static final int MAX_RETRY_LIMIT = 5;
  private static final long BASE_WAIT_TIME = 2000;

  public static void runExample(DfpServices dfpServices, DfpSession session)
      throws Exception {
    int pageLimit = StatementBuilder.SUGGESTED_PAGE_LIMIT;

    // Get the LineItemService.
    LineItemServiceInterface lineItemService =
        dfpServices.get(session, LineItemServiceInterface.class);
    // Create a statement to select all line items.
    StatementBuilder statementBuilder =
        new StatementBuilder().orderBy("id ASC").limit(pageLimit).offset(0);

    // Default for total result set size.
    int totalResultSetSize = 0, retryCount = 0;
    LineItemPage page = null;
    long waitTime = BASE_WAIT_TIME;

    do {
      // Reset these values for each page.
      retryCount = 0;
      pageLimit = StatementBuilder.SUGGESTED_PAGE_LIMIT;
      waitTime = BASE_WAIT_TIME;
      page = null;

      do {
        try {
          System.out.printf(
              "Getting line items with page size of %d at offset %d (attempt " +
                  "%d)...\n", pageLimit,
              statementBuilder.getOffset(), retryCount + 1);
          // Get line items by statement.
          page = lineItemService.getLineItemsByStatement(
              statementBuilder.toStatement());
          System.out.printf("Attempt %d succeeded.\n\n", retryCount + 1);
        } catch (Exception e) {
          pageLimit /= 2;
          System.out.printf(
              "Attempt %d failed. Retrying in %d milliseconds with page size " +
                  "of %d...\n",
              retryCount + 1, waitTime, pageLimit);
          Thread.sleep(waitTime);
          waitTime *= 2;
          statementBuilder.limit(pageLimit);
          retryCount++;
        }
      } while (page == null && retryCount < MAX_RETRY_LIMIT);

      if (page != null) {
        totalResultSetSize = page.getTotalResultSetSize();
        // Your code to handle the line item page, e.g., save it to a database.
      } else {
        throw new Exception(String.format(
            "Failed to get line items at offset %s with page size %d after " +
                "%d attempts.",
            statementBuilder.getOffset(), pageLimit, retryCount));
      }

      statementBuilder.increaseOffsetBy(pageLimit);
    } while (statementBuilder.getOffset() < totalResultSetSize);

    System.out.printf("Number of results found: %d\n", totalResultSetSize);
  }
Using the above code, the output would look like the following if the first three attempts failed, but the fourth one succeeded for the first page.
Getting line items with page size of 500 at offset 0 (attempt 1)...
  Attempt 1 failed. Retrying in 2000 milliseconds with page size of 250...
  Getting line items with page size of 250 at offset 0 (attempt 2)...
  Attempt 2 failed. Retrying in 4000 milliseconds with page size of 125...
  Getting line items with page size of 125 at offset 0 (attempt 3)...
  Attempt 3 failed. Retrying in 8000 milliseconds with page size of 62...
  Getting line items with page size of 62 at offset 0 (attempt 4)...
  Attempt 4 succeeded.

  Getting line items with page size of 500 at offset 62 (attempt 1)...
  Attempt 1 succeeded.
If you’re getting exceptions that weren't addressed by our examples and are unclear on how to diagnose them, then you can always write to us on the API forums. Include the requestId of the call that failed, especially in the case that you receive a SERVER_ERROR.

Posted:
Starting on April 22nd, 2014, a v201309 or v201402 AdGroupBidModifierService.mutate request will fail with a CriterionError and reason CANNOT_BID_MODIFY_CRITERION_TYPE if all of the following conditions are met for the same criterion:
  • The criterion is a Platform criterion for the mobile platform ID (30001)
  • The Campaign has a CampaignCriterion for the mobile platform criterion with a bidModifier set to 0 (this is displayed as a Bid adj. of -100% under Settings in the AdWords UI)
  • The AdGroupBidModifier has the same mobile platform criterion and attempts to set the bidModifier to any value other than 0
The AdWords API and UI will start rejecting such requests because allowing this combination could give the impression that the ad group will serve ads for the mobile platform criterion when in fact it will not.

For example, assume you create a campaign with a CampaignCriterion containing the following criterion and bid modifier:
<criterion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:type="Platform">
    <id>30001</id>
    <type>PLATFORM</type>
    <Criterion.Type>Platform</Criterion.Type>
    <platformName>HighEndMobile</platformName>
</criterion>
<!-- This will appear as "-100%" in the UI. -->
<bidModifier>0.0</bidModifier>
If you attempt to create an AdGroupBidModifier containing the following criterion and bid modifier for any ad group in the campaign on or after April 22nd, 2014, it will fail because the non-zero ad group bid modifier of 1.25 would have no effect:
<criterion xmlns:ns2="https://adwords.google.com/api/adwords/cm/v201402"
  xsi:type="ns2:Platform">
    <ns2:id>30001</ns2:id>
</criterion>
<!-- This will appear as "+125%" in the UI.  Any other non-zero
     value will also fail. -->
<bidModifier>1.25</bidModifier>
Before April 22nd, 2014, please take the following actions to ensure a smooth transition for your application:
  • Make sure that your application will be able to properly handle the error
  • Examine your existing campaigns and ad groups and address any bid modifiers that meet the conditions above
Not using bid adjustments in your campaigns and ad groups? Check out our bid modifiers guide to learn how to use this powerful feature of AdWords.

Still have questions? Feel free to visit us on the AdWords API Forum or our Google+ page.

Posted:
We’ve long worked to keep your searches on Google secure. We provided SSL encryption for signed-in searches in 2011 and have rolled that out to searches from the omnibox in the Chrome browser. Today, we are extending our efforts to keep search secure by removing the query from the referer on ad clicks originating from SSL searches on Google.com.

Advertisers will continue to have access to useful data to optimize and improve their campaigns and landing pages. For example, you can access detailed information in the AdWords search terms report and the Google Webmaster Tools Search Queries report.

Best practices

The AdWords search terms report (previously known as the search query performance report) lets you see search queries that generated ad clicks along with key performance data. And the Search Queries report available in Google Webmaster Tools provides aggregate information about the top 2000 queries, each day, that generated organic clicks.

If you use the query in the referer for reporting, automated keyword generation or landing page customization, then we suggest using the following alternatives: We understand that some partners may need to make changes to their systems and operations, but we think that this is the right path forward for the security of our users searching on Google.com.

Posted:
We often get a lot of questions about running into exceptions when using the DFP API and what to do about them. Seeing an exception from time-to-time is a normal part of the API workflow and should be expected. Some exceptions are caused by user errors, others by server issues. We strongly recommend that you write code to handle exceptions early on so that it’s easier to troubleshoot issues when they occur. In this blog post, we’ll go over the first of two major scenarios where adding exception handling will benefit you.

Creating or updating entities

When creating or updating entities with the DFP API, if you forget to supply a required field or set a field to an invalid value, the API throws an ApiException. If you write code to catch this exception, you can print out the error message to show the root cause. The following is an example showing how to do this when using the Google Ads API Java Client Library to create line items.

  // Create a line item.
  LineItem lineItem = new LineItem();
  // lineItem.setSomeThings(...);

  try {
    // Create the line item on the server.
    lineItemService.createLineItems(new LineItem[] {lineItem});
  } catch (ApiException e) {
    ApiError[] apiErrors = e.getErrors();
    for (ApiError apiError : apiErrors) {
      StringBuilder errorMessage = new StringBuilder();
      errorMessage.append(String.format(
          "There was an error of type '%s', on the field '%s',"
          + "caused by an invalid "
          + "value '%s', with the error message '%s'",
          apiError.getApiErrorType(), apiError.getFieldPath(),
          apiError.getTrigger(), apiError.getErrorString()));
      if (apiError instanceof NotNullError) {
        errorMessage.append(String.format(", with the reason '%s'.",
            ((NotNullError) apiError).getReason()));
      } else if (apiError instanceof FrequencyCapError) {
        errorMessage.append(String.format(", with the reason '%s'.",
            ((FrequencyCapError) apiError).getReason()));
      }

      // Append details of other ApiErrors that you are interested in.

      System.err.println(errorMessage.toString());
    }
  }
If you use this code to create a line item, it prints the following if you don’t specify an order ID:
There was an error of type 'NotNullError', on the field 'lineItem[0].orderId',
  caused by an invalid value '', with the error message 'NotNullError.NULL',
  with the reason 'NULL'.
If you provide invalid values for the frequency caps field when creating a line item, you’ll get the following:
There was an error of type 'FrequencyCapError', on the field
  'lineItem[0].frequencyCaps', caused by an invalid value '1000', with the
  error message 'FrequencyCapError.RANGE_LIMIT_EXCEEDED', with the reason
  'RANGE_LIMIT_EXCEEDED'.
There was an error of type 'FrequencyCapError', on the field
  'lineItem[0].frequencyCaps', caused by an invalid value '0', with the error
  message 'FrequencyCapError.IMPRESSIONS_TOO_LOW', with the reason
  'IMPRESSIONS_TOO_LOW'.
Depending on the type of application you’re writing, you may want to show these error messages to your user in a UI or log them. Note that you can find an entity’s required fields and valid field values in our reference documentation, for example, LineItem.

If you’re getting exceptions when creating or updating entities that are not ApiException errors (such as a SERVER_ERROR) and are unclear on how to diagnose them, then you can always write to us on the API forums and include the requestId of the call that failed and we will help you diagnose the issue.

Look forward to part II of this blog post where we will discuss handling exceptions that are thrown when retrieving entities.

Posted:
We're pleased to announce that the AdWords API Workshops are coming back for another round in April and May of 2014. Registration is now open!

Workshop topics include:
  • Shopping campaigns - how shopping integrates into the AdWords API
  • MCC scripts - The feature that was most requested is now available for beta signup
  • FeedServices updates - including the new location extensions setup
  • Targeting types - DSAs, DomainSetting and AutoTagging
  • Analytics - getting started with the Analytics API and how it fits within your AdWords apps
  • Bid Estimation - the different ways to estimate bids using the API

Not only are the workshops an important way to keep up-to-date with new features and best practices in the AdWords API, they are a great way for you to meet with and ask questions of the Google AdWords API team in person. This is also a key event for members of the community to bring their feedback directly to us. Finally, it’s a great opportunity for you to exchange ideas and best practices with fellow developers.

Register now and join us at a workshop in one of the following cities:
  • London, UK, April 29
  • Hamburg, Germany, May 13
  • Amsterdam, Netherlands, May 15
  • New York City, USA, May 5
  • San Francisco, USA, May 12
  • Shanghai, China, May 20 (in Chinese)
  • Taipei, Taiwan, May 22 (in Chinese)
  • Delhi, India, May 26
  • Sydney, Australia, May 29

Note: The workshops in the USA, Europe, and Australia are technical in nature and best suited to developers. Those in China, Taiwan, and India will have additional sessions accessible to a broader audience, including non-developers.

For more information on the agenda, location and logistics, please visit the AdWords API Workshop website.

Posted:
Starting with v201402 of the AdWords API, all newly created Search and Display Network campaigns will automatically be Search Network with Display Select campaigns. This means:
  • If you don't specify displaySelect when creating a Search and Display Network campaign, it will default to true, creating a Search Network with Display Select campaign
  • If you edit the networkSetting of an existing Search only campaign to target Content Network and do not specify displaySelect, we will upgrade the campaign to Search Network with Display Select by setting displaySelect to true
  • If you create a Search and Display Network campaign or edit the networkSetting of an existing Search only campaign to target Content Network, and specify displaySelect as false, you will receive an OPERATION_NOT_PERMITTED_FOR_CAMPAIGN_TYPE error
We encourage you to update your campaigns to Search Network with Display Select by setting the displaySelect property to true. Starting on September 16, 2014, we will start converting all Search and Display Network campaigns to Search Network with Display Select.

Search Network with Display Select uses improved signals and methods of predicting when and where your ads are likely to perform best, and sets a higher bar for when to show them. That means your ads are more likely to be shown to a smaller number of prospective customers, who are more likely to be interested in your offerings.

For more information about creating these types of campaigns, see our previous blog post. For more general information about this AdWords feature, please see here.

If you have any questions about this change, please contact us on the forum or via our Google+ page.

Posted:
Update: This change went into effect on May 7th, 2014. The AdWords API will return a DistinctError with reason DUPLICATE_ELEMENT if you attempt to ADD or UPDATE an active Feed with the same name as another active Feed. The uniqueness check is case-insensitive.


Starting on April 29, 2014, the FeedService will start validating that newly added Feeds have unique names within an AdWords account. Once this change goes into effect, you will get a FeedError when you ADD a new Feed with the same name as another active Feed.

Currently it’s possible to have multiple Feeds with the same name. Introducing unique names will improve clarity in the user interface, and it will also allow Feeds to be referenced unambiguously by name, in addition to their ID.

Please ensure that your application is both uniquely naming Feeds and able to handle a potential naming conflict before this change goes into effect.

Shortly after this change is rolled out, we will append a unique value to the names of any remaining duplicates. For example, the following pair of Feeds on the left would be renamed as follows:

Old Names
New Names
My Sitelinks Feed
My Sitelinks Feed-1
My Sitelinks Feed
My Sitelinks Feed-2