Ad Extensions Code Example

This example demonstrates how to add, get, and delete extensions for an account's ad extension library, set, get, and delete the extension associations with a campaign, and determine why an extension failed editorial reviews.

Tip

Use the language selector in the documentation header to choose C#, Java, Php, or Python.

To get access and refresh tokens for your Microsoft Advertising user and make your first service call using the Bing Ads API, see the Quick Start guide. You'll want to review the Get Started guide and walkthroughs for your preferred language e.g., C#, Java, Php, and Python.

Supporting files for C#, Java, Php, and Python examples are available at GitHub. You can clone each repository or repurpose snippets as needed.

using System;
using System.Drawing;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Threading.Tasks;
using Microsoft.BingAds.V13.CampaignManagement;
using Microsoft.BingAds;

namespace BingAdsExamplesLibrary.V13
{
    /// <summary>
    /// How to manage ad extensions for an account's ad extension library, 
    /// associate the extensions with a campaign, and determine why an extension failed 
    /// editorial review.
    /// </summary>
    public class AdExtensions : ExampleBase
    {
        // To run this example you'll need to provide your own image.  
        // For required aspect ratios and recommended dimensions please see 
        // Image remarks at https://go.microsoft.com/fwlink/?linkid=872754.

        private const string MediaFilePath = "c:\\dev\\media\\";
        private const string ImageAdExtensionMediaFileName = "imageadextension300x200.png";

        public override string Description
        {
            get { return "Ad Extensions | Campaign Management V13"; }
        }

        public async override Task RunAsync(AuthorizationData authorizationData)
        {
            try
            {
                ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment;

                CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper(
                    OutputStatusMessageDefault: this.OutputStatusMessage);
                CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient<ICampaignManagementService>(
                    authorizationData: authorizationData,
                    environment: environment);

                // Add a campaign to associate with ad extensions. 

                var campaigns = new[] {
                    new Campaign
                    {
                        BudgetType = BudgetLimitType.DailyBudgetStandard,
                        DailyBudget = 50,
                        Languages = new string[] { "All" },
                        Name = "Everyone's Shoes " + DateTime.UtcNow,
                        TimeZone = "PacificTimeUSCanadaTijuana",
                    }
                };

                OutputStatusMessage("-----\nAddCampaigns:");
                AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync(
                    accountId: authorizationData.AccountId,
                    campaigns: campaigns);
                long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray();
                BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray();
                OutputStatusMessage("CampaignIds:");
                CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds);
                OutputStatusMessage("PartialErrors:");
                CampaignManagementExampleHelper.OutputArrayOfBatchError(campaignErrors);
                
                // Create media for the image ad extension that we'll add later. 

                var imageAdExtensionMedia = GetImageMedia(
                    "Image15x10",
                    MediaFilePath + ImageAdExtensionMediaFileName,
                    System.Drawing.Imaging.ImageFormat.Png);

                var media = new Media[]
                {
                    imageAdExtensionMedia
                };

                // Add the media to the account's library.

                OutputStatusMessage("-----\nAddMedia:");
                AddMediaResponse addMediaResponse = await CampaignManagementExampleHelper.AddMediaAsync(
                    accountId: authorizationData.AccountId,
                    media: media);
                long[] mediaIds = addMediaResponse.MediaIds.ToArray();
                OutputStatusMessage("MediaIds:");
                CampaignManagementExampleHelper.OutputArrayOfLong(mediaIds);

                // Add the extensions to the account's library.

                var adExtensions = new AdExtension[] {
                    new ActionAdExtension
                    {
                        ActionType = ActionAdExtensionActionType.ActNow,
                        FinalUrls = new string[]
                        {
                            "https://www.contoso.com/womenshoesale"
                        },
                        Language = "English",
                        Status = AdExtensionStatus.Active,
                    },
                    //new AppAdExtension
                    //{
                    //    AppPlatform = "Windows",
                    //    AppStoreId = "AppStoreIdGoesHere",
                    //    DestinationUrl = "DestinationUrlGoesHere",
                    //    DisplayText = "Contoso",
                    //},
                    new CallAdExtension {
                        CountryCode = "US",
                        PhoneNumber = "2065550100",
                        IsCallOnly = false,
                        // Include the call extension Monday - Friday from 9am - 9pm
                        // in the account's time zone.
                        Scheduling = new Schedule {
                            UseSearcherTimeZone = false,
                            DayTimeRanges = new[]
                            {
                                new DayTime
                                {
                                    Day = Day.Monday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Tuesday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Wednesday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Thursday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Friday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                            },
                            StartDate = null,
                            EndDate = new Microsoft.BingAds.V13.CampaignManagement.Date {
                                Month = 12,
                                Day = 31,
                                Year = DateTime.UtcNow.Year + 1
                            },
                        }
                    },
                    new CalloutAdExtension
                    {
                        Text = "Callout Text"
                    },
                    new ImageAdExtension
                    {
                        AlternativeText = "Image Extension Alt Text",
                        ImageMediaIds = mediaIds,
                        Images = new AssetLink[]
                        {
                            new AssetLink
                            {
                                Asset = new ImageAsset
                                {
                                    Id = 12345, // 1.91 aspect ratio image id. This type of image is required for Images
                                    SubType = "LandscapeImageMedia",
                                }
                            }
                        }
                    },
                    new LocationAdExtension {
                        PhoneNumber = "206-555-0100",
                        CompanyName = "Contoso Shoes",
                        Address = new Microsoft.BingAds.V13.CampaignManagement.Address {
                            StreetAddress = "1234 Washington Place",
                            StreetAddress2 = "Suite 1210",
                            CityName = "Woodinville",
                            ProvinceName = "WA",
                            CountryCode = "US",
                            PostalCode = "98608"
                        },
                        // Include the location extension every Saturday morning
                        // in the search user's time zone.
                        Scheduling = new Schedule {
                            UseSearcherTimeZone = true,
                            DayTimeRanges = new[]
                            {
                                new DayTime
                                {
                                    Day = Day.Saturday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 12,
                                    EndMinute = Minute.Zero,
                                },
                            },
                            StartDate = null,
                            EndDate = new Microsoft.BingAds.V13.CampaignManagement.Date {
                                Month = 12,
                                Day = 31,
                                Year = DateTime.UtcNow.Year + 1
                            },
                        }
                    },
                    new PriceAdExtension
                    {
                        Language = "English",
                        PriceExtensionType = PriceExtensionType.Events,
                        TableRows = new PriceTableRow[]
                        {
                            new PriceTableRow
                            {
                                CurrencyCode = "USD",
                                Description = "Come to the event",
                                FinalUrls = new string[]
                                {
                                    "https://contoso.com"
                                },
                                Header = "New Event",
                                Price = 9.99,
                                PriceQualifier = PriceQualifier.From,
                                PriceUnit = PriceUnit.PerDay,
                            },
                            new PriceTableRow
                            {
                                CurrencyCode = "USD",
                                Description = "Come to the next event",
                                FinalUrls = new string[]
                                {
                                    "https://contoso.com"
                                },
                                Header = "Next Event",
                                Price = 9.99,
                                PriceQualifier = PriceQualifier.From,
                                PriceUnit = PriceUnit.PerDay,
                            },
                            new PriceTableRow
                            {
                                CurrencyCode = "USD",
                                Description = "Come to the final event",
                                FinalUrls = new string[]
                                {
                                    "https://contoso.com"
                                },
                                Header = "Final Event",
                                Price = 9.99,
                                PriceQualifier = PriceQualifier.From,
                                PriceUnit = PriceUnit.PerDay,
                            },
                        },
                    },
                    new ReviewAdExtension
                    {
                        IsExact = true,
                        Source = "Review Source Name",
                        Text = "Review Text",
                        // The Url of the third-party review. This is not your business Url.
                        Url = "https://review.contoso.com" 
                    },
                    new SitelinkAdExtension {
                        Description1 = "Simple & Transparent.",
                        Description2 = "No Upfront Cost.",
                        DisplayText = "Everyone's Shoe Sale",
                        FinalUrls = new[] {
                            "https://www.contoso.com/womenshoesale"
                        },
                    },
                    new StructuredSnippetAdExtension
                    {
                        Header = "Brands",
                        Values = new [] { "Windows", "Xbox", "Skype"}
                    }
                };

                OutputStatusMessage("-----\nAddAdExtensions:");
                var addAdExtensionsResponse = await CampaignManagementExampleHelper.AddAdExtensionsAsync(
                    accountId: authorizationData.AccountId,
                    adExtensions: adExtensions);
                OutputStatusMessage("AdExtensionIdentities:");
                var adExtensionIdentities = addAdExtensionsResponse?.AdExtensionIdentities;
                OutputStatusMessage("NestedPartialErrors:");
                CampaignManagementExampleHelper.OutputArrayOfBatchErrorCollection(addAdExtensionsResponse?.NestedPartialErrors);
                
                // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
                // operations each require a list of type AdExtensionIdToEntityIdAssociation.
                var adExtensionIdToEntityIdAssociations = new List<AdExtensionIdToEntityIdAssociation>();

                // GetAdExtensionsByIds requires a list of type long.
                var adExtensionIds = new List<long>();

                // Loop through the list of extension IDs and build any required data structures
                // for subsequent operations. 

                foreach (var adExtensionIdentity in adExtensionIdentities)
                {
                    if (adExtensionIdentity != null)
                    {
                        adExtensionIdToEntityIdAssociations.Add(new AdExtensionIdToEntityIdAssociation
                        {
                            AdExtensionId = adExtensionIdentity.Id,
                            EntityId = (long)campaignIds[0]
                        });

                        adExtensionIds.Add(adExtensionIdentity.Id);
                    }
                }

                // Associate the ad extensions with the campaign. 

                OutputStatusMessage("-----\nSetAdExtensionsAssociations:");
                var setAdExtensionsAssociationsResponse = await CampaignManagementExampleHelper.SetAdExtensionsAssociationsAsync(
                    accountId: authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations: adExtensionIdToEntityIdAssociations,
                    associationType: AssociationType.Campaign);
                OutputStatusMessage("PartialErrors:");
                CampaignManagementExampleHelper.OutputArrayOfBatchError(setAdExtensionsAssociationsResponse?.PartialErrors);

                // Get editorial rejection reasons for the ad extension and entity associations.

                OutputStatusMessage("-----\nGetAdExtensionsEditorialReasons:");
                var getAdExtensionsEditorialReasonsResponse = await CampaignManagementExampleHelper.GetAdExtensionsEditorialReasonsAsync(
                        accountId: authorizationData.AccountId,
                        adExtensionIdToEntityIdAssociations: adExtensionIdToEntityIdAssociations,
                        associationType: AssociationType.Campaign);
                OutputStatusMessage("EditorialReasons:");
                var adExtensionEditorialReasonCollection =
                    (AdExtensionEditorialReasonCollection[])getAdExtensionsEditorialReasonsResponse?.EditorialReasons;
                CampaignManagementExampleHelper.OutputArrayOfAdExtensionEditorialReasonCollection(adExtensionEditorialReasonCollection);
                OutputStatusMessage("PartialErrors:");
                CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionsEditorialReasonsResponse?.PartialErrors);
                
                // Get only the location ad extensions and then remove scheduling.

                AdExtensionsTypeFilter adExtensionsTypeFilter = AdExtensionsTypeFilter.LocationAdExtension;

                // In this example partial errors will be returned for indices where the ad extensions 
                // are not location ad extensions.
                // This is an example, and ideally you would only send the required ad extension IDs.

                OutputStatusMessage("-----\nGetAdExtensionsByIds:");
                var getAdExtensionsByIdsResponse = (await CampaignManagementExampleHelper.GetAdExtensionsByIdsAsync(
                    accountId: authorizationData.AccountId,
                    adExtensionIds: adExtensionIds,
                    adExtensionType: adExtensionsTypeFilter,
                    returnAdditionalFields: AdExtensionAdditionalField.DisplayText | AdExtensionAdditionalField.Images));
                adExtensions = getAdExtensionsByIdsResponse?.AdExtensions.ToArray();
                BatchError[] getAdExtensionErrors = getAdExtensionsByIdsResponse?.PartialErrors.ToArray();
                OutputStatusMessage("AdExtensions:");
                CampaignManagementExampleHelper.OutputArrayOfAdExtension(adExtensions);
                OutputStatusMessage("PartialErrors:");
                CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionErrors);
                
                var updateExtensions = new List<AdExtension>();
                var updateExtensionIds = new List<long>();

                foreach (var extension in adExtensions)
                {
                    // GetAdExtensionsByIds will return a nil element if the request conditions were not met.
                    if (extension != null && extension.Id != null)
                    {
                        // Remove read-only elements that would otherwise cause the update operation to fail.
                        var updateExtension = SetReadOnlyAdExtensionElementsToNull(extension);

                        // If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged. 
                        // If you set this to any non-null Schedule object, you are effectively replacing existing scheduling 
                        // for the ad extension. In this example, we will remove any existing scheduling by setting this element  
                        // to an empty Schedule object.
                        updateExtension.Scheduling = new Schedule { };
                        updateExtensions.Add(updateExtension);
                        updateExtensionIds.Add((long)updateExtension.Id);
                    }
                }

                OutputStatusMessage("-----\nUpdateAdExtensions:");                
                await CampaignManagementExampleHelper.UpdateAdExtensionsAsync(
                    accountId: authorizationData.AccountId, 
                    adExtensions: updateExtensions);
                OutputStatusMessage("Removed scheduling from the location ad extensions.");

                // Get only the location extensions to output the result.

                OutputStatusMessage("-----\nGetAdExtensionsByIds:");
                getAdExtensionsByIdsResponse = await CampaignManagementExampleHelper.GetAdExtensionsByIdsAsync(
                    accountId: authorizationData.AccountId,
                    adExtensionIds: updateExtensionIds,
                    adExtensionType: adExtensionsTypeFilter,
                    returnAdditionalFields: AdExtensionAdditionalField.DisplayText | AdExtensionAdditionalField.Images);
                adExtensions = getAdExtensionsByIdsResponse?.AdExtensions.ToArray();
                getAdExtensionErrors = getAdExtensionsByIdsResponse?.PartialErrors.ToArray();
                OutputStatusMessage("AdExtensions:");
                CampaignManagementExampleHelper.OutputArrayOfAdExtension(adExtensions);
                OutputStatusMessage("PartialErrors:");
                CampaignManagementExampleHelper.OutputArrayOfBatchError(getAdExtensionErrors);

                // Delete the ad extension associations, ad extensions, and campaign, that were previously added.  
                // At this point the ad extensions are still available in the account's ad extensions library. 

                OutputStatusMessage("-----\nDeleteAdExtensionsAssociations:");
                await CampaignManagementExampleHelper.DeleteAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign);
                OutputStatusMessage("Deleted ad extension associations.");

                // Delete the ad extensions from the account's ad extension library.

                OutputStatusMessage("-----\nDeleteAdExtensions:");
                await CampaignManagementExampleHelper.DeleteAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensionIds);
                OutputStatusMessage("Deleted ad extensions.");

                // Delete the account's media that was used for the image ad extension.

                OutputStatusMessage("-----\nDeleteMedia:");
                await CampaignManagementExampleHelper.DeleteMediaAsync(
                    accountId: authorizationData.AccountId,
                    mediaIds: mediaIds);

                foreach (var id in mediaIds)
                {
                    OutputStatusMessage(string.Format("Deleted Media Id {0}", id));
                }

                // Delete the campaign and everything it contains e.g., ad groups and ads.

                OutputStatusMessage("-----\nDeleteCampaigns:");
                await CampaignManagementExampleHelper.DeleteCampaignsAsync(
                    accountId: authorizationData.AccountId,
                    campaignIds: new[] { (long)campaignIds[0] });
                OutputStatusMessage(string.Format("Deleted Campaign Id {0}", campaignIds[0]));
            }
            // Catch authentication exceptions
            catch (OAuthTokenRequestException ex)
            {
                OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description));
            }
            // Catch Campaign Management service exceptions
            catch (FaultException<Microsoft.BingAds.V13.CampaignManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V13.CampaignManagement.ApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V13.CampaignManagement.EditorialApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            // Catch Customer Management service exceptions
            catch (FaultException<Microsoft.BingAds.V13.CustomerManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V13.CustomerManagement.ApiFault> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (Exception ex)
            {
                OutputStatusMessage(ex.Message);
            }
        }

        /// <summary>
        /// Get image media that can be managed with the Campaign Management API.
        /// </summary>
        /// <param name="mediaType">The media type reflects the aspect ratio.</param>
        /// <param name="imageFileName">The file name and path.</param>
        /// <param name="imageFormat">For supported image formats see <see href="https://go.microsoft.com/fwlink/?linkid=872754">Image remarks</see>.</param>
        /// <returns>A Campaign Management Image object.</returns>
        private Microsoft.BingAds.V13.CampaignManagement.Image GetImageMedia(
            string mediaType,
            string imageFileName,
            System.Drawing.Imaging.ImageFormat imageFormat)
        {
            var image = new Microsoft.BingAds.V13.CampaignManagement.Image();
            image.Data = GetBmpBase64String(imageFileName, imageFormat);
            image.MediaType = mediaType;
            image.Type = "Image";

            return image;
        }

        /// <summary>
        /// Get the image media as base64 string.
        /// </summary>
        /// <param name="imageFileName">The file name and path.</param>
        /// <param name="imageFormat">For supported image formats see <see href="https://go.microsoft.com/fwlink/?linkid=872754">Image remarks</see>.</param>
        /// <returns></returns>
        private string GetBmpBase64String(
            string imageFileName,
            System.Drawing.Imaging.ImageFormat imageFormat)
        {
            var bmp = new Bitmap(imageFileName);
            using (MemoryStream ms = new MemoryStream())
            {
                bmp.Save(ms, imageFormat);
                byte[] imageBytes = ms.ToArray();
                string base64String = Convert.ToBase64String(imageBytes);
                return base64String;
            }
        }        
    }
}
package com.microsoft.bingads.examples.v13;

import com.microsoft.bingads.*;
import com.microsoft.bingads.v13.campaignmanagement.*;

import java.util.ArrayList;
import java.util.Calendar;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.util.Base64;

public class AdExtensions extends ExampleBase {

    public static void main(java.lang.String[] args) {
        
    // To run this example you'll need to provide your own image.  
        // For required aspect ratios and recommended dimensions please see 
        // Image remarks at https://go.microsoft.com/fwlink/?linkid=872754.

        java.lang.String MEDIA_FILE_PATH = "c:\\dev\\media\\";
        java.lang.String IMAGE_AD_EXTENSION_MEDIA_FILE_NAME = "imageadextension300x200.png";
        
        try
        {
            authorizationData = getAuthorizationData();

            CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient<ICampaignManagementService>(
                    authorizationData,
                    API_ENVIRONMENT,
                    ICampaignManagementService.class);
                        
            Calendar calendar = Calendar.getInstance();
                                      
            // Add a campaign to associate with ad extensions. 

            ArrayOfCampaign campaigns = new ArrayOfCampaign();
            Campaign campaign = new Campaign();
            campaign.setBudgetType(BudgetLimitType.DAILY_BUDGET_STANDARD);
            campaign.setDailyBudget(50.00);
            ArrayOfstring languages = new ArrayOfstring();
            languages.getStrings().add("All");
            campaign.setLanguages(languages);
            campaign.setName("Everyone's Shoes " + System.currentTimeMillis());
            campaign.setTimeZone("PacificTimeUSCanadaTijuana");
            campaigns.getCampaigns().add(campaign);

            outputStatusMessage("-----\nAddCampaigns:");
            AddCampaignsResponse addCampaignsResponse = CampaignManagementExampleHelper.addCampaigns(
                authorizationData.getAccountId(), 
                campaigns
            );            
            ArrayOfNullableOflong campaignIds = addCampaignsResponse.getCampaignIds();
            ArrayOfBatchError campaignErrors = addCampaignsResponse.getPartialErrors();
            outputStatusMessage("CampaignIds:");
            CampaignManagementExampleHelper.outputArrayOfNullableOflong(campaignIds);
            outputStatusMessage("PartialErrors:");
            CampaignManagementExampleHelper.outputArrayOfBatchError(campaignErrors);
            
            // Create media for the image ad extension that we'll add later. 
            
            Image imageAdExtensionMedia = getImageMedia(
                "Image15x10",
                MEDIA_FILE_PATH + IMAGE_AD_EXTENSION_MEDIA_FILE_NAME);

            ArrayOfMedia addMedia = new ArrayOfMedia();
            addMedia.getMedias().add(imageAdExtensionMedia);
            CampaignManagementExampleHelper.outputArrayOfMedia(addMedia);

            outputStatusMessage("-----\nAddMedia:");
            ArrayOflong mediaIds = CampaignManagementExampleHelper.addMedia(
                authorizationData.getAccountId(),
                addMedia).getMediaIds();
            outputStatusMessage("MediaIds:");
            CampaignManagementExampleHelper.outputArrayOflong(mediaIds);

            // Add the extensions to the account's library.

            ArrayOfAdExtension adExtensions = new ArrayOfAdExtension();
            
            ActionAdExtension actionAdExtension = new ActionAdExtension();
            actionAdExtension.setActionType(ActionAdExtensionActionType.ACT_NOW);
            com.microsoft.bingads.v13.campaignmanagement.ArrayOfstring finalUrls = new com.microsoft.bingads.v13.campaignmanagement.ArrayOfstring();
            finalUrls.getStrings().add("https://www.contoso.com/womenshoesale");
            actionAdExtension.setFinalUrls(finalUrls);
            actionAdExtension.setLanguage("English");
            actionAdExtension.setStatus(AdExtensionStatus.ACTIVE);
            adExtensions.getAdExtensions().add(actionAdExtension);

            AppAdExtension appAdExtension = new AppAdExtension();
            appAdExtension.setAppPlatform("Windows");
            appAdExtension.setAppStoreId("AppStoreIdGoesHere");
            appAdExtension.setDestinationUrl("DestinationUrlGoesHere");
            appAdExtension.setDisplayText("Contoso");
            // If you supply the AppAdExtension properties above, then you can add this line.
            //adExtensions.getAdExtensions().add(appAdExtension);

            CallAdExtension callAdExtension = new CallAdExtension();
            callAdExtension.setCountryCode("US");
            callAdExtension.setPhoneNumber("2065550100");
            callAdExtension.setIsCallOnly(false);
            // Include the call extension Monday - Friday from 9am - 9pm
            // in the account's time zone.
            Schedule callScheduling = new Schedule();
            ArrayOfDayTime callDayTimeRanges = new ArrayOfDayTime();
            DayTime callMonday = new DayTime();
            callMonday.setDay(Day.MONDAY);
            callMonday.setStartHour(9);
            callMonday.setStartMinute(Minute.ZERO);
            callMonday.setEndHour(21);
            callMonday.setEndMinute(Minute.ZERO);
            callDayTimeRanges.getDayTimes().add(callMonday);
            DayTime callTuesday = new DayTime();
            callTuesday.setDay(Day.TUESDAY);
            callTuesday.setStartHour(9);
            callTuesday.setStartMinute(Minute.ZERO);
            callTuesday.setEndHour(21);
            callTuesday.setEndMinute(Minute.ZERO);
            callDayTimeRanges.getDayTimes().add(callTuesday);
            DayTime callWednesday = new DayTime();
            callWednesday.setDay(Day.WEDNESDAY);
            callWednesday.setStartHour(9);
            callWednesday.setStartMinute(Minute.ZERO);
            callWednesday.setEndHour(21);
            callWednesday.setEndMinute(Minute.ZERO);
            callDayTimeRanges.getDayTimes().add(callWednesday);
            DayTime callThursday = new DayTime();
            callThursday.setDay(Day.THURSDAY);
            callThursday.setStartHour(9);
            callThursday.setStartMinute(Minute.ZERO);
            callThursday.setEndHour(21);
            callThursday.setEndMinute(Minute.ZERO);
            callDayTimeRanges.getDayTimes().add(callThursday);
            DayTime callFriday = new DayTime();
            callFriday.setDay(Day.FRIDAY);
            callFriday.setStartHour(9);
            callFriday.setStartMinute(Minute.ZERO);
            callFriday.setEndHour(21);
            callFriday.setEndMinute(Minute.ZERO);
            callDayTimeRanges.getDayTimes().add(callFriday);
            callScheduling.setDayTimeRanges(callDayTimeRanges);
            callScheduling.setEndDate(new com.microsoft.bingads.v13.campaignmanagement.Date());
            callScheduling.getEndDate().setDay(31);
            callScheduling.getEndDate().setMonth(12);
            callScheduling.getEndDate().setYear(calendar.get(Calendar.YEAR) + 1);
            callScheduling.setStartDate(null);
            callAdExtension.setScheduling(callScheduling);
            adExtensions.getAdExtensions().add(callAdExtension);

            CalloutAdExtension calloutAdExtension = new CalloutAdExtension();
            calloutAdExtension.setText("Callout text");
            adExtensions.getAdExtensions().add(calloutAdExtension);

            LocationAdExtension locationAdExtension = new LocationAdExtension();
            locationAdExtension.setPhoneNumber("206-555-0100");
            locationAdExtension.setCompanyName("Contoso Shoes");
            com.microsoft.bingads.v13.campaignmanagement.Address address = 
                    new com.microsoft.bingads.v13.campaignmanagement.Address();
            address.setStreetAddress("1234 Washington Place");
            address.setStreetAddress2("Suite 1210");
            address.setCityName("Woodinville");
            address.setProvinceName("WA"); 
            address.setCountryCode("US");
            address.setPostalCode("98608");
            locationAdExtension.setAddress(address);
            // Include the location extension every Saturday morning
            // in the search user's time zone.
            Schedule locationScheduling = new Schedule();
            ArrayOfDayTime locationDayTimeRanges = new ArrayOfDayTime();
            DayTime locationDayTime = new DayTime();
            locationDayTime.setDay(Day.SATURDAY);
            locationDayTime.setStartHour(9);
            locationDayTime.setStartMinute(Minute.ZERO);
            locationDayTime.setEndHour(12);
            locationDayTime.setEndMinute(Minute.ZERO);
            locationDayTimeRanges.getDayTimes().add(locationDayTime);
            locationScheduling.setDayTimeRanges(locationDayTimeRanges);
            locationScheduling.setEndDate(new com.microsoft.bingads.v13.campaignmanagement.Date());
            locationScheduling.getEndDate().setDay(31);
            locationScheduling.getEndDate().setMonth(12);
            locationScheduling.getEndDate().setYear(calendar.get(Calendar.YEAR) + 1);
            locationScheduling.setStartDate(null);
            locationAdExtension.setScheduling(locationScheduling);
            adExtensions.getAdExtensions().add(locationAdExtension);
            
            PriceAdExtension priceAdExtension = new PriceAdExtension();
            priceAdExtension.setLanguage("English");
            priceAdExtension.setPriceExtensionType(PriceExtensionType.EVENTS);
            ArrayOfPriceTableRow tableRows = new ArrayOfPriceTableRow();
            PriceTableRow tableRowA = new PriceTableRow();
            tableRowA.setCurrencyCode("USD");
            tableRowA.setDescription("Come to the event");
            tableRowA.setFinalUrls(finalUrls);
            tableRowA.setHeader("New Event");
            tableRowA.setPrice(9.99D);
            tableRowA.setPriceQualifier(PriceQualifier.FROM);
            tableRowA.setPriceUnit(PriceUnit.PER_DAY);
            tableRows.getPriceTableRows().add(tableRowA);
            PriceTableRow tableRowB = new PriceTableRow();
            tableRowB.setCurrencyCode("USD");
            tableRowB.setDescription("Come to the next event");
            tableRowB.setFinalUrls(finalUrls);
            tableRowB.setHeader("Next Event");
            tableRowB.setPrice(9.99D);
            tableRowB.setPriceQualifier(PriceQualifier.FROM);
            tableRowB.setPriceUnit(PriceUnit.PER_DAY);
            tableRows.getPriceTableRows().add(tableRowB);
            PriceTableRow tableRowC = new PriceTableRow();
            tableRowC.setCurrencyCode("USD");
            tableRowC.setDescription("Come to the final event");
            tableRowC.setFinalUrls(finalUrls);
            tableRowC.setHeader("Final Event");
            tableRowC.setPrice(9.99D);
            tableRowC.setPriceQualifier(PriceQualifier.FROM);
            tableRowC.setPriceUnit(PriceUnit.PER_DAY);
            tableRows.getPriceTableRows().add(tableRowC);
            priceAdExtension.setTableRows(tableRows);
            adExtensions.getAdExtensions().add(priceAdExtension);

            ReviewAdExtension reviewAdExtension = new ReviewAdExtension();
            reviewAdExtension.setIsExact(true);
            reviewAdExtension.setSource("Review Source Name");
            reviewAdExtension.setText("Review Text");
            // The Url of the third-party review. This is not your business Url.
            reviewAdExtension.setUrl("https://review.contoso.com"); 
            adExtensions.getAdExtensions().add(reviewAdExtension);
            
            SitelinkAdExtension sitelinkAdExtension = new SitelinkAdExtension();
            sitelinkAdExtension.setDescription1("Simple & Transparent.");
            sitelinkAdExtension.setDescription2("No Upfront Cost.");
            sitelinkAdExtension.setDisplayText("Everyone's Shoe Sale");
            sitelinkAdExtension.setFinalUrls(finalUrls);
            adExtensions.getAdExtensions().add(sitelinkAdExtension);
                        
            StructuredSnippetAdExtension structuredSnippetAdExtension = new StructuredSnippetAdExtension();
            structuredSnippetAdExtension.setHeader("Brands");
            com.microsoft.bingads.v13.campaignmanagement.ArrayOfstring values = new com.microsoft.bingads.v13.campaignmanagement.ArrayOfstring();
            values.getStrings().add("Windows");
            values.getStrings().add("Xbox");
            values.getStrings().add("Skype");
            structuredSnippetAdExtension.setValues(values);
            adExtensions.getAdExtensions().add(structuredSnippetAdExtension);            
            
            outputStatusMessage("-----\nAddAdExtensions:");
            AddAdExtensionsResponse addAdExtensionsResponse = CampaignManagementExampleHelper.addAdExtensions(
                authorizationData.getAccountId(),
                adExtensions);
            outputStatusMessage("AdExtensionIdentities:");
            ArrayOfAdExtensionIdentity adExtensionIdentities = addAdExtensionsResponse.getAdExtensionIdentities();
            outputStatusMessage("NestedPartialErrors:");
            CampaignManagementExampleHelper.outputArrayOfBatchErrorCollection(addAdExtensionsResponse.getNestedPartialErrors());
            
            // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
            // operations each require a list of type AdExtensionIdToEntityIdAssociation.
            ArrayOfAdExtensionIdToEntityIdAssociation adExtensionIdToEntityIdAssociations = new ArrayOfAdExtensionIdToEntityIdAssociation();

            // GetAdExtensionsByIds requires a list of type long.
            ArrayOflong adExtensionIds = new ArrayOflong();

            // Loop through the list of extension IDs and build any required data structures
            // for subsequent operations. 

            for (AdExtensionIdentity adExtensionIdentity : adExtensionIdentities.getAdExtensionIdentities()) {
                AdExtensionIdToEntityIdAssociation adExtensionIdToEntityIdAssociation = new AdExtensionIdToEntityIdAssociation();
                adExtensionIdToEntityIdAssociation.setAdExtensionId(adExtensionIdentity.getId());
                adExtensionIdToEntityIdAssociation.setEntityId(campaignIds.getLongs().get(0));
                adExtensionIdToEntityIdAssociations.getAdExtensionIdToEntityIdAssociations().add(adExtensionIdToEntityIdAssociation);

                adExtensionIds.getLongs().add(adExtensionIdentity.getId());
            }

            // Associate the ad extensions with the campaign. 
            
            outputStatusMessage("-----\nSetAdExtensionsAssociations:");
            SetAdExtensionsAssociationsResponse setAdExtensionsAssociationsResponse = CampaignManagementExampleHelper.setAdExtensionsAssociations(
                authorizationData.getAccountId(), 
                adExtensionIdToEntityIdAssociations, 
                AssociationType.CAMPAIGN);
            outputStatusMessage("PartialErrors:");
            CampaignManagementExampleHelper.outputArrayOfBatchError(setAdExtensionsAssociationsResponse.getPartialErrors());
            
            // Get editorial rejection reasons for the ad extension and entity associations.
            
            outputStatusMessage("-----\nGetAdExtensionsEditorialReasons:");
            GetAdExtensionsEditorialReasonsResponse getAdExtensionsEditorialReasonsResponse = CampaignManagementExampleHelper.getAdExtensionsEditorialReasons(
                authorizationData.getAccountId(), 
                adExtensionIdToEntityIdAssociations, 
                AssociationType.CAMPAIGN);
            outputStatusMessage("EditorialReasons:");
            ArrayOfAdExtensionEditorialReasonCollection adExtensionEditorialReasonCollection = getAdExtensionsEditorialReasonsResponse.getEditorialReasons();
            CampaignManagementExampleHelper.outputArrayOfAdExtensionEditorialReasonCollection(adExtensionEditorialReasonCollection);
            outputStatusMessage("PartialErrors:");
            CampaignManagementExampleHelper.outputArrayOfBatchError(getAdExtensionsEditorialReasonsResponse.getPartialErrors());
            
            // Get only the location extensions and remove scheduling.

            ArrayList<AdExtensionsTypeFilter> adExtensionsTypeFilter = new ArrayList<AdExtensionsTypeFilter>();
            adExtensionsTypeFilter.add(AdExtensionsTypeFilter.LOCATION_AD_EXTENSION);
            
            ArrayList<AdExtensionAdditionalField> returnAdditionalFields = new ArrayList<AdExtensionAdditionalField>();
            returnAdditionalFields.add(AdExtensionAdditionalField.DISPLAY_TEXT);
            returnAdditionalFields.add(AdExtensionAdditionalField.IMAGES);

            // In this example partial errors will be returned for indices where the ad extensions 
            // are not location ad extensions.
            // This is an example, and ideally you would only send the required ad extension IDs.
            
            outputStatusMessage("-----\nGetAdExtensionsByIds:");
            GetAdExtensionsByIdsResponse getAdExtensionsByIdsResponse = CampaignManagementExampleHelper.getAdExtensionsByIds(
                authorizationData.getAccountId(),
                adExtensionIds,
                adExtensionsTypeFilter,
                returnAdditionalFields);
            adExtensions = getAdExtensionsByIdsResponse.getAdExtensions();
            outputStatusMessage("AdExtensions:");
            CampaignManagementExampleHelper.outputArrayOfAdExtension(adExtensions);
            outputStatusMessage("PartialErrors:");
            CampaignManagementExampleHelper.outputArrayOfBatchError(getAdExtensionsByIdsResponse.getPartialErrors());     

            ArrayOfAdExtension updateExtensions = new ArrayOfAdExtension();
            ArrayOflong updateExtensionIds = new ArrayOflong();

            for (AdExtension extension : adExtensions.getAdExtensions())
            {
                // GetAdExtensionsByIds will return a nil element if the request conditions were not met.
                if(extension != null && extension.getId() != null)
                {
                    // Remove read-only elements that would otherwise cause the update operation to fail.
                    AdExtension updateExtension = setReadOnlyAdExtensionElementsToNull(extension);

                    // If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged. 
                    // If you set this to any non-null Schedule object, you are effectively replacing existing scheduling 
                    // for the ad extension. In this example, we will remove any existing scheduling by setting this element  
                    // to an empty Schedule object.
                    updateExtension.setScheduling(new Schedule());
                    updateExtensions.getAdExtensions().add(updateExtension);
                    updateExtensionIds.getLongs().add((long)updateExtension.getId());
                }
            }

            outputStatusMessage("-----\nUpdateAdExtensions:"); 
            CampaignManagementExampleHelper.updateAdExtensions(
                    authorizationData.getAccountId(), 
                    updateExtensions);
            outputStatusMessage("Removed scheduling from the location ad extensions.");

            // Get only the location extension to output the result.
            
            outputStatusMessage("-----\nGetAdExtensionsByIds:");
            getAdExtensionsByIdsResponse = CampaignManagementExampleHelper.getAdExtensionsByIds(
                authorizationData.getAccountId(),
                adExtensionIds,
                adExtensionsTypeFilter,
                returnAdditionalFields);
            adExtensions = getAdExtensionsByIdsResponse.getAdExtensions();
            outputStatusMessage("AdExtensions:");
            CampaignManagementExampleHelper.outputArrayOfAdExtension(adExtensions);
            outputStatusMessage("PartialErrors:");
            CampaignManagementExampleHelper.outputArrayOfBatchError(getAdExtensionsByIdsResponse.getPartialErrors());  

            // Delete the ad extension associations, ad extensions, and campaign, that were previously added.  
            // At this point the ad extensions are still available in the account's ad extensions library. 
            
            outputStatusMessage("-----\nDeleteAdExtensionsAssociations:");
            CampaignManagementExampleHelper.deleteAdExtensionsAssociations(
                authorizationData.getAccountId(),
                adExtensionIdToEntityIdAssociations,
                AssociationType.CAMPAIGN);
            outputStatusMessage("Deleted ad extension associations.");

            // Delete the ad extensions from the account’s ad extension library.

            outputStatusMessage("-----\nDeleteAdExtensions:");
            CampaignManagementExampleHelper.deleteAdExtensions(
                authorizationData.getAccountId(),
                adExtensionIds);
            outputStatusMessage("Deleted ad extensions.");
            
            // Delete the account's media that was used for the image ad extension.
            
            outputStatusMessage("-----\nDeleteMedia:");
            CampaignManagementExampleHelper.deleteMedia(
                    authorizationData.getAccountId(), 
                    mediaIds);

            for (java.lang.Long id : mediaIds.getLongs())
            {
                outputStatusMessage(String.format("Deleted Media Id %s", id));
            }

            // Delete the campaign and everything it contains e.g., ad groups and ads.

            outputStatusMessage("-----\nDeleteCampaigns:");
            ArrayOflong deleteCampaignIds = new ArrayOflong();
            deleteCampaignIds.getLongs().add(campaignIds.getLongs().get(0));
            CampaignManagementExampleHelper.deleteCampaigns(
                    authorizationData.getAccountId(), 
                    deleteCampaignIds);
            outputStatusMessage(String.format("Deleted CampaignId %d", deleteCampaignIds.getLongs().get(0)));        
        } 
        catch (Exception ex) {
            String faultXml = ExampleExceptionHelper.getBingAdsExceptionFaultXml(ex, System.out);
            outputStatusMessage(faultXml);
            String message = ExampleExceptionHelper.handleBingAdsSDKException(ex, System.out);
            outputStatusMessage(message);
        }
    }        
    
    // Get image media that can be managed with the Campaign Management API.

    static Image getImageMedia(
        java.lang.String mediaType, 
        java.lang.String imageFileName) throws UnsupportedEncodingException, IOException
    {
        Image image = new Image();
        image.setData(getBmpBase64String(imageFileName));
        image.setMediaType(mediaType);
        image.setType("Image");

        return image;
    }

    // Get the image media as base64 string.

    static java.lang.String getBmpBase64String(
        java.lang.String imageFileName) throws UnsupportedEncodingException, IOException
    {
        File fi = new File(imageFileName);
        byte[] imageBytes = Files.readAllBytes(fi.toPath());
        java.lang.String base64String = new java.lang.String(Base64.getEncoder().encode(imageBytes), "UTF-8");
        return base64String;
    }
}
<?php

namespace Microsoft\BingAds\Samples\V13;

// For more information about installing and using the Bing Ads PHP SDK, 
// see https://go.microsoft.com/fwlink/?linkid=838593.

require_once __DIR__ . "/../vendor/autoload.php";

include __DIR__ . "/AuthHelper.php";
include __DIR__ . "/OutputHelper.php";
include __DIR__ . "/CampaignManagementExampleHelper.php";

use SoapVar;
use SoapFault;
use Exception;

// Specify the Microsoft\BingAds\V13\CampaignManagement classes that will be used.
use Microsoft\BingAds\V13\CampaignManagement\Campaign;
use Microsoft\BingAds\V13\CampaignManagement\BudgetLimitType;
use Microsoft\BingAds\V13\CampaignManagement\AdExtension;
use Microsoft\BingAds\V13\CampaignManagement\ActionAdExtension;
use Microsoft\BingAds\V13\CampaignManagement\ActionAdExtensionActionType;
use Microsoft\BingAds\V13\CampaignManagement\AppAdExtension;
use Microsoft\BingAds\V13\CampaignManagement\CallAdExtension;
use Microsoft\BingAds\V13\CampaignManagement\CalloutAdExtension;
use Microsoft\BingAds\V13\CampaignManagement\Image;
use Microsoft\BingAds\V13\CampaignManagement\ImageAdExtension;
use Microsoft\BingAds\V13\CampaignManagement\LocationAdExtension;
use Microsoft\BingAds\V13\CampaignManagement\PriceAdExtension;
use Microsoft\BingAds\V13\CampaignManagement\PriceTableRow;
use Microsoft\BingAds\V13\CampaignManagement\ReviewAdExtension;
use Microsoft\BingAds\V13\CampaignManagement\SitelinkAdExtension;
use Microsoft\BingAds\V13\CampaignManagement\StructuredSnippetAdExtension;
use Microsoft\BingAds\V13\CampaignManagement\Schedule;
use Microsoft\BingAds\V13\CampaignManagement\DayTime;
use Microsoft\BingAds\V13\CampaignManagement\Day;
use Microsoft\BingAds\V13\CampaignManagement\Minute;
use Microsoft\BingAds\V13\CampaignManagement\Date;
use Microsoft\BingAds\V13\CampaignManagement\AdExtensionAssociation;
use Microsoft\BingAds\V13\CampaignManagement\AdExtensionAssociationCollection;
use Microsoft\BingAds\V13\CampaignManagement\AdExtensionEditorialReason;
use Microsoft\BingAds\V13\CampaignManagement\AdExtensionEditorialReasonCollection;
use Microsoft\BingAds\V13\CampaignManagement\AdExtensionEditorialStatus;
use Microsoft\BingAds\V13\CampaignManagement\AdExtensionIdentity;
use Microsoft\BingAds\V13\CampaignManagement\AdExtensionIdToEntityIdAssociation;
use Microsoft\BingAds\V13\CampaignManagement\AdExtensionStatus;
use Microsoft\BingAds\V13\CampaignManagement\AdExtensionsTypeFilter;
use Microsoft\BingAds\V13\CampaignManagement\Address;
use Microsoft\BingAds\V13\CampaignManagement\AssociationType;

// Specify the Microsoft\BingAds\Auth classes that will be used.
use Microsoft\BingAds\Auth\ServiceClient;
use Microsoft\BingAds\Auth\ServiceClientType;

// Specify the Microsoft\BingAds\Samples classes that will be used.
use Microsoft\BingAds\Samples\V13\AuthHelper;
use Microsoft\BingAds\Samples\V13\CampaignManagementExampleHelper;

// To run this example you'll need to provide your own image.  
// For required aspect ratios and recommended dimensions please see 
// Image remarks at https://go.microsoft.com/fwlink/?linkid=872754.

$mediaFilePath = "c:\\dev\\media\\";
$imageAdExtensionMediaFileName = "imageadextension300x200.png";

try
{
    // Authenticate user credentials and set the account ID for the sample.  
    AuthHelper::Authenticate();

    date_default_timezone_set('UTC');

    // Add a campaign to associate with ad extensions.
    
    $campaigns = array();   
    $campaign = new Campaign();
    $campaign->Name = "Women's Shoes " . $_SERVER['REQUEST_TIME'];
    $campaign->BudgetType = BudgetLimitType::DailyBudgetStandard;
    $campaign->DailyBudget = 50.00;
    $campaign->Languages = array("All");
    $campaign->TimeZone = "PacificTimeUSCanadaTijuana";
    $campaigns[] = $campaign;
    
    print("-----\r\nAddCampaigns:\r\n");
    $addCampaignsResponse = CampaignManagementExampleHelper::AddCampaigns(
        $GLOBALS['AuthorizationData']->AccountId, 
        $campaigns
    );
    $campaignIds = $addCampaignsResponse->CampaignIds;
    print("CampaignIds:\r\n");
    CampaignManagementExampleHelper::OutputArrayOfLong($campaignIds);
    print("PartialErrors:\r\n");
    CampaignManagementExampleHelper::OutputArrayOfBatchError($addCampaignsResponse->PartialErrors);

    $imageAdExtensionMedia = GetImageMedia(
        "Image15x10",
        $mediaFilePath . $imageAdExtensionMediaFileName
    );

    $addMedia = array();
    $addMedia[] = $imageAdExtensionMedia;

    print("-----\r\nAddMedia:\r\n");
    $mediaIds = CampaignManagementExampleHelper::AddMedia(
        $GLOBALS['AuthorizationData']->AccountId,
        $addMedia
    )->MediaIds;        
    print("MediaIds:\r\n");
    CampaignManagementExampleHelper::OutputArrayOfLong($mediaIds);
    
    // Add the extensions to the account's library.
    
    $adExtensions = array();

    $extension = new ActionAdExtension();
    $extension->ActionType = ActionAdExtensionActionType::ActNow;
    $extension->FinalUrls = array("http://www.contoso.com/womenshoesale");;
    $extension->Language = "English";
    $extension->Status = AdExtensionStatus::Active;
    $encodedExtension = new SoapVar(
        $extension, SOAP_ENC_OBJECT, 
        'ActionAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace()
    );
    $adExtensions[] = $encodedExtension;
    
    $extension = new AppAdExtension();
    $extension->AppPlatform = "Windows";
    $extension->AppStoreId="AppStoreIdGoesHere";
    $extension->DisplayText= "Contoso";
    $extension->DestinationUrl="DestinationUrlGoesHere";
    $encodedExtension = new SoapVar(
        $extension, SOAP_ENC_OBJECT, 
        'AppAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace()
    );
    // If you supply the AppAdExtension properties above, then you can add this line.
    //$adExtensions[] = $encodedExtension;
    
    $extension = new CallAdExtension();
    $extension->CountryCode = "US";
    $extension->PhoneNumber = "2065550100";
    $extension->IsCallOnly = false;
    // Include the call extension Monday - Friday from 9am - 9pm
    // in the account's time zone.
    $callScheduling = new Schedule(); 
    $callDayTimeRanges = array();
    for($index = 0; $index < 5; $index++)
    {
        $dayTime = new DayTime();
        $dayTime->StartHour = 9;
        $dayTime->StartMinute = Minute::Zero;
        $dayTime->EndHour = 21;
        $dayTime->EndMinute = Minute::Zero;
        $callDayTimeRanges[] = $dayTime;
    }
    $callDayTimeRanges[0]->Day = Day::Monday;
    $callDayTimeRanges[1]->Day = Day::Tuesday;
    $callDayTimeRanges[2]->Day = Day::Wednesday;
    $callDayTimeRanges[3]->Day = Day::Thursday;
    $callDayTimeRanges[4]->Day = Day::Friday;
    $callScheduling->DayTimeRanges = $callDayTimeRanges;    
    $callScheduling->UseSearcherTimeZone = false;
    $callScheduling->StartDate = null;
    $callSchedulingEndDate = new Date();
    $callSchedulingEndDate->Day = 31;
    $callSchedulingEndDate->Month = 12;
    $callSchedulingEndDate->Year = date("Y");
    $callScheduling->EndDate = $callSchedulingEndDate;
    $extension->Scheduling = $callScheduling;
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'CallAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace()
    );
    $adExtensions[] = $encodedExtension;
    
    $extension = new CalloutAdExtension();
    $extension->Text = "Callout Text";
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'CalloutAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace()
    );
    $adExtensions[] = $encodedExtension;
        
    $extension = new LocationAdExtension();
    $extension->PhoneNumber = "206-555-0100";
    $extension->CompanyName = "Alpine Ski House";
    $extension->Address = new Address;
    $extension->Address->StreetAddress = "1234 Washington Place";
    $extension->Address->StreetAddress2 = "Suite 1210";
    $extension->Address->CityName = "Woodinville";
    $extension->Address->ProvinceName = "WA"; 
    $extension->Address->CountryCode = "US";
    $extension->Address->PostalCode = "98608";
    // Include the location extension every Saturday morning
    // in the search user's time zone.
    $locationScheduling = new Schedule();
    $locationDayTimeRanges = array();
    $locationDayTime = new DayTime();
    $locationDayTime->Day = Day::Saturday;
    $locationDayTime->StartHour = 9;
    $locationDayTime->StartMinute = Minute::Zero;
    $locationDayTime->EndHour = 12;
    $locationDayTime->EndMinute = Minute::Zero;
    $locationDayTimeRanges[] = $locationDayTime;
    $locationScheduling->DayTimeRanges = $locationDayTimeRanges;    
    $locationScheduling->UseSearcherTimeZone = false;
    $locationScheduling->StartDate = null;
    $locationSchedulingEndDate = new Date();
    $locationSchedulingEndDate->Day = 31;
    $locationSchedulingEndDate->Month = 12;
    $locationSchedulingEndDate->Year = date("Y");
    $locationScheduling->EndDate = $locationSchedulingEndDate;
    $extension->Scheduling = $locationScheduling;
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'LocationAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace()
    );
    $adExtensions[] = $encodedExtension;  

    $extension = new PriceAdExtension();
    $extension->Language="English";
    $extension->PriceExtensionType='Events';
    $tableRows = array();
    $tableRowA = new PriceTableRow();
    $tableRowA->CurrencyCode="USD";
    $tableRowA->Description="Come to the event";
    $tableRowA->FinalUrls = array("http://www.contoso.com/womenshoesale");;
    $tableRowA->Header="New Event";
    $tableRowA->Price=9.99;
    $tableRowA->PriceQualifier='From';
    $tableRowA->PriceUnit='PerDay';
    $tableRows[] = $tableRowA;
    $tableRowB = new PriceTableRow();
    $tableRowB->CurrencyCode="USD";
    $tableRowB->Description="Come to the next event";
    $tableRowB->FinalUrls = array("http://www.contoso.com/womenshoesale");;
    $tableRowB->Header="Next Event";
    $tableRowB->Price=9.99;
    $tableRowB->PriceQualifier='From';
    $tableRowB->PriceUnit='PerDay';
    $tableRows[] = $tableRowB;
    $tableRowC = new PriceTableRow();
    $tableRowC->CurrencyCode="USD";
    $tableRowC->Description="Come to the final event";
    $tableRowC->FinalUrls = array("http://www.contoso.com/womenshoesale");;
    $tableRowC->Header="Final Event";
    $tableRowC->Price=9.99;
    $tableRowC->PriceQualifier='From';
    $tableRowC->PriceUnit='PerDay';
    $tableRows[] = $tableRowC;
    $extension->TableRows=$tableRows;
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'PriceAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace()
    );
    $adExtensions[] = $encodedExtension;  
    
    $extension = new ReviewAdExtension();
    $extension->IsExact = true;
    $extension->Source = "Review Source Name";
    $extension->Text = "Review Text";
    // The Url of the third-party review. This is not your business Url.
    $extension->Url = "http://review.contoso.com"; 
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'ReviewAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace()
    );
    $adExtensions[] = $encodedExtension;

    $extension = new SitelinkAdExtension();
    $extension->Description1 = "Simple & Transparent.";
    $extension->Description2 = "No Upfront Cost.";
    $extension->DisplayText = "Women's Shoe Sale";
    $extension->FinalUrls = array();
    $extension->FinalUrls[] = "http://www.contoso.com/womenshoesale";    
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'SitelinkAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace()
    );
    $adExtensions[] = $encodedExtension;
        
    $extension = new StructuredSnippetAdExtension();
    $extension->Header = "Brands";
    $extension->Values = array("Windows", "Xbox", "Skype");
    $encodedExtension = new SoapVar(
        $extension, 
        SOAP_ENC_OBJECT, 
        'StructuredSnippetAdExtension', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace()
    );
    $adExtensions[] = $encodedExtension;
        
    // Add all extensions to the account's ad extension library
    
    print("-----\r\nAddAdExtensions:\r\n");
    $addAdExtensionsResponse = CampaignManagementExampleHelper::AddAdExtensions(
        $GLOBALS['AuthorizationData']->AccountId, 
        $adExtensions
    );    
    $adExtensionIdentities = $addAdExtensionsResponse->AdExtensionIdentities;
    print("AdExtensionIdentities:\r\n");
    CampaignManagementExampleHelper::OutputArrayOfAdExtensionIdentity($adExtensionIdentities);
    print("NestedPartialErrors:\r\n");
    CampaignManagementExampleHelper::OutputArrayOfBatchErrorCollection($addAdExtensionsResponse->NestedPartialErrors);
    
    // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
    // operations each require a list of type AdExtensionIdToEntityIdAssociation.
    
    $adExtensionIdToEntityIdAssociations = array ();

    // GetAdExtensionsByIds requires a list of type long.
    
    $adExtensionIds = array ();
                
    // Loop through the list of extension IDs and build any required data structures
    // for subsequent operations. 
    
    $associations = array();
    
    for ($index = 0; $index < count($adExtensionIdentities->AdExtensionIdentity); $index++)
    {
        if(!empty($adExtensionIdentities->AdExtensionIdentity[$index]) && isset($adExtensionIdentities->AdExtensionIdentity[$index]->Id))
        {
            $adExtensionIdToEntityIdAssociations[$index] = new AdExtensionIdToEntityIdAssociation();
            $adExtensionIdToEntityIdAssociations[$index]->AdExtensionId = $adExtensionIdentities->AdExtensionIdentity[$index]->Id;;
            $adExtensionIdToEntityIdAssociations[$index]->EntityId = $campaignIds->long[0];
                    
            $adExtensionIds[$index] = $adExtensionIdentities->AdExtensionIdentity[$index]->Id;
        }
    };

    // Associate the ad extensions with the campaign. 
    
    print("-----\r\nSetAdExtensionsAssociations:\r\n");
    $setAdExtensionsAssociationsResponse = CampaignManagementExampleHelper::SetAdExtensionsAssociations(
        $GLOBALS['AuthorizationData']->AccountId,
        $adExtensionIdToEntityIdAssociations,
        AssociationType::Campaign
    );
    print("PartialErrors:\r\n");
    CampaignManagementExampleHelper::OutputArrayOfBatchError($setAdExtensionsAssociationsResponse->PartialErrors);

    // Get editorial rejection reasons for the respective ad extension and entity associations.
    
    print("-----\r\nGetAdExtensionsEditorialReasons:\r\n");
    $getAdExtensionsEditorialReasonsResponse = CampaignManagementExampleHelper::GetAdExtensionsEditorialReasons(
        $GLOBALS['AuthorizationData']->AccountId,
        $adExtensionIdToEntityIdAssociations,
        AssociationType::Campaign
    );
    $adExtensionEditorialReasonCollection = $getAdExtensionsEditorialReasonsResponse->EditorialReasons;
    print("EditorialReasons:\r\n");
    CampaignManagementExampleHelper::OutputArrayOfAdExtensionEditorialReasonCollection($adExtensionEditorialReasonCollection);
    print("PartialErrors:\r\n");
    CampaignManagementExampleHelper::OutputArrayOfBatchError($getAdExtensionsEditorialReasonsResponse->PartialErrors);
   
    // Get only the location extensions and remove scheduling.

    $adExtensionsTypeFilter = array(AdExtensionsTypeFilter::LocationAdExtension);

    // In this example partial errors will be returned for indices where the ad extensions 
    // are not location ad extensions.
    // This is an example, and ideally you would only send the required ad extension IDs.

    print("-----\r\nGetAdExtensionsByIds:\r\n");
    $adExtensions = CampaignManagementExampleHelper::GetAdExtensionsByIds(
        $GLOBALS['AuthorizationData']->AccountId,
        $adExtensionIds,
        $adExtensionsTypeFilter,
        null
    )->AdExtensions;
    print("AdExtensions:\r\n");
    CampaignManagementExampleHelper::OutputArrayOfAdExtension($adExtensions);

    $updateExtensions = array();
    $updateExtensionIds = array();

    foreach ($adExtensions->AdExtension as $extension)
    {
        // GetAdExtensionsByIds will return a nil element if the request filters / conditions were not met.
        if(!empty($extension) && isset($extension->Id))
        {
            // Remove read-only elements that would otherwise cause the update operation to fail.
            $updateExtension = OutputHelper::SetReadOnlyAdExtensionElementsToNull($extension);

            // If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged. 
            // If you set this to any non-null Schedule object, you are effectively replacing existing scheduling 
            // for the ad extension. In this example, we will remove any existing scheduling by setting this element  
            // to an empty Schedule object.
            $updateExtension->Scheduling = new Schedule();

            $updateExtensions[] = new SoapVar(
                $updateExtension, 
                SOAP_ENC_OBJECT, 
                'LocationAdExtension', 
                $GLOBALS['CampaignManagementProxy']->GetNamespace()
            );
            
            $updateExtensionIds[] = $updateExtension->Id;
        }
    }

    print("-----\r\nUpdateAdExtensions:\r\n");
    CampaignManagementExampleHelper::UpdateAdExtensions(
        $GLOBALS['AuthorizationData']->AccountId, 
        $updateExtensions
    );
    print("Removed scheduling from the location ad extensions.\r\n");

    // Get only the location extension to output the result.
    
    print("-----\r\nGetAdExtensionsByIds:\r\n");
    $adExtensions = CampaignManagementExampleHelper::GetAdExtensionsByIds(
        $GLOBALS['AuthorizationData']->AccountId,
        $adExtensionIds,
        $adExtensionsTypeFilter,
        null
    )->AdExtensions;
    print("AdExtensions:\r\n");
    CampaignManagementExampleHelper::OutputArrayOfAdExtension($adExtensions);
    
    // Delete the ad extension associations, ad extensions, and campaign, that were previously added.  
    // At this point the ad extensions are still available in the account's ad extensions library. 

    print("-----\r\nDeleteAdExtensionsAssociations:\r\n");
    CampaignManagementExampleHelper::DeleteAdExtensionsAssociations(
        $GLOBALS['AuthorizationData']->AccountId, 
        $adExtensionIdToEntityIdAssociations, 
        AssociationType::Campaign
    );
    
    // Deletes the ad extensions from the account's ad extension library.

    print("-----\r\nDeleteAdExtensions:\r\n");
    CampaignManagementExampleHelper::DeleteAdExtensions(
        $GLOBALS['AuthorizationData']->AccountId, 
        $adExtensionIds
    );
    print("Deleted ad extensions.\r\n");

    // Delete the account's media that was used for the image ad extension.

    print("-----\r\nDeleteMedia:\r\n");
    CampaignManagementExampleHelper::DeleteMedia(
        $GLOBALS['AuthorizationData']->AccountId, 
        $mediaIds
    );
    
    foreach ($mediaIds->long as $id)
    {
        printf("Deleted Media Id %s\r\n", $id);
    }

    // Delete the campaign and everything it contains e.g., ad groups and ads.

    print("-----\r\nDeleteCampaigns:\r\n");
    CampaignManagementExampleHelper::DeleteCampaigns(
        $GLOBALS['AuthorizationData']->AccountId, 
        array($campaignIds->long[0])
    );
    printf("Deleted Campaign Id %s\r\n", $campaignIds->long[0]);    
}
catch (SoapFault $e)
{
    printf("-----\r\nFault Code: %s\r\nFault String: %s\r\nFault Detail: \r\n", $e->faultcode, $e->faultstring);
    var_dump($e->detail);
    print "-----\r\nLast SOAP request/response:\r\n";
    print $GLOBALS['Proxy']->GetWsdl() . "\r\n";
    print $GLOBALS['Proxy']->GetService()->__getLastRequest()."\r\n";
    print $GLOBALS['Proxy']->GetService()->__getLastResponse()."\r\n";
}
catch (Exception $e)
{
    // Ignore fault exceptions that we already caught.
    if ($e->getPrevious())
    { ; }
    else
    {
        print $e->getCode()." ".$e->getMessage()."\r\n";
        print $e->getTraceAsString()."\r\n";
    }
}

function GetImageMedia(
    $mediaType,
    $imageFileName){
    $image = new Image();
    $image->Data = GetBase64ImageData($imageFileName);
    $image->MediaType = $mediaType;
    $image->Type = "Image";

    $encodedImage = new SoapVar(
        $image, 
        SOAP_ENC_OBJECT, 
        'Image', 
        $GLOBALS['CampaignManagementProxy']->GetNamespace()
    );

    return $encodedImage;
}

function GetBase64ImageData($imageFileName){
    $imageData = file_get_contents($imageFileName);
    $base64ImageData = base64_encode($imageData);

    return $base64ImageData;
}
import base64

from auth_helper import *
from campaignmanagement_example_helper import *

# You must provide credentials in auth_helper.py.

# To run this example you'll need to provide your own image.  
# For required aspect ratios and recommended dimensions please see 
# Image remarks at https://go.microsoft.com/fwlink/?linkid=872754.

MEDIA_FILE_PATH = "c:\dev\media\\"
IMAGE_AD_EXTENSION_MEDIA_FILE_NAME = "imageadextension300x200.png"

def main(authorization_data):
    
    try:
        
        # Add a campaign to associate with ad extensions. 

        campaigns=campaign_service.factory.create('ArrayOfCampaign')
        campaign=set_elements_to_none(campaign_service.factory.create('Campaign'))
        campaign.BudgetType='DailyBudgetStandard'
        campaign.DailyBudget=50
        languages=campaign_service.factory.create('ns3:ArrayOfstring')
        languages.string.append('All')
        campaign.Languages=languages
        campaign.Name="Women's Shoes " + strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
        campaign.TimeZone='PacificTimeUSCanadaTijuana'
        campaigns.Campaign.append(campaign)

        output_status_message("-----\nAddCampaigns:")
        add_campaigns_response=campaign_service.AddCampaigns(
            AccountId=authorization_data.account_id,
            Campaigns=campaigns
        )
        campaign_ids={
            'long': add_campaigns_response.CampaignIds['long'] if add_campaigns_response.CampaignIds['long'] else None
        }
        output_status_message("CampaignIds:")
        output_array_of_long(campaign_ids)
        output_status_message("PartialErrors:")
        output_array_of_batcherror(add_campaigns_response.PartialErrors)
        
        # Create media for the image ad extension that we'll add later. 

        image_ad_extension_media = get_image_media(
            "Image15x10",
            MEDIA_FILE_PATH + IMAGE_AD_EXTENSION_MEDIA_FILE_NAME)

        media = { 
            'Media': 
            [
                image_ad_extension_media
            ]
        }
        
        # Add the media to the account's library.

        output_status_message("-----\nAddMedia:")
        media_ids = campaign_service.AddMedia(
            AccountId=authorization_data.account_id,
            Media=media)
        output_status_message("MediaIds:")
        output_array_of_long(media_ids)
        
        # Add the extensions to the account's library.

        ad_extensions=campaign_service.factory.create('ArrayOfAdExtension')
        final_urls=campaign_service.factory.create('ns3:ArrayOfstring')
        final_urls.string.append('https://www.contoso.com/womenshoesale')

        action_ad_extension=set_elements_to_none(campaign_service.factory.create('ActionAdExtension'))
        action_ad_extension.ActionType='ActNow'
        action_ad_extension.FinalUrls=final_urls
        action_ad_extension.Language='English'
        action_ad_extension.Status='Active'
        ad_extensions.AdExtension.append(action_ad_extension)
        
        app_ad_extension=set_elements_to_none(campaign_service.factory.create('AppAdExtension'))
        app_ad_extension.AppPlatform='Windows'
        app_ad_extension.AppStoreId='AppStoreIdGoesHere'
        app_ad_extension.DisplayText='Contoso'
        app_ad_extension.DestinationUrl='DestinationUrlGoesHere'
        # If you supply the AppAdExtension properties above, then you can add this line.
        #ad_extensions.AdExtension.append(app_ad_extension)

        call_ad_extension=set_elements_to_none(campaign_service.factory.create('CallAdExtension'))
        call_ad_extension.CountryCode="US"
        call_ad_extension.PhoneNumber="2065550100"
        call_ad_extension.IsCallOnly=False
        # Include the call extension Monday - Friday from 9am - 9pm
        # in the account's time zone.
        call_scheduling=set_elements_to_none(campaign_service.factory.create('Schedule'))
        call_day_time_ranges=campaign_service.factory.create('ArrayOfDayTime')
        call_monday=set_elements_to_none(campaign_service.factory.create('DayTime'))
        call_monday.Day='Monday'
        call_monday.StartHour=9
        call_monday.StartMinute='Zero'
        call_monday.EndHour=21
        call_monday.EndMinute='Zero'
        call_day_time_ranges.DayTime.append(call_monday)
        call_tuesday=set_elements_to_none(campaign_service.factory.create('DayTime'))
        call_tuesday.Day='Tuesday'
        call_tuesday.StartHour=9
        call_tuesday.StartMinute='Zero'
        call_tuesday.EndHour=21
        call_tuesday.EndMinute='Zero'
        call_day_time_ranges.DayTime.append(call_tuesday)
        call_wednesday=set_elements_to_none(campaign_service.factory.create('DayTime'))
        call_wednesday.Day='Wednesday'
        call_wednesday.StartHour=9
        call_wednesday.StartMinute='Zero'
        call_wednesday.EndHour=21
        call_wednesday.EndMinute='Zero'
        call_day_time_ranges.DayTime.append(call_wednesday)
        call_thursday=set_elements_to_none(campaign_service.factory.create('DayTime'))
        call_thursday.Day='Thursday'
        call_thursday.StartHour=9
        call_thursday.StartMinute='Zero'
        call_thursday.EndHour=21
        call_thursday.EndMinute='Zero'
        call_day_time_ranges.DayTime.append(call_thursday)
        call_friday=set_elements_to_none(campaign_service.factory.create('DayTime'))
        call_friday.Day='Friday'
        call_friday.StartHour=9
        call_friday.StartMinute='Zero'
        call_friday.EndHour=21
        call_friday.EndMinute='Zero'
        call_day_time_ranges.DayTime.append(call_friday)
        call_scheduling.DayTimeRanges=call_day_time_ranges
        call_scheduling_end_date=campaign_service.factory.create('Date')
        call_scheduling_end_date.Day=31
        call_scheduling_end_date.Month=12
        call_scheduling_end_date.Year=strftime("%Y", gmtime())
        call_scheduling.EndDate=call_scheduling_end_date
        call_scheduling.StartDate=None
        call_ad_extension.Scheduling=call_scheduling
        ad_extensions.AdExtension.append(call_ad_extension)

        callout_ad_extension=set_elements_to_none(campaign_service.factory.create('CalloutAdExtension'))
        callout_ad_extension.Text="Callout Text"
        ad_extensions.AdExtension.append(callout_ad_extension)

        location_ad_extension=set_elements_to_none(campaign_service.factory.create('LocationAdExtension'))
        location_ad_extension.PhoneNumber="206-555-0100"
        location_ad_extension.CompanyName="Contoso Shoes"
        address=campaign_service.factory.create('Address')
        address.StreetAddress="1234 Washington Place"
        address.StreetAddress2="Suite 1210"
        address.CityName="Woodinville"
        address.ProvinceName="WA"
        address.CountryCode="US"
        address.PostalCode="98608"
        location_ad_extension.Address=address
        # Include the location extension every Saturday morning
        # in the search user's time zone.
        location_scheduling=set_elements_to_none(campaign_service.factory.create('Schedule'))
        location_day_time_ranges=campaign_service.factory.create('ArrayOfDayTime')
        location_day_time=set_elements_to_none(campaign_service.factory.create('DayTime'))
        location_day_time.Day='Saturday'
        location_day_time.StartHour=9
        location_day_time.StartMinute='Zero'
        location_day_time.EndHour=12
        location_day_time.EndMinute='Zero'
        location_day_time_ranges.DayTime.append(location_day_time)
        location_scheduling.DayTimeRanges=location_day_time_ranges
        location_scheduling_end_date=campaign_service.factory.create('Date')
        location_scheduling_end_date.Day=31
        location_scheduling_end_date.Month=12
        location_scheduling_end_date.Year=strftime("%Y", gmtime())
        location_scheduling.EndDate=location_scheduling_end_date
        location_scheduling.StartDate=None
        location_ad_extension.Scheduling=location_scheduling
        ad_extensions.AdExtension.append(location_ad_extension)

        price_ad_extension=set_elements_to_none(campaign_service.factory.create('PriceAdExtension'))
        price_ad_extension.Language="English"
        price_ad_extension.PriceExtensionType='Events'
        table_rows=campaign_service.factory.create('ArrayOfPriceTableRow')
        table_row_a=campaign_service.factory.create('PriceTableRow')
        table_row_a.CurrencyCode="USD"
        table_row_a.Description="Come to the event"
        table_row_a.FinalUrls=final_urls
        table_row_a.Header="New Event"
        table_row_a.Price=9.99
        table_row_a.PriceQualifier='From'
        table_row_a.PriceUnit='PerDay'
        table_rows.PriceTableRow.append(table_row_a)
        table_row_b=campaign_service.factory.create('PriceTableRow')
        table_row_b.CurrencyCode="USD"
        table_row_b.Description="Come to the next event"
        table_row_b.FinalUrls=final_urls
        table_row_b.Header="Next Event"
        table_row_b.Price=9.99
        table_row_b.PriceQualifier='From'
        table_row_b.PriceUnit='PerDay'
        table_rows.PriceTableRow.append(table_row_b)
        table_row_c=campaign_service.factory.create('PriceTableRow')
        table_row_c.CurrencyCode="USD"
        table_row_c.Description="Come to the final event"
        table_row_c.FinalUrls=final_urls
        table_row_c.Header="Final Event"
        table_row_c.Price=9.99
        table_row_c.PriceQualifier='From'
        table_row_c.PriceUnit='PerDay'
        table_rows.PriceTableRow.append(table_row_c)
        price_ad_extension.TableRows=table_rows
        ad_extensions.AdExtension.append(price_ad_extension)

        review_ad_extension=set_elements_to_none(campaign_service.factory.create('ReviewAdExtension'))
        review_ad_extension.IsExact=True
        review_ad_extension.Source="Review Source Name"
        review_ad_extension.Text="Review Text"
        # The Url of the third-party review. This is not your business Url.
        review_ad_extension.Url="https://review.contoso.com" 
        ad_extensions.AdExtension.append(review_ad_extension)

        sitelink_ad_extension=set_elements_to_none(campaign_service.factory.create('SitelinkAdExtension'))
        sitelink_ad_extension.Description1="Simple & Transparent."
        sitelink_ad_extension.Description2="No Upfront Cost."
        sitelink_ad_extension.DisplayText = "Women's Shoe Sale"
        sitelink_ad_extension.FinalUrls=final_urls
        ad_extensions.AdExtension.append(sitelink_ad_extension)

        structured_snippet_ad_extension=set_elements_to_none(campaign_service.factory.create('StructuredSnippetAdExtension'))
        structured_snippet_ad_extension.Header = "Brands"
        values=campaign_service.factory.create('ns3:ArrayOfstring')
        values.string.append('Windows')
        values.string.append('Xbox')
        values.string.append('Skype')
        structured_snippet_ad_extension.Values=values
        ad_extensions.AdExtension.append(structured_snippet_ad_extension)
        
        # Add all extensions to the account's ad extension library

        output_status_message("-----\nAddAdExtensions:")
        add_ad_extensions_response=campaign_service.AddAdExtensions(
            AccountId=authorization_data.account_id,
            AdExtensions=ad_extensions
        )
        output_status_message("AdExtensionIdentities:")
        ad_extension_identities={
            'AdExtensionIdentity': add_ad_extensions_response.AdExtensionIdentities['AdExtensionIdentity'] 
                if add_ad_extensions_response.AdExtensionIdentities['AdExtensionIdentity']
                else None
        }
        output_array_of_adextensionidentity(ad_extension_identities)
        output_status_message("NestedPartialErrors:")
        output_array_of_batcherrorcollection(add_ad_extensions_response.NestedPartialErrors) 

        # DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
        # operations each require a list of type AdExtensionIdToEntityIdAssociation.
        ad_extension_id_to_entity_id_associations=campaign_service.factory.create('ArrayOfAdExtensionIdToEntityIdAssociation')

        # GetAdExtensionsByIds requires a list of type long.
        ad_extension_ids=[]

        # Loop through the list of extension IDs and build any required data structures
        # for subsequent operations. 

        for ad_extension_identity in ad_extension_identities['AdExtensionIdentity']:
            ad_extension_id_to_entity_id_association=campaign_service.factory.create('AdExtensionIdToEntityIdAssociation')
            ad_extension_id_to_entity_id_association.AdExtensionId=ad_extension_identity.Id
            ad_extension_id_to_entity_id_association.EntityId=campaign_ids['long'][0]
            ad_extension_id_to_entity_id_associations.AdExtensionIdToEntityIdAssociation.append(ad_extension_id_to_entity_id_association)

            ad_extension_ids.append(ad_extension_identity.Id)

        # Associate the ad extensions with the campaign. 

        output_status_message("-----\nSetAdExtensionsAssociations:")
        set_ad_extensions_associations_response=campaign_service.SetAdExtensionsAssociations(
            AccountId=authorization_data.account_id,
            AdExtensionIdToEntityIdAssociations=ad_extension_id_to_entity_id_associations,
            AssociationType='Campaign'
        )
        output_status_message("PartialErrors:")
        if hasattr(set_ad_extensions_associations_response, 'PartialErrors'):
            output_array_of_batcherror(set_ad_extensions_associations_response.PartialErrors)

        # Get editorial rejection reasons for the respective ad extension and entity associations.

        output_status_message("-----\nGetAdExtensionsEditorialReasons:")
        get_ad_extensions_editorial_reasons_response=campaign_service.GetAdExtensionsEditorialReasons(
            AccountId=authorization_data.account_id,
            AdExtensionIdToEntityIdAssociations=ad_extension_id_to_entity_id_associations,
            AssociationType='Campaign'
        )
        ad_extension_editorial_reason_collection={
            'AdExtensionEditorialReasonCollection': get_ad_extensions_editorial_reasons_response.EditorialReasons['AdExtensionEditorialReasonCollection'] 
                if get_ad_extensions_editorial_reasons_response.EditorialReasons['AdExtensionEditorialReasonCollection']
                else None
        }
        output_status_message("EditorialReasons:")
        output_array_of_adextensioneditorialreasoncollection(ad_extension_editorial_reason_collection)
        output_status_message("PartialErrors:")
        output_array_of_batcherror(get_ad_extensions_editorial_reasons_response.PartialErrors)

        # Get only the location extensions and remove scheduling.

        ad_extensions_type_filter = 'LocationAdExtension'

        return_additional_fields = 'DisplayText Images'

        # In this example partial errors will be returned for indices where the ad extensions 
        # are not location ad extensions.
        # This is an example, and ideally you would only send the required ad extension IDs.

        output_status_message("-----\nGetAdExtensionsByIds:")
        get_ad_extensions_by_ids_response=campaign_service.GetAdExtensionsByIds(
            AccountId=authorization_data.account_id,
            AdExtensionIds={'long': ad_extension_ids},
            AdExtensionType=ad_extensions_type_filter,
            ReturnAdditionalFields=return_additional_fields
        )
        ad_extensions={
            'AdExtension': get_ad_extensions_by_ids_response.AdExtensions['AdExtension'] 
                if get_ad_extensions_by_ids_response.AdExtensions['AdExtension']
                else None
        }
        output_status_message("AdExtensions:")
        output_array_of_adextension(ad_extensions)
        output_status_message("PartialErrors:")
        output_array_of_batcherror(get_ad_extensions_by_ids_response.PartialErrors)

        update_extensions=campaign_service.factory.create('ArrayOfAdExtension')
        update_extension_ids = []

        for extension in ad_extensions['AdExtension']:

            # GetAdExtensionsByIds will return a nil element if the request conditions were not met.
            if extension is not None and extension.Id is not None:
            
                # Remove read-only elements that would otherwise cause the update operation to fail.
                update_extension = set_read_only_ad_extension_elements_to_none(extension)

                # If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged. 
                # If you set this to any non-null Schedule object, you are effectively replacing existing scheduling 
                # for the ad extension. In this example, we will remove any existing scheduling by setting this element  
                # to an empty Schedule object.
                update_extension.Scheduling=campaign_service.factory.create('Schedule')
                update_extensions.AdExtension.append(update_extension)
                update_extension_ids.append(update_extension.Id)
        
        output_status_message("-----\nUpdateAdExtensions:")
        campaign_service.UpdateAdExtensions(
            AccountId=authorization_data.account_id,
            AdExtensions=update_extensions
        )
        output_status_message("Removed scheduling from the location ad extensions.")
        
        # Get only the location extension to output the result.

        output_status_message("-----\nGetAdExtensionsByIds:")
        get_ad_extensions_by_ids_response=campaign_service.GetAdExtensionsByIds(
            AccountId=authorization_data.account_id,
            AdExtensionIds={'long': update_extension_ids},
            AdExtensionType=ad_extensions_type_filter,
            ReturnAdditionalFields=return_additional_fields
        )
        ad_extensions={
            'AdExtension': get_ad_extensions_by_ids_response.AdExtensions['AdExtension'] 
                if get_ad_extensions_by_ids_response.AdExtensions['AdExtension']
                else None
        }
        output_status_message("AdExtensions:")
        output_array_of_adextension(ad_extensions)
        output_status_message("PartialErrors:")
        output_array_of_batcherror(get_ad_extensions_by_ids_response.PartialErrors)

        # Delete the ad extension associations, ad extensions, and campaign, that were previously added.  
        # At this point the ad extensions are still available in the account's ad extensions library. 

        output_status_message("-----\nDeleteAdExtensionsAssociations:")
        campaign_service.DeleteAdExtensionsAssociations(
            AccountId=authorization_data.account_id,
            AdExtensionIdToEntityIdAssociations=ad_extension_id_to_entity_id_associations,
            AssociationType='Campaign'
        )
        output_status_message("Deleted ad extension associations.")

        # Delete the ad extensions from the account's ad extension library.

        output_status_message("-----\nDeleteAdExtensions:")
        campaign_service.DeleteAdExtensions(
            AccountId=authorization_data.account_id,
            AdExtensionIds={'long': ad_extension_ids},
        )
        output_status_message("Deleted ad extensions.")

        # Delete the account's media that was used for the image ad extension.
        
        output_status_message("-----\nDeleteMedia:")
        delete_media_response = campaign_service.DeleteMedia(
            authorization_data.account_id,
            media_ids)

        for id in media_ids['long']:
            output_status_message("Deleted Media Id {0}".format(id))

        output_status_message("-----\nDeleteCampaigns:")
        campaign_service.DeleteCampaigns(
            AccountId=authorization_data.account_id,
            CampaignIds=campaign_ids
        )
        output_status_message("Deleted Campaign Id {0}".format(campaign_ids['long'][0]))

    except WebFault as ex:
        output_webfault_errors(ex)
    except Exception as ex:
        output_status_message(ex)

def get_image_media(
    media_type, 
    image_file_name):
    image = campaign_service.factory.create('Image')
    image.Data = get_bmp_base64_string(image_file_name)
    image.MediaType = media_type
    image.Type = "Image"

    return image

def get_bmp_base64_string(image_file_name):
    image = open(image_file_name, 'rb') 
    image_bytes = image.read() 
    base64_string = base64.b64encode(image_bytes).decode("utf-8")
    return base64_string

# Main execution
if __name__ == '__main__':

    print("Loading the web service client proxies...")
    
    authorization_data=AuthorizationData(
        account_id=None,
        customer_id=None,
        developer_token=DEVELOPER_TOKEN,
        authentication=None,
    )

    campaign_service=ServiceClient(
        service='CampaignManagementService', 
        version=13,
        authorization_data=authorization_data, 
        environment=ENVIRONMENT,
    )

    customer_service=ServiceClient(
        service='CustomerManagementService', 
        version=13,
        authorization_data=authorization_data, 
        environment=ENVIRONMENT,
    )
        
    authenticate(authorization_data)
        
    main(authorization_data)

See Also

Get Started with the Bing Ads API