The AdWords API will sunset on April 27, 2022. Migrate to the Google Ads API to take advantage of the latest Google Ads features.

Extensions Samples

The code samples below provide examples of common extension functions using the AdWords API. Client Library.

Associate a Google My Business feed to that of a customer

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;
using Google.Api.Ads.Common.Lib;

using System;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example adds a feed that syncs feed items from a Google
    /// My Business (GMB) account and associates the feed with a customer.
    /// </summary>
    public class AddGoogleMyBusinessLocationExtensions : ExampleBase
    {
        /// <summary>
        /// The placeholder type for location extensions. See the Placeholder
        /// reference page for a list of all the placeholder types and fields.
        ///
        /// https://developers.google.com/adwords/api/docs/appendix/placeholders
        /// </summary>
        private const int PLACEHOLDER_LOCATION = 7;

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example adds a feed that syncs feed items from a Google My " +
                    "Business (GMB) account and associates the feed with a customer.";
            }
        }

        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            AddGoogleMyBusinessLocationExtensions codeExample =
                new AddGoogleMyBusinessLocationExtensions();
            Console.WriteLine(codeExample.Description);

            AdWordsUser user = new AdWordsUser();

            try
            {
                // The email address of either an owner or a manager of the GMB account.
                string gmbEmailAddress = "INSERT_GMB_EMAIL_ADDRESS_HERE";

                // Refresh the access token so that there's a valid access token.
                user.OAuthProvider.RefreshAccessToken();

                // If the gmbEmailAddress above is the same user you used to generate
                // your AdWords API refresh token, leave the assignment below unchanged.
                // Otherwise, to obtain an access token for your GMB account, run the
                // OAuth Token generator utility while logged in as the same user as
                // gmbEmailAddress. Copy and paste the AccessToken value into the
                // assignment below.
                string gmbAccessToken = user.OAuthProvider.Config.OAuth2AccessToken;

                // If the gmbEmailAddress above is for a GMB manager instead of the GMB
                // account owner, then set businessAccountIdentifier to the +Page ID of
                // a location for which the manager has access. See the location
                // extensions guide at
                // https://developers.google.com/adwords/api/docs/guides/feed-services-locations
                // for details.
                string businessAccountIdentifier = null;
                codeExample.Run(user, gmbEmailAddress, gmbAccessToken, businessAccountIdentifier);
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="gmbEmailAddress">The email address for Google My Business
        /// account.</param>
        /// <param name="gmbAccessToken">The OAuth2 access token for Google
        /// My Business account.</param>
        /// <param name="businessAccountIdentifier">The account identifier for
        /// Google My Business account.</param>
        public void Run(AdWordsUser user, string gmbEmailAddress, string gmbAccessToken,
            string businessAccountIdentifier)
        {
            Feed gmbFeed = CreateGmbFeed(user, gmbEmailAddress, gmbAccessToken,
                businessAccountIdentifier);
            AddCustomerFeed(user, gmbFeed);
        }

        /// <summary>
        /// Create a feed that will sync to the Google My Business account
        /// specified by gmbEmailAddress.
        /// </summary>
        /// <param name="user">The user.</param>
        /// <param name="gmbEmailAddress">The GMB email address.</param>
        /// <param name="gmbAccessToken">The GMB access token.</param>
        /// <param name="businessAccountIdentifier">The GMB account identifier.</param>
        /// <returns>The newly created GMB feed.</returns>
        private static Feed CreateGmbFeed(AdWordsUser user, string gmbEmailAddress,
            string gmbAccessToken, string businessAccountIdentifier)
        {
            if (string.IsNullOrEmpty(gmbAccessToken))
            {
                user.OAuthProvider.RefreshAccessToken();
                gmbAccessToken = user.OAuthProvider.Config.OAuth2AccessToken;
            }
            if (string.IsNullOrEmpty(businessAccountIdentifier))
            {
                businessAccountIdentifier = null;
            }

            using (FeedService feedService =
                (FeedService) user.GetService(AdWordsService.v201809.FeedService))
            {
                // Create a feed that will sync to the Google My Business account
                // specified by gmbEmailAddress. Do not add FeedAttributes to this object,
                // as AdWords will add them automatically because this will be a
                // system generated feed.
                Feed gmbFeed = new Feed
                {
                    name = string.Format("Google My Business feed #{0}",
                        ExampleUtilities.GetRandomString())
                };

                PlacesLocationFeedData feedData = new PlacesLocationFeedData
                {
                    emailAddress = gmbEmailAddress,
                    businessAccountIdentifier = businessAccountIdentifier,

                    // Optional: specify labels to filter Google My Business listings. If
                    // specified, only listings that have any of the labels set are
                    // synchronized into FeedItems.
                    labelFilters = new string[]
                    {
                        "Stores in New York City"
                    }
                };

                OAuthInfo oAuthInfo = new OAuthInfo
                {
                    httpMethod = "GET",

                    // Permissions for the AdWords API scope will also cover GMB.
                    httpRequestUrl = user.Config.GetDefaultOAuth2Scope(),
                    httpAuthorizationHeader = string.Format("Bearer {0}", gmbAccessToken)
                };
                feedData.oAuthInfo = oAuthInfo;

                gmbFeed.systemFeedGenerationData = feedData;

                // Since this feed's feed items will be managed by AdWords,
                // you must set its origin to ADWORDS.
                gmbFeed.origin = FeedOrigin.ADWORDS;

                // Create an operation to add the feed.
                FeedOperation feedOperation = new FeedOperation
                {
                    operand = gmbFeed,
                    @operator = Operator.ADD
                };

                try
                {
                    // Add the feed. Since it is a system generated feed, AdWords will
                    // automatically:
                    // 1. Set up the FeedAttributes on the feed.
                    // 2. Set up a FeedMapping that associates the FeedAttributes of the
                    //    feed with the placeholder fields of the LOCATION placeholder
                    //    type.
                    FeedReturnValue addFeedResult = feedService.mutate(new FeedOperation[]
                    {
                        feedOperation
                    });
                    Feed addedFeed = addFeedResult.value[0];
                    Console.WriteLine("Added GMB feed with ID {0}", addedFeed.id);
                    return addedFeed;
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException("Failed to create GMB feed.", e);
                }
            }
        }

        /// <summary>
        /// Add a CustomerFeed that associates the feed with this customer for
        /// the LOCATION placeholder type.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="feed">The GMB feed.</param>
        void AddCustomerFeed(AdWordsUser user, Feed feed)
        {
            using (CustomerFeedService customerFeedService =
                (CustomerFeedService) user.GetService(AdWordsService.v201809.CustomerFeedService))
            {
                // Add a CustomerFeed that associates the feed with this customer for
                // the LOCATION placeholder type.
                CustomerFeed customerFeed = new CustomerFeed
                {
                    feedId = feed.id,
                    placeholderTypes = new int[]
                    {
                        PLACEHOLDER_LOCATION
                    }
                };

                // Create a matching function that will always evaluate to true.
                Function customerMatchingFunction = new Function();
                ConstantOperand constOperand = new ConstantOperand
                {
                    type = ConstantOperandConstantType.BOOLEAN,
                    booleanValue = true
                };
                customerMatchingFunction.lhsOperand = new FunctionArgumentOperand[]
                {
                    constOperand
                };
                customerMatchingFunction.@operator = FunctionOperator.IDENTITY;
                customerFeed.matchingFunction = customerMatchingFunction;

                // Create an operation to add the customer feed.
                CustomerFeedOperation customerFeedOperation = new CustomerFeedOperation
                {
                    operand = customerFeed,
                    @operator = Operator.ADD
                };

                // After the completion of the Feed ADD operation above the added feed
                // will not be available for usage in a CustomerFeed until the sync
                // between the AdWords and GMB accounts completes.  The loop below
                // will retry adding the CustomerFeed up to ten times with an
                // exponential back-off policy.
                CustomerFeed addedCustomerFeed = null;

                AdWordsAppConfig config = new AdWordsAppConfig
                {
                    RetryCount = 10
                };

                ErrorHandler errorHandler = new ErrorHandler(config);
                try
                {
                    do
                    {
                        try
                        {
                            CustomerFeedReturnValue customerFeedResult = customerFeedService.mutate(
                                new CustomerFeedOperation[]
                                {
                                    customerFeedOperation
                                });
                            addedCustomerFeed = customerFeedResult.value[0];

                            Console.WriteLine(
                                "Added CustomerFeed for feed ID {0} and placeholder type {1}",
                                addedCustomerFeed.feedId, addedCustomerFeed.placeholderTypes[0]);
                            break;
                        }
                        catch (AdWordsApiException e)
                        {
                            ApiException apiException = (ApiException) e.ApiException;
                            foreach (ApiError error in apiException.errors)
                            {
                                if (error is CustomerFeedError)
                                {
                                    if ((error as CustomerFeedError).reason ==
                                        CustomerFeedErrorReason
                                            .MISSING_FEEDMAPPING_FOR_PLACEHOLDER_TYPE)
                                    {
                                        errorHandler.DoExponentialBackoff();
                                        errorHandler.IncrementRetriedAttempts();
                                    }
                                    else
                                    {
                                        throw;
                                    }
                                }
                            }
                        }
                    } while (errorHandler.HaveMoreRetryAttemptsLeft());
                    // OPTIONAL: Create a CampaignFeed to specify which FeedItems to use at
                    // the Campaign level.  This will be similar to the CampaignFeed in the
                    // AddSiteLinks example, except you can also filter based on the
                    // business name and category of each FeedItem by using a
                    // FeedAttributeOperand in your matching function.

                    // OPTIONAL: Create an AdGroupFeed for even more fine grained control
                    // over which feed items are used at the AdGroup level.
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException("Failed to create customer feed.", e);
                }
            }
        }
    }
}

Associate a price extension to an account

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;
using System.Collections.Generic;

using DayOfWeek = Google.Api.Ads.AdWords.v201809.DayOfWeek;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example adds a price extension and associates it with an
    /// account. Campaign targeting is also set using the specified campaign ID.
    /// To get campaigns, run AddCampaigns.cs.
    /// </summary>
    public class AddPrices : ExampleBase
    {
        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            AddPrices codeExample = new AddPrices();
            Console.WriteLine(codeExample.Description);
            try
            {
                long campaignId = long.Parse("INSERT_CAMPAIGN_ID_HERE");
                codeExample.Run(new AdWordsUser(), campaignId);
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example adds a price extension and associates it with an " +
                    "account. Campaign targeting is also set using the specified campaign ID. " +
                    "To get campaigns, run AddCampaigns.cs.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="campaignId">Id of the campaign to which sitelinks will
        /// be added.</param>
        public void Run(AdWordsUser user, long campaignId)
        {
            using (CustomerExtensionSettingService customerExtensionSettingService =
                (CustomerExtensionSettingService) user.GetService(AdWordsService.v201809
                    .CustomerExtensionSettingService))
            {
                // Create the price extension feed item.
                PriceFeedItem priceFeedItem = new PriceFeedItem()
                {
                    priceExtensionType = PriceExtensionType.SERVICES,

                    // Price qualifier is optional.
                    priceQualifier = PriceExtensionPriceQualifier.FROM,
                    trackingUrlTemplate = "http://tracker.example.com/?u={lpurl}",
                    language = "en",

                    campaignTargeting = new FeedItemCampaignTargeting()
                    {
                        TargetingCampaignId = campaignId,
                    },
                    scheduling = new FeedItemSchedule[]
                    {
                        new FeedItemSchedule()
                        {
                            dayOfWeek = DayOfWeek.SATURDAY,
                            startHour = 10,
                            startMinute = MinuteOfHour.ZERO,
                            endHour = 22,
                            endMinute = MinuteOfHour.ZERO
                        },
                        new FeedItemSchedule()
                        {
                            dayOfWeek = DayOfWeek.SUNDAY,
                            startHour = 10,
                            startMinute = MinuteOfHour.ZERO,
                            endHour = 18,
                            endMinute = MinuteOfHour.ZERO
                        }
                    }
                };

                // To create a price extension, at least three table rows are needed.
                List<PriceTableRow> priceTableRows = new List<PriceTableRow>();
                string currencyCode = "USD";
                priceTableRows.Add(CreatePriceTableRow("Scrubs", "Body Scrub, Salt Scrub",
                    "http://www.example.com/scrubs", "http://m.example.com/scrubs", 60000000,
                    currencyCode, PriceExtensionPriceUnit.PER_HOUR));
                priceTableRows.Add(CreatePriceTableRow("Hair Cuts", "Once a month",
                    "http://www.example.com/haircuts", "http://m.example.com/haircuts", 75000000,
                    currencyCode, PriceExtensionPriceUnit.PER_MONTH));
                priceTableRows.Add(CreatePriceTableRow("Skin Care Package", "Four times a month",
                    "http://www.example.com/skincarepackage", null, 250000000, currencyCode,
                    PriceExtensionPriceUnit.PER_MONTH));

                priceFeedItem.tableRows = priceTableRows.ToArray();

                // Create your campaign extension settings. This associates the sitelinks
                // to your campaign.
                CustomerExtensionSetting customerExtensionSetting = new CustomerExtensionSetting()
                {
                    extensionType = FeedType.PRICE,
                    extensionSetting = new ExtensionSetting()
                    {
                        extensions = new ExtensionFeedItem[]
                        {
                            priceFeedItem
                        }
                    }
                };

                CustomerExtensionSettingOperation operation =
                    new CustomerExtensionSettingOperation()
                    {
                        operand = customerExtensionSetting,
                        @operator = Operator.ADD
                    };

                try
                {
                    // Add the extensions.
                    CustomerExtensionSettingReturnValue retVal =
                        customerExtensionSettingService.mutate(
                            new CustomerExtensionSettingOperation[]
                            {
                                operation
                            });
                    if (retVal.value != null && retVal.value.Length > 0)
                    {
                        CustomerExtensionSetting newExtensionSetting = retVal.value[0];
                        Console.WriteLine("Extension setting with type '{0}' was added.",
                            newExtensionSetting.extensionType);
                    }
                    else
                    {
                        Console.WriteLine("No extension settings were created.");
                    }
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException("Failed to create extension settings.",
                        e);
                }
            }
        }

        /// <summary>
        /// Creates a price table row.
        /// </summary>
        /// <param name="header">The row header.</param>
        /// <param name="description">The description text.</param>
        /// <param name="finalUrl">The final URL.</param>
        /// <param name="finalMobileUrl">The mobile final URL, or null if this field
        /// should not be set.</param>
        /// <param name="priceInMicros">The price in micros.</param>
        /// <param name="currencyCode">The currency code.</param>
        /// <param name="priceUnit">The price unit.</param>
        /// <returns>A price table row for creating price extension.</returns>
        private static PriceTableRow CreatePriceTableRow(string header, string description,
            string finalUrl, string finalMobileUrl, long priceInMicros, string currencyCode,
            PriceExtensionPriceUnit priceUnit)
        {
            PriceTableRow retval = new PriceTableRow()
            {
                header = header,
                description = description,
                finalUrls = new UrlList()
                {
                    urls = new string[]
                    {
                        finalUrl
                    }
                },
                price = new MoneyWithCurrency()
                {
                    currencyCode = currencyCode,
                    money = new Money()
                    {
                        microAmount = priceInMicros
                    }
                },
                priceUnit = priceUnit
            };

            // Optional: set the mobile final URLs.
            if (!string.IsNullOrEmpty(finalMobileUrl))
            {
                retval.finalMobileUrls = new UrlList()
                {
                    urls = new string[]
                    {
                        finalMobileUrl
                    }
                };
            }

            return retval;
        }

    }
}

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;
using System.Collections.Generic;

using DayOfWeek = Google.Api.Ads.AdWords.v201809.DayOfWeek;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example adds sitelinks to a campaign. To create a campaign,
    /// run AddCampaign.cs.
    /// </summary>
    public class AddSitelinks : ExampleBase
    {
        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            AddSitelinks codeExample = new AddSitelinks();
            Console.WriteLine(codeExample.Description);
            try
            {
                long campaignId = long.Parse("INSERT_CAMPAIGN_ID_HERE");
                codeExample.Run(new AdWordsUser(), campaignId);
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return
                    "This code example adds sitelinks to a campaign. To create a campaign, run " +
                    "AddCampaign.cs.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="campaignId">Id of the campaign to which sitelinks will
        /// be added.</param>
        public void Run(AdWordsUser user, long campaignId)
        {
            using (CampaignExtensionSettingService campaignExtensionSettingService =
                (CampaignExtensionSettingService) user.GetService(AdWordsService.v201809
                    .CampaignExtensionSettingService))
            {
                Customer customer = null;
                using (CustomerService customerService =
                    (CustomerService) user.GetService(AdWordsService.v201809.CustomerService))
                {
                    // Find the matching customer and its time zone. The getCustomers method
                    // will return a single Customer object corresponding to the session's
                    // clientCustomerId.
                    customer = customerService.getCustomers()[0];
                    Console.WriteLine("Found customer ID {0:###-###-####} with time zone '{1}'.",
                        customer.customerId, customer.dateTimeZone);
                }

                List<ExtensionFeedItem> extensions = new List<ExtensionFeedItem>();

                // Create your sitelinks.
                SitelinkFeedItem sitelink1 = new SitelinkFeedItem()
                {
                    sitelinkText = "Store Hours",
                    sitelinkFinalUrls = new UrlList()
                    {
                        urls = new string[]
                        {
                            "http://www.example.com/storehours"
                        }
                    }
                };
                extensions.Add(sitelink1);

                DateTime startOfThanksGiving = new DateTime(DateTime.Now.Year, 11, 20, 0, 0, 0);
                DateTime endOfThanksGiving = new DateTime(DateTime.Now.Year, 11, 27, 23, 59, 59);

                // Add check to make sure we don't create a sitelink with end date in the
                // past.
                if (DateTime.Now < endOfThanksGiving)
                {
                    // Show the Thanksgiving specials link only from 20 - 27 Nov.
                    SitelinkFeedItem sitelink2 = new SitelinkFeedItem()
                    {
                        sitelinkText = "Thanksgiving Specials",
                        sitelinkFinalUrls = new UrlList()
                        {
                            urls = new string[]
                            {
                                "http://www.example.com/thanksgiving"
                            }
                        },
                        startTime = string.Format("{0} {1}",
                            startOfThanksGiving.ToString("yyyyMMdd HHmmss"), customer.dateTimeZone),
                        endTime = string.Format("{0} {1}",
                            endOfThanksGiving.ToString("yyyyMMdd HHmmss"), customer.dateTimeZone),

                        // Target this sitelink for United States only. See
                        // https://developers.google.com/adwords/api/docs/appendix/geotargeting
                        // for valid geolocation codes.
                        geoTargeting = new Location()
                        {
                            id = 2840
                        },

                        // Restrict targeting only to people physically within the United States.
                        // Otherwise, this could also show to people interested in the United States
                        // but not physically located there.
                        geoTargetingRestriction = new FeedItemGeoRestriction()
                        {
                            geoRestriction = GeoRestriction.LOCATION_OF_PRESENCE
                        }
                    };
                    extensions.Add(sitelink2);
                }

                // Show the wifi details primarily for high end mobile users.
                SitelinkFeedItem sitelink3 = new SitelinkFeedItem()
                {
                    sitelinkText = "Wifi available",
                    sitelinkFinalUrls = new UrlList()
                    {
                        urls = new string[]
                        {
                            "http://www.example.com/mobile/wifi"
                        }
                    },
                    devicePreference = new FeedItemDevicePreference()
                    {
                        // See https://developers.google.com/adwords/api/docs/appendix/platforms
                        // for device criteria IDs.
                        devicePreference = 30001
                    },

                    // Target this sitelink for the keyword "free wifi".
                    keywordTargeting = new Keyword()
                    {
                        text = "free wifi",
                        matchType = KeywordMatchType.BROAD
                    }
                };
                extensions.Add(sitelink3);

                // Show the happy hours link only during Mon - Fri 6PM to 9PM.
                SitelinkFeedItem sitelink4 = new SitelinkFeedItem()
                {
                    sitelinkText = "Happy hours",
                    sitelinkFinalUrls = new UrlList()
                    {
                        urls = new string[]
                        {
                            "http://www.example.com/happyhours"
                        },
                    },
                    scheduling = new FeedItemSchedule[]
                    {
                        new FeedItemSchedule()
                        {
                            dayOfWeek = DayOfWeek.MONDAY,
                            startHour = 18,
                            startMinute = MinuteOfHour.ZERO,
                            endHour = 21,
                            endMinute = MinuteOfHour.ZERO
                        },
                        new FeedItemSchedule()
                        {
                            dayOfWeek = DayOfWeek.TUESDAY,
                            startHour = 18,
                            startMinute = MinuteOfHour.ZERO,
                            endHour = 21,
                            endMinute = MinuteOfHour.ZERO
                        },
                        new FeedItemSchedule()
                        {
                            dayOfWeek = DayOfWeek.WEDNESDAY,
                            startHour = 18,
                            startMinute = MinuteOfHour.ZERO,
                            endHour = 21,
                            endMinute = MinuteOfHour.ZERO
                        },
                        new FeedItemSchedule()
                        {
                            dayOfWeek = DayOfWeek.THURSDAY,
                            startHour = 18,
                            startMinute = MinuteOfHour.ZERO,
                            endHour = 21,
                            endMinute = MinuteOfHour.ZERO
                        },
                        new FeedItemSchedule()
                        {
                            dayOfWeek = DayOfWeek.FRIDAY,
                            startHour = 18,
                            startMinute = MinuteOfHour.ZERO,
                            endHour = 21,
                            endMinute = MinuteOfHour.ZERO
                        }
                    }
                };
                extensions.Add(sitelink4);

                // Create your campaign extension settings. This associates the sitelinks
                // to your campaign.
                CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting
                {
                    campaignId = campaignId,
                    extensionType = FeedType.SITELINK,
                    extensionSetting = new ExtensionSetting()
                    {
                        extensions = extensions.ToArray()
                    }
                };

                CampaignExtensionSettingOperation operation =
                    new CampaignExtensionSettingOperation()
                    {
                        operand = campaignExtensionSetting,
                        @operator = Operator.ADD
                    };

                try
                {
                    // Add the extensions.
                    CampaignExtensionSettingReturnValue retVal =
                        campaignExtensionSettingService.mutate(
                            new CampaignExtensionSettingOperation[]
                            {
                                operation
                            });

                    // Display the results.
                    if (retVal.value != null && retVal.value.Length > 0)
                    {
                        CampaignExtensionSetting newExtensionSetting = retVal.value[0];
                        Console.WriteLine(
                            "Extension setting with type = {0} was added to campaign ID {1}.",
                            newExtensionSetting.extensionType, newExtensionSetting.campaignId);
                    }
                    else
                    {
                        Console.WriteLine("No extension settings were created.");
                    }
                }
                catch (Exception e)
                {
                    throw new System.ApplicationException("Failed to create extension settings.",
                        e);
                }
            }

        }
    }
}

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Ads.AdWords.Lib;
using Google.Api.Ads.AdWords.v201809;

using System;
using System.Collections.Generic;

namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809
{
    /// <summary>
    /// This code example adds sitelinks to a campaign using feed services.
    /// To create a campaign, run AddCampaign.cs. To add sitelinks using the
    /// simpler ExtensionSetting services, see AddSitelinks.cs.
    /// </summary>
    public class AddSitelinksUsingFeeds : ExampleBase
    {
        /// <summary>
        /// Holds data about sitelinks in a feed.
        /// </summary>
        private class SitelinksDataHolder
        {
            /// <summary>
            /// The sitelink feed item IDs.
            /// </summary>
            private List<long> feedItemIds = new List<long>();

            /// <summary>
            /// Gets the sitelink feed item IDs.
            /// </summary>
            public List<long> FeedItemIds
            {
                get { return feedItemIds; }
            }

            /// <summary>
            /// Gets or sets the feed ID.
            /// </summary>
            public long FeedId { get; set; }

            /// <summary>
            /// Gets or sets the link text feed attribute ID.
            /// </summary>
            public long LinkTextFeedAttributeId { get; set; }

            /// <summary>
            /// Gets or sets the link URL feed attribute ID.
            /// </summary>
            public long LinkFinalUrlFeedAttributeId { get; set; }

            /// <summary>
            /// Gets or sets the line 2 feed attribute ID.
            /// </summary>
            public long Line2FeedAttributeId { get; set; }

            /// <summary>
            /// Gets or sets the line 3 feed attribute ID.
            /// </summary>
            public long Line3FeedAttributeId { get; set; }
        }

        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            AddSitelinksUsingFeeds codeExample = new AddSitelinksUsingFeeds();
            Console.WriteLine(codeExample.Description);
            try
            {
                long campaignId = long.Parse("INSERT_CAMPAIGN_ID_HERE");
                long adGroupId = long.Parse("INSERT_ADGROUP_ID_HERE");
                string feedName = "INSERT_FEED_NAME_HERE";
                codeExample.Run(new AdWordsUser(), campaignId, feedName, adGroupId);
            }
            catch (Exception e)
            {
                Console.WriteLine("An exception occurred while running this code example. {0}",
                    ExampleUtilities.FormatException(e));
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This code example adds sitelinks to a campaign using feed services." +
                    "To create a campaign, run AddCampaign.cs. To add sitelinks using the " +
                    "simpler ExtensionSetting services, see AddSitelinks.cs.";
            }
        }

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="campaignId">Id of the campaign with which sitelinks are associated.
        /// </param>
        /// <param name="adGroupId">Id of the adgroup to restrict targeting to.</param>
        /// <param name="feedName">Name of the feed to be created.</param>
        public void Run(AdWordsUser user, long campaignId, string feedName, long? adGroupId)
        {
            SitelinksDataHolder sitelinksData = new SitelinksDataHolder();
            CreateSitelinksFeed(user, sitelinksData, feedName);
            CreateSitelinksFeedItems(user, sitelinksData);
            createSitelinksFeedMapping(user, sitelinksData);
            CreateSitelinksCampaignFeed(user, sitelinksData, campaignId);
            RestrictFeedItemToAdGroup(user, sitelinksData, adGroupId);
        }

        private static void RestrictFeedItemToGeoTarget(AdWordsUser user, FeedItem feedItem,
            long locationId)
        {
            FeedItemCriterionTarget criterionTarget = new FeedItemCriterionTarget()
            {
                feedId = feedItem.feedId,
                feedItemId = feedItem.feedItemId,
                // The IDs can be found in the documentation or retrieved with the
                // LocationCriterionService.
                criterion = new Location()
                {
                    id = locationId,
                }
            };

            using (FeedItemTargetService feedItemTargetService =
                (FeedItemTargetService) user.GetService(
                    AdWordsService.v201809.FeedItemTargetService))
            {
                FeedItemTargetOperation operation = new FeedItemTargetOperation()
                {
                    @operator = Operator.ADD,
                    operand = criterionTarget
                };


                FeedItemTargetReturnValue retval = feedItemTargetService.mutate(
                    new FeedItemTargetOperation[]
                    {
                        operation
                    });
                FeedItemCriterionTarget newLocationTarget =
                    (FeedItemCriterionTarget) retval.value[0];
                Console.WriteLine(
                    "Feed item target for feed ID {0} and feed item ID {1}" +
                    " was created to restrict serving to location ID {2}", newLocationTarget.feedId,
                    newLocationTarget.feedItemId, newLocationTarget.criterion.id);
            }
        }

        private static void RestrictFeedItemToAdGroup(AdWordsUser user,
            SitelinksDataHolder sitelinksData, long? adGroupId)
        {
            // Optional: Restrict the first feed item to only serve with ads for the
            // specified ad group ID.
            FeedItemAdGroupTarget adGroupTarget = new FeedItemAdGroupTarget()
            {
                feedId = sitelinksData.FeedId,
                feedItemId = sitelinksData.FeedItemIds[0],
                adGroupId = adGroupId.Value
            };

            using (FeedItemTargetService feedItemTargetService =
                (FeedItemTargetService) user.GetService(
                    AdWordsService.v201809.FeedItemTargetService))
            {
                FeedItemTargetOperation operation = new FeedItemTargetOperation()
                {
                    @operator = Operator.ADD,
                    operand = adGroupTarget
                };

                FeedItemTargetReturnValue retval = feedItemTargetService.mutate(
                    new FeedItemTargetOperation[]
                    {
                        operation
                    });
                FeedItemAdGroupTarget newAdGroupTarget = (FeedItemAdGroupTarget) retval.value[0];
                Console.WriteLine(
                    "Feed item target for feed ID {0} and feed item ID {1}" +
                    " was created to restrict serving to ad group ID {2}", newAdGroupTarget.feedId,
                    newAdGroupTarget.feedItemId, newAdGroupTarget.adGroupId);
            }
        }

        private static void CreateSitelinksFeed(AdWordsUser user, SitelinksDataHolder sitelinksData,
            string feedName)
        {
            using (FeedService feedService =
                (FeedService) user.GetService(AdWordsService.v201809.FeedService))
            {
                // Create attributes.
                FeedAttribute textAttribute = new FeedAttribute()
                {
                    type = FeedAttributeType.STRING,
                    name = "Link Text"
                };

                FeedAttribute finalUrlAttribute = new FeedAttribute()
                {
                    type = FeedAttributeType.URL_LIST,
                    name = "Link Final URLs"
                };

                FeedAttribute line2Attribute = new FeedAttribute()
                {
                    type = FeedAttributeType.STRING,
                    name = "Line 2"
                };

                FeedAttribute line3Attribute = new FeedAttribute()
                {
                    type = FeedAttributeType.STRING,
                    name = "Line 3"
                };

                // Create the feed.
                Feed sitelinksFeed = new Feed()
                {
                    name = feedName,
                    attributes = new FeedAttribute[]
                    {
                        textAttribute,
                        finalUrlAttribute,
                        line2Attribute,
                        line3Attribute
                    },
                    origin = FeedOrigin.USER
                };

                // Create operation.
                FeedOperation operation = new FeedOperation()
                {
                    operand = sitelinksFeed,
                    @operator = Operator.ADD
                };

                // Add the feed.
                FeedReturnValue result = feedService.mutate(new FeedOperation[]
                {
                    operation
                });

                Feed savedFeed = result.value[0];
                sitelinksData.FeedId = savedFeed.id;

                FeedAttribute[] savedAttributes = savedFeed.attributes;
                sitelinksData.LinkTextFeedAttributeId = savedAttributes[0].id;
                sitelinksData.LinkFinalUrlFeedAttributeId = savedAttributes[1].id;
                sitelinksData.Line2FeedAttributeId = savedAttributes[2].id;
                sitelinksData.Line3FeedAttributeId = savedAttributes[3].id;

                Console.WriteLine(
                    "Feed with name {0} and ID {1} with linkTextAttributeId {2}, " +
                    "linkFinalUrlAttributeId {3}, line2AttributeId {4} and line3AttributeId {5} " +
                    "was created.", savedFeed.name, savedFeed.id, savedAttributes[0].id,
                    savedAttributes[1].id, savedAttributes[2].id, savedAttributes[3].id);
            }
        }

        private static void CreateSitelinksFeedItems(AdWordsUser user,
            SitelinksDataHolder siteLinksData)
        {
            using (FeedItemService feedItemService =
                (FeedItemService) user.GetService(AdWordsService.v201809.FeedItemService))
            {
                // Create operations to add FeedItems.
                FeedItemOperation home = NewSitelinkFeedItemAddOperation(siteLinksData, "Home",
                    "http://www.example.com", "Home line 2", "Home line 3");
                FeedItemOperation stores = NewSitelinkFeedItemAddOperation(siteLinksData, "Stores",
                    "http://www.example.com/stores", "Stores line 2", "Stores line 3");
                FeedItemOperation onSale = NewSitelinkFeedItemAddOperation(siteLinksData, "On Sale",
                    "http://www.example.com/sale", "On Sale line 2", "On Sale line 3");
                FeedItemOperation support = NewSitelinkFeedItemAddOperation(siteLinksData,
                    "Support", "http://www.example.com/support", "Support line 2",
                    "Support line 3");
                FeedItemOperation products = NewSitelinkFeedItemAddOperation(siteLinksData,
                    "Products", "http://www.example.com/prods", "Products line 2",
                    "Products line 3");

                // This site link is using geographical targeting to use LOCATION_OF_PRESENCE.
                FeedItemOperation aboutUs = NewSitelinkFeedItemAddOperation(siteLinksData,
                    "About Us", "http://www.example.com/about", "About Us line 2",
                    "About Us line 3", true);

                FeedItemOperation[] operations = new FeedItemOperation[]
                {
                    home,
                    stores,
                    onSale,
                    support,
                    products,
                    aboutUs
                };

                FeedItemReturnValue result = feedItemService.mutate(operations);
                foreach (FeedItem item in result.value)
                {
                    Console.WriteLine("FeedItem with feedItemId {0} was added.", item.feedItemId);
                    siteLinksData.FeedItemIds.Add(item.feedItemId);
                }

                // Target the "aboutUs" sitelink to geographically target California.
                // See https://developers.google.com/adwords/api/docs/appendix/geotargeting for
                // location criteria for supported locations.
                RestrictFeedItemToGeoTarget(user, result.value[5], 21137);
            }
        }

        // See the Placeholder reference page for a list of all the placeholder types and fields.
        // https://developers.google.com/adwords/api/docs/appendix/placeholders.html
        private const int PLACEHOLDER_SITELINKS = 1;

        // See the Placeholder reference page for a list of all the placeholder types and fields.
        private const int PLACEHOLDER_FIELD_SITELINK_LINK_TEXT = 1;

        private const int PLACEHOLDER_FIELD_SITELINK_FINAL_URL = 5;
        private const int PLACEHOLDER_FIELD_LINE_2_TEXT = 3;
        private const int PLACEHOLDER_FIELD_LINE_3_TEXT = 4;

        private static void createSitelinksFeedMapping(AdWordsUser user,
            SitelinksDataHolder sitelinksData)
        {
            using (FeedMappingService feedMappingService =
                (FeedMappingService) user.GetService(AdWordsService.v201809.FeedMappingService))
            {
                // Map the FeedAttributeIds to the fieldId constants.
                AttributeFieldMapping linkTextFieldMapping = new AttributeFieldMapping()
                {
                    feedAttributeId = sitelinksData.LinkTextFeedAttributeId,
                    fieldId = PLACEHOLDER_FIELD_SITELINK_LINK_TEXT
                };

                AttributeFieldMapping linkFinalUrlFieldMapping = new AttributeFieldMapping()
                {
                    feedAttributeId = sitelinksData.LinkFinalUrlFeedAttributeId,
                    fieldId = PLACEHOLDER_FIELD_SITELINK_FINAL_URL
                };

                AttributeFieldMapping line2FieldMapping = new AttributeFieldMapping()
                {
                    feedAttributeId = sitelinksData.Line2FeedAttributeId,
                    fieldId = PLACEHOLDER_FIELD_LINE_2_TEXT
                };

                AttributeFieldMapping line3FieldMapping = new AttributeFieldMapping()
                {
                    feedAttributeId = sitelinksData.Line3FeedAttributeId,
                    fieldId = PLACEHOLDER_FIELD_LINE_3_TEXT
                };

                // Create the FieldMapping and operation.
                FeedMappingOperation operation = new FeedMappingOperation()
                {
                    operand = new FeedMapping()
                    {
                        placeholderType = PLACEHOLDER_SITELINKS,
                        feedId = sitelinksData.FeedId,
                        attributeFieldMappings = new AttributeFieldMapping[]
                        {
                            linkTextFieldMapping,
                            linkFinalUrlFieldMapping,
                            line2FieldMapping,
                            line3FieldMapping
                        }
                    },
                    @operator = Operator.ADD
                };

                // Save the field mapping.
                FeedMappingReturnValue result = feedMappingService.mutate(new FeedMappingOperation[]
                {
                    operation
                });

                foreach (FeedMapping savedFeedMapping in result.value)
                {
                    Console.WriteLine(
                        "Feed mapping with ID {0} and placeholderType {1} was saved for feed " +
                        "with ID {2}.",
                        savedFeedMapping.feedMappingId, savedFeedMapping.placeholderType,
                        savedFeedMapping.feedId);
                }
            }
        }

        private static void CreateSitelinksCampaignFeed(AdWordsUser user,
            SitelinksDataHolder sitelinksData, long campaignId)
        {
            using (CampaignFeedService campaignFeedService =
                (CampaignFeedService) user.GetService(AdWordsService.v201809.CampaignFeedService))
            {
                // Construct a matching function that associates the sitelink feeditems
                // to the campaign, and set the device preference to Mobile. See the
                // matching function guide at
                // https://developers.google.com/adwords/api/docs/guides/feed-matching-functions
                // for more details.
                string matchingFunctionString = string.Format(@"
          AND(
            IN(FEED_ITEM_ID, {{{0}}}),
            EQUALS(CONTEXT.DEVICE, 'Mobile')
          )", string.Join(",", sitelinksData.FeedItemIds));

                CampaignFeed campaignFeed = new CampaignFeed()
                {
                    feedId = sitelinksData.FeedId,
                    campaignId = campaignId,
                    matchingFunction = new Function()
                    {
                        functionString = matchingFunctionString
                    },
                    // Specifying placeholder types on the CampaignFeed allows the same feed
                    // to be used for different placeholders in different Campaigns.
                    placeholderTypes = new int[]
                    {
                        PLACEHOLDER_SITELINKS
                    }
                };

                CampaignFeedOperation operation = new CampaignFeedOperation()
                {
                    operand = campaignFeed,
                    @operator = Operator.ADD
                };

                CampaignFeedReturnValue result = campaignFeedService.mutate(
                    new CampaignFeedOperation[]
                    {
                        operation
                    });

                foreach (CampaignFeed savedCampaignFeed in result.value)
                {
                    Console.WriteLine("Campaign with ID {0} was associated with feed with ID {1}",
                        savedCampaignFeed.campaignId, savedCampaignFeed.feedId);
                }
            }
        }

        private static FeedItemOperation NewSitelinkFeedItemAddOperation(
            SitelinksDataHolder sitelinksData, string text, string finalUrl, string line2,
            string line3)
        {
            return NewSitelinkFeedItemAddOperation(sitelinksData, text, finalUrl, line2, line3,
                false);
        }

        private static FeedItemOperation NewSitelinkFeedItemAddOperation(
            SitelinksDataHolder sitelinksData, string text, string finalUrl, string line2,
            string line3, bool restrictToLop)
        {
            // Create the FeedItemAttributeValues for our text values.
            FeedItemAttributeValue linkTextAttributeValue = new FeedItemAttributeValue()
            {
                feedAttributeId = sitelinksData.LinkTextFeedAttributeId,
                stringValue = text
            };

            FeedItemAttributeValue linkFinalUrlAttributeValue = new FeedItemAttributeValue()
            {
                feedAttributeId = sitelinksData.LinkFinalUrlFeedAttributeId,
                stringValues = new string[]
                {
                    finalUrl
                }
            };

            FeedItemAttributeValue line2AttributeValue = new FeedItemAttributeValue()
            {
                feedAttributeId = sitelinksData.Line2FeedAttributeId,
                stringValue = line2
            };

            FeedItemAttributeValue line3AttributeValue = new FeedItemAttributeValue()
            {
                feedAttributeId = sitelinksData.Line3FeedAttributeId,
                stringValue = line3
            };

            // Create the feed item and operation.
            FeedItem item = new FeedItem()
            {
                feedId = sitelinksData.FeedId,
                attributeValues = new FeedItemAttributeValue[]
                {
                    linkTextAttributeValue,
                    linkFinalUrlAttributeValue,
                    line2AttributeValue,
                    line3AttributeValue
                }
            };

            // OPTIONAL: Restrict targeting only to people physically within the location.
            if (restrictToLop)
            {
                item.geoTargetingRestriction = new FeedItemGeoRestriction()
                {
                    geoRestriction = GeoRestriction.LOCATION_OF_PRESENCE
                };
            }

            return new FeedItemOperation()
            {
                operand = item,
                @operator = Operator.ADD
            };
        }
    }
}