Edit

Offline Conversions Code Example

This example demonstrates how to send Microsoft Advertising your offline conversions using the Campaign Management service.

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.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 send Microsoft Advertising your offline conversions using the Campaign Management service.
    /// </summary>
    public class OfflineConversions : ExampleBase
    {
        public static ServiceClient<ICampaignManagementService> Service;

        public override string Description
        {
            get { return "Offline Conversions | 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);

                // A conversion goal cannot be deleted, so even if this is a test
                // please choose an appropriate name accordingly. 
                var offlineConversionGoalName = "My Offline Conversion Goal";

                var conversionGoals = new ConversionGoal[]
                {
                    new OfflineConversionGoal
                    {
                        GoalCategory = ConversionGoalCategory.Purchase,
                        // Determines how long after a click that you want to count offline conversions. 
                        ConversionWindowInMinutes = 43200,

                        // If the count type is 'Unique' then only the first offline conversion will be counted.
                        // By setting the count type to 'All', then all offline conversions for the same
                        // MicrosoftClickId with different conversion times will be added cumulatively. 
                        CountType = ConversionGoalCountType.All,

                        Name = offlineConversionGoalName,

                        // The default conversion currency code and value. Each offline conversion can override it.
                        Revenue = new ConversionGoalRevenue
                        {
                            CurrencyCode = null,
                            Type = ConversionGoalRevenueType.FixedValue,
                            Value = 5.00m,
                        },
                        Scope = EntityScope.Account,
                        Status = ConversionGoalStatus.Active,
                        TagId = null
                    },
                };

                OutputStatusMessage("-----\nAddConversionGoals:");
                var addConversionGoalsResponse = await CampaignManagementExampleHelper.AddConversionGoalsAsync(
                    conversionGoals: conversionGoals);
                var conversionGoalIds = addConversionGoalsResponse.ConversionGoalIds.ToArray();
                BatchError[] conversionGoalErrors = addConversionGoalsResponse.PartialErrors.ToArray();
                OutputStatusMessage("ConversionGoalIds:");
                CampaignManagementExampleHelper.OutputArrayOfLong(conversionGoalIds);
                OutputStatusMessage("PartialErrors:");
                CampaignManagementExampleHelper.OutputArrayOfBatchError(conversionGoalErrors);
                
                List<long> goalIds = GetNonNullableIds(conversionGoalIds);
                
                var conversionGoalTypes = ConversionGoalType.OfflineConversion;

                OutputStatusMessage("-----\nGetConversionGoalsByIds:");
                var getConversionGoalsResponse = (await CampaignManagementExampleHelper.GetConversionGoalsByIdsAsync(
                        conversionGoalIds: goalIds,
                        conversionGoalTypes: conversionGoalTypes,
                        returnAdditionalFields: ConversionGoalAdditionalField.ViewThroughConversionWindowInMinutes));
                var getConversionGoals = getConversionGoalsResponse.ConversionGoals;
                conversionGoalErrors = getConversionGoalsResponse.PartialErrors.ToArray();
                OutputStatusMessage("ConversionGoals:");
                CampaignManagementExampleHelper.OutputArrayOfConversionGoal(getConversionGoals);
                OutputStatusMessage("PartialErrors:");
                CampaignManagementExampleHelper.OutputArrayOfBatchError(conversionGoalErrors);

                // Every time you create a new OfflineConversionGoal via either the Microsoft Advertising web application or Campaign Management API, 
                // the MSCLKIDAutoTaggingEnabled value of the corresponding AccountProperty is set to 'true' automatically.
                // We can confirm the setting now.

                var accountPropertyNames = new List<AccountPropertyName>();
                accountPropertyNames.Add(AccountPropertyName.MSCLKIDAutoTaggingEnabled);

                OutputStatusMessage("-----\nGetAccountProperties:");
                var getAccountPropertiesResponse = await CampaignManagementExampleHelper.GetAccountPropertiesAsync(
                    accountPropertyNames: accountPropertyNames);
                var accountProperties = getAccountPropertiesResponse.AccountProperties;
                BatchError[] accountPropertiesErrors = getAccountPropertiesResponse.PartialErrors.ToArray();
                OutputStatusMessage("AccountProperties:");
                CampaignManagementExampleHelper.OutputArrayOfAccountProperty(accountProperties);
                OutputStatusMessage("PartialErrors:");
                CampaignManagementExampleHelper.OutputArrayOfBatchError(accountPropertiesErrors);
                
                var offlineConversions = new[]
                {
                    new OfflineConversion
                    {
                        // If you do not specify an offline conversion currency code, 
                        // then the 'CurrencyCode' element of the goal's 'ConversionGoalRevenue' is used.
                        ConversionCurrencyCode = "USD",

                        // The conversion name must match the 'Name' of the 'OfflineConversionGoal'.
                        // If it does not match you won't observe any error, although the offline
                        // conversion will not be counted.
                        ConversionName = offlineConversionGoalName,

                        // The date and time must be in UTC, should align to the date and time of the 
                        // recorded click (MicrosoftClickId), and cannot be in the future.
                        ConversionTime = DateTime.UtcNow,

                        // If you do not specify an offline conversion value, 
                        // then the 'Value' element of the goal's 'ConversionGoalRevenue' is used.
                        ConversionValue = 10,

                        MicrosoftClickId = "f894f652ea334e739002f7167ab8f8e3"
                    }
                };

                // After the OfflineConversionGoal is set up, wait two hours before submitting the offline conversions. 
                // This example would not succeed in production because we created the goal very recently i.e., 
                // please see above call to AddConversionGoalsAsync. 

                OutputStatusMessage("-----\nApplyOfflineConversions:");
                var applyOfflineConversionsResponse = await CampaignManagementExampleHelper.ApplyOfflineConversionsAsync(
                    offlineConversions: offlineConversions);
                OutputStatusMessage("PartialErrors:");
                CampaignManagementExampleHelper.OutputArrayOfBatchError(applyOfflineConversionsResponse.PartialErrors);
            }
            // 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 (Exception ex)
            {
                OutputStatusMessage(ex.Message);
            }
        }

    }
}
package com.microsoft.bingads.examples.v13;

import java.util.ArrayList;

import java.util.Calendar;
import java.util.TimeZone;

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

public class OfflineConversions extends ExampleBase {

    public static void main(java.lang.String[] args) {
     
        try
        {
            authorizationData = getAuthorizationData();
             
            CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient<ICampaignManagementService>(
                        authorizationData, 
                        API_ENVIRONMENT,
                        ICampaignManagementService.class);

            // A conversion goal cannot be deleted, so even if this is a test
            // please choose an appropriate name accordingly. 
            java.lang.String offlineConversionGoalName = "My Offline Conversion Goal";
            
            ArrayOfConversionGoal conversionGoals = new ArrayOfConversionGoal();
            
            OfflineConversionGoal offlineConversionGoal = new OfflineConversionGoal();
            offlineConversionGoal.setGoalCategory(ConversionGoalCategory.PURCHASE);
            // Determines how long after a click that you want to count offline conversions. 
            offlineConversionGoal.setConversionWindowInMinutes(43200);
            // If the count type is 'Unique' then only the first offline conversion will be counted.
            // By setting the count type to 'All', then all offline conversions for the same
            // MicrosoftClickId with different conversion times will be added cumulatively. 
            offlineConversionGoal.setCountType(ConversionGoalCountType.ALL);
            offlineConversionGoal.setName(offlineConversionGoalName);
            // The default conversion currency code and value. Each offline conversion can override it.
            ConversionGoalRevenue offlineConversionGoalRevenue = new ConversionGoalRevenue();
            offlineConversionGoalRevenue.setType(ConversionGoalRevenueType.FIXED_VALUE);
            offlineConversionGoalRevenue.setValue(new java.math.BigDecimal(5.00));
            offlineConversionGoalRevenue.setCurrencyCode(null);
            offlineConversionGoal.setRevenue(offlineConversionGoalRevenue);
            offlineConversionGoal.setScope(EntityScope.ACCOUNT);
            offlineConversionGoal.setStatus(ConversionGoalStatus.ACTIVE);
            offlineConversionGoal.setTagId(null);
            conversionGoals.getConversionGoals().add(offlineConversionGoal);
            
            outputStatusMessage("-----\nAddConversionGoals:");
            AddConversionGoalsResponse addConversionGoalsResponse = CampaignManagementExampleHelper.addConversionGoals(
                    conversionGoals);
            ArrayOfNullableOflong conversionGoalIds = addConversionGoalsResponse.getConversionGoalIds();
            ArrayOfBatchError goalErrors = addConversionGoalsResponse.getPartialErrors();
            outputStatusMessage("ConversionGoalIds:");
            CampaignManagementExampleHelper.outputArrayOfNullableOflong(conversionGoalIds);
            outputStatusMessage("PartialErrors:");
            CampaignManagementExampleHelper.outputArrayOfBatchError(goalErrors);

            ArrayOflong goalIds = new ArrayOflong();
            for (java.lang.Long goalId : conversionGoalIds.getLongs())
            {
                if (goalId != null)
                {
                    goalIds.getLongs().add((long)goalId);
                }
            }
            
            ArrayList<ConversionGoalType> conversionGoalTypes = new ArrayList<ConversionGoalType>();
            conversionGoalTypes.add(ConversionGoalType.OFFLINE_CONVERSION);
            
            ArrayList<ConversionGoalAdditionalField> returnAdditionalFields = new ArrayList<ConversionGoalAdditionalField>();
            returnAdditionalFields.add(ConversionGoalAdditionalField.VIEW_THROUGH_CONVERSION_WINDOW_IN_MINUTES);
            
            outputStatusMessage("-----\nGetConversionGoalsByIds:");
            GetConversionGoalsByIdsResponse getConversionGoalsByIdsResponse = CampaignManagementExampleHelper.getConversionGoalsByIds(
                    goalIds, 
                    conversionGoalTypes,
                    returnAdditionalFields);
            ArrayOfConversionGoal getConversionGoals = getConversionGoalsByIdsResponse.getConversionGoals();
            goalErrors = getConversionGoalsByIdsResponse.getPartialErrors();
            outputStatusMessage("ConversionGoals:");
            CampaignManagementExampleHelper.outputArrayOfConversionGoal(getConversionGoals);
            outputStatusMessage("PartialErrors:");
            CampaignManagementExampleHelper.outputArrayOfBatchError(goalErrors);
            
            // Every time you create a new OfflineConversionGoal via either the Bing Ads web application or Campaign Management API, 
            // the MSCLKIDAutoTaggingEnabled value of the corresponding AccountProperty is set to 'true' automatically.
            // We can confirm the setting now.
            
            ArrayOfAccountPropertyName accountPropertyNames = new ArrayOfAccountPropertyName();
            accountPropertyNames.getAccountPropertyNames().add(AccountPropertyName.MSCLKID_AUTO_TAGGING_ENABLED);

            outputStatusMessage("-----\nGetAccountProperties:");
            GetAccountPropertiesResponse getAccountPropertiesResponse = CampaignManagementExampleHelper.getAccountProperties(
                    accountPropertyNames);
            outputStatusMessage("AccountProperties:");
            CampaignManagementExampleHelper.outputArrayOfAccountProperty(getAccountPropertiesResponse.getAccountProperties());
            outputStatusMessage("PartialErrors:");
            CampaignManagementExampleHelper.outputArrayOfBatchError(getAccountPropertiesResponse.getPartialErrors());
            
            ArrayOfOfflineConversion offlineConversions = new ArrayOfOfflineConversion();
            OfflineConversion offlineConversion = new OfflineConversion();             
            // If you do not specify an offline conversion currency code, 
            // then the 'CurrencyCode' element of the goal's 'ConversionGoalRevenue' is used.
            offlineConversion.setConversionCurrencyCode("USD");
            // The conversion name must match the 'Name' of the 'OfflineConversionGoal'.
            // If it does not match you won't observe any error, although the offline
            // conversion will not be counted.
            offlineConversion.setConversionName(offlineConversionGoalName);
            // The date and time must be in UTC, should align to the date and time of the 
            // recorded click (MicrosoftClickId), and cannot be in the future.
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
            calendar.set(2018, 4, 26, 0, 0, 0);
            offlineConversion.setConversionTime(calendar);
            // If you do not specify an offline conversion value, 
            // then the 'Value' element of the goal's 'ConversionGoalRevenue' is used.
            offlineConversion.setConversionValue(10D);
            offlineConversion.setMicrosoftClickId("f894f652ea334e739002f7167ab8f8e3");
            
            offlineConversions.getOfflineConversions().add(offlineConversion);

            // After the OfflineConversionGoal is set up, wait two hours before sending Bing Ads the offline conversions. 
            // This example would not succeed in production because we created the goal very recently i.e., 
            // please see above call to AddConversionGoalsAsync. 

            outputStatusMessage("-----\nApplyOfflineConversions:");
            ApplyOfflineConversionsResponse applyOfflineConversionsResponse = CampaignManagementExampleHelper.applyOfflineConversions(
                    offlineConversions);
            outputStatusMessage("PartialErrors:");
            CampaignManagementExampleHelper.outputArrayOfBatchError(applyOfflineConversionsResponse.getPartialErrors());
        } 
        catch (Exception ex) {
            String faultXml = ExampleExceptionHelper.getBingAdsExceptionFaultXml(ex, System.out);
            outputStatusMessage(faultXml);
            String message = ExampleExceptionHelper.handleBingAdsSDKException(ex, System.out);
            outputStatusMessage(message);
        }
    }
 }
<?php

namespace Microsoft\MsAds\Rest\Test\Services;

use DateTime;
use Exception;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\AccountPropertyName;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\AddConversionGoalsRequest;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\ApplyOfflineConversionsRequest;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\ConversionGoalCategory;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\ConversionGoalCountType;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\ConversionGoalRevenue;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\ConversionGoalRevenueType;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\ConversionGoalStatus;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\ConversionGoalType;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\EntityScope;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\GetAccountPropertiesRequest;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\GetConversionGoalsByIdsRequest;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\OfflineConversion;
use Microsoft\MsAds\Rest\Model\CampaignManagementService\OfflineConversionGoal;
use Microsoft\MsAds\Rest\Test\RestApiTestBase;

class OfflineConversionsTest extends RestApiTestBase
{
    protected static string $goalName;

    /**
     * @throws Exception
     */
    public function testAddOfflineConversionGoal()
    {
        print("-----\r\nAdding Offline Conversion Goal:\r\n");

        self::$goalName = "TEST_OFFLINE_CONVERSION_GOAL".self::generateRandomAlphaNumeric();
        // A conversion goal cannot be deleted, so even if this is a test
        // please choose an appropriate name accordingly.
        $offlineConversionGoal = (new OfflineConversionGoal())
            // Determines how long after a click that you want to count offline conversions.
            ->setConversionWindowInMinutes(43200)
            // If the count type is 'Unique' then only the first offline conversion will be counted.
            // By setting the count type to 'All', then all offline conversions for the same
            // MicrosoftClickId with different conversion times will be added cumulatively.
            ->setCountType(ConversionGoalCountType::ALL)
            ->setName(self::$goalName)
            // The default conversion currency code and value. Each offline conversion can override it.
            ->setRevenue(
                (new ConversionGoalRevenue())
                    ->setType(ConversionGoalRevenueType::FIXED_VALUE)
                    ->setValue(5.00)
            )
            ->setScope(EntityScope::ACCOUNT)
            ->setStatus(ConversionGoalStatus::ACTIVE)
            ->setGoalCategory(ConversionGoalCategory::PURCHASE);

        $request = new AddConversionGoalsRequest([
            'ConversionGoals' => [$offlineConversionGoal]
        ]);

        $response = self::$campaignManagementServiceApi->addConversionGoals($request);

        $conversionGoalIds = $response->getConversionGoalIds();
        print("Conversion Goal IDs:\r\n");
        print_r($conversionGoalIds);
        print("Partial Errors:\r\n");
        print_r($response->getPartialErrors());
        self::assertNotEmpty($conversionGoalIds);
        self::assertEmpty($response->getPartialErrors());

        return $conversionGoalIds;
    }

    /**
     * @depends testAddOfflineConversionGoal
     * @throws Exception
     */
    public function testGetOfflineConversionGoal(array $conversionGoalIds)
    {
        print("-----\r\nGetting Offline Conversion Goal:\r\n");

        $request = new GetConversionGoalsByIdsRequest([
            'ConversionGoalIds' => $conversionGoalIds,
            'ConversionGoalTypes' => [ConversionGoalType::OFFLINE_CONVERSION]
        ]);

        $response = self::$campaignManagementServiceApi->getConversionGoalsByIds($request);

        $conversionGoals = $response->getConversionGoals();
        print("Conversion Goals:\r\n");
        print_r($conversionGoals);
        print("Partial Errors:\r\n");
        print_r($response->getPartialErrors());

        self::assertNotEmpty($conversionGoals);
        self::assertEmpty($response->getPartialErrors());
    }

    /**
     * @depends testAddOfflineConversionGoal
     * @throws Exception
     */
    public function testGetAccountProperties()
    {
        // Every time you create a new OfflineConversionGoal via either the Bing Ads web application or Campaign Management API,
        // the MSCLKIDAutoTaggingEnabled value of the corresponding AccountProperty is set to 'true' automatically.
        // We can confirm the setting now.
        print("-----\r\nGetting Account Properties:\r\n");

        $request = new GetAccountPropertiesRequest([
            'AccountPropertyNames' => [AccountPropertyName::MSCLKID_AUTO_TAGGING_ENABLED]
        ]);

        $response = self::$campaignManagementServiceApi->getAccountProperties($request);

        $accountProperties = $response->getAccountProperties();
        print("Account Properties:\r\n");
        print_r($accountProperties);
        print("Partial Errors:\r\n");
        print_r($response->getPartialErrors());

        self::assertNotEmpty($accountProperties);
        self::assertEmpty($response->getPartialErrors());
    }

    /**
     * @depends testAddOfflineConversionGoal
     * @throws Exception
     */
    public function testApplyOfflineConversions()
    {
        print("-----\r\nApplying Offline Conversions:\r\n");

        $offlineConversion = (new OfflineConversion())
            // If you do not specify an offline conversion currency code,
            // then the 'CurrencyCode' element of the goal's 'ConversionGoalRevenue' is used.
            ->setConversionCurrencyCode("USD")
            // The conversion name must match the 'Name' of the 'OfflineConversionGoal'.
            // If it does not match you won't observe any error, although the offline
            // conversion will not be counted.
            ->setConversionName(self::$goalName)
            // The date and time must be in UTC, should align to the date and time of the
            // recorded click (MicrosoftClickId), and cannot be in the future.
            ->setConversionTime(new DateTime())
            // If you do not specify an offline conversion value,
            // then the 'Value' element of the goal's 'ConversionGoalRevenue' is used.
            ->setConversionValue(10)
            ->setMicrosoftClickId("f894f652ea334e739002f7167ab8f8e3");


        // After the OfflineConversionGoal is set up, wait two hours before sending Bing Ads the offline conversions.
        // This example would not succeed in production because we created the goal very recently i.e.,
        // please see above call to AddConversionGoalsAsync.
        $request = new ApplyOfflineConversionsRequest([
            'OfflineConversions' => [$offlineConversion]
        ]);

        $response = self::$campaignManagementServiceApi->applyOfflineConversions($request);
        print("Partial Errors:\r\n");
        print_r($response->getPartialErrors());
        self::assertNotEmpty($response->getPartialErrors());
    }
}
import uuid
from datetime import timezone
from auth_helper import *
from openapi_client.models.campaign import *

def main(authorization_data):
    try:
        # Create an offline conversion goal
        print("Creating offline conversion goal...")
        
        goal_name = f"TEST_OFFLINE_CONVERSION_GOAL{str(uuid.uuid4())[:8]}"
        print(f"Goal Name: {goal_name}")
        print("Note: A conversion goal cannot be deleted, so please choose an appropriate name.")
        
        offline_conversion_goal = OfflineConversionGoal(
            # Determines how long after a click that you want to count offline conversions.
            # 43200 minutes = 30 days
            conversion_window_in_minutes=43200,
            # If the count type is 'Unique' then only the first offline conversion will be counted.
            # By setting the count type to 'All', then all offline conversions for the same
            # MicrosoftClickId with different conversion times will be added cumulatively.
            count_type=ConversionGoalCountType.ALL,
            name=goal_name,
            # The default conversion currency code and value. Each offline conversion can override it.
            revenue=ConversionGoalRevenue(
                type=ConversionGoalRevenueType.FIXEDVALUE,
                value=5.00
            ),
            scope=EntityScope.ACCOUNT,
            status=ConversionGoalStatus.ACTIVE,
            goal_category=ConversionGoalCategory.PURCHASE
        )
        
        add_conversion_goals_request = AddConversionGoalsRequest(
            conversion_goals=[offline_conversion_goal]
        )
        
        add_conversion_goals_response = campaign_service.add_conversion_goals(
            add_conversion_goals_request=add_conversion_goals_request
        )
        
        conversion_goal_ids = add_conversion_goals_response.ConversionGoalIds
        print(f"Created Conversion Goal IDs: {conversion_goal_ids}")
        
        if add_conversion_goals_response.PartialErrors:
            print(f"Partial Errors: {add_conversion_goals_response.PartialErrors}")
        else:
            print("Offline conversion goal created successfully")
        
        # Get the offline conversion goal
        print("\nGetting offline conversion goal...")
        
        get_conversion_goals_request = GetConversionGoalsByIdsRequest(
            conversion_goal_ids=conversion_goal_ids,
            conversion_goal_types=ConversionGoalType.OFFLINECONVERSION
        )
        
        get_conversion_goals_response = campaign_service.get_conversion_goals_by_ids(
            get_conversion_goals_by_ids_request=get_conversion_goals_request
        )
        
        conversion_goals = get_conversion_goals_response.ConversionGoals
        print(f"Retrieved {len(conversion_goals)} conversion goals")
        
        for goal in conversion_goals:
            if goal:
                print(f"  Goal ID: {goal.Id}")
                print(f"  Goal Name: {goal.Name}")
                print(f"  Goal Status: {goal.Status}")
                print(f"  Conversion Window: {goal.ConversionWindowInMinutes} minutes")
        
        if get_conversion_goals_response.PartialErrors:
            print(f"Partial Errors: {get_conversion_goals_response.PartialErrors}")
        
        # Get account properties
        # Every time you create a new OfflineConversionGoal via either the Bing Ads web application 
        # or Campaign Management API, the MSCLKIDAutoTaggingEnabled value of the corresponding 
        # AccountProperty is set to 'true' automatically. We can confirm the setting now.
        print("\nGetting account properties...")
        
        get_account_properties_request = GetAccountPropertiesRequest(
            account_property_names=[AccountPropertyName.MSCLKIDAUTOTAGGINGENABLED]
        )
        
        get_account_properties_response = campaign_service.get_account_properties(
            get_account_properties_request=get_account_properties_request
        )
        
        account_properties = get_account_properties_response.AccountProperties
        print(f"Account Properties:")
        
        for prop in account_properties:
            if prop:
                print(f"  Name: {prop.Name}")
                print(f"  Value: {prop.Value}")
        
        if get_account_properties_response.PartialErrors:
            print(f"Partial Errors: {get_account_properties_response.PartialErrors}")
        
        # Apply offline conversions
        print("\nApplying offline conversions...")
        print("Note: This example demonstrates the API call, but it will likely fail because")
        print("you must wait at least 2 hours after creating the goal before applying conversions.")
        
        # Get current UTC time
        current_utc_time = datetime.now(timezone.utc)
        
        offline_conversion = OfflineConversion(
            # If you do not specify an offline conversion currency code,
            # then the 'CurrencyCode' element of the goal's 'ConversionGoalRevenue' is used.
            conversion_currency_code="USD",
            # The conversion name must match the 'Name' of the 'OfflineConversionGoal'.
            # If it does not match you won't observe any error, although the offline
            # conversion will not be counted.
            conversion_name=goal_name,
            # The date and time must be in UTC, should align to the date and time of the
            # recorded click (MicrosoftClickId), and cannot be in the future.
            conversion_time=current_utc_time,
            # If you do not specify an offline conversion value,
            # then the 'Value' element of the goal's 'ConversionGoalRevenue' is used.
            conversion_value=10.0,
            # Use a real Microsoft Click ID from your account
            microsoft_click_id="f894f652ea334e739002f7167ab8f8e3"
        )
        
        apply_offline_conversions_request = ApplyOfflineConversionsRequest(
            offline_conversions=[offline_conversion]
        )
        
        apply_offline_conversions_response = campaign_service.apply_offline_conversions(
            apply_offline_conversions_request=apply_offline_conversions_request
        )
        
        if apply_offline_conversions_response.PartialErrors:
            print(f"Partial Errors (expected): {apply_offline_conversions_response.PartialErrors}")
            print("This is expected because we just created the goal and need to wait 2 hours.")
        else:
            print("Offline conversions applied successfully")
        
        print("\n" + "="*80)
        print("Test completed successfully!")
        print(f"Note: The conversion goal '{goal_name}' (ID: {conversion_goal_ids[0]}) ")
        print("has been created and CANNOT be deleted. It will remain in your account.")
        print("="*80)
        
    except Exception as ex:
        print(f"Error occurred: {str(ex)}")
        import traceback
        traceback.print_exc()

if __name__ == '__main__':
    print("Loading the web service client...")
    print("\n" + "="*80)
    print("WARNING: This test creates an offline conversion goal that CANNOT be deleted!")
    print("Please ensure you want to proceed before running this test.")
    print("="*80 + "\n")
    
    authorization_data = AuthorizationData(
        account_id=None,
        customer_id=None,
        developer_token=DEVELOPER_TOKEN,
        authentication=None,
    )
    
    authenticate(authorization_data)
    
    campaign_service = ServiceClient(
        service='CampaignManagementService',
        version=13,
        authorization_data=authorization_data,
        environment=ENVIRONMENT,
    )
    
    main(authorization_data)

See Also

Get Started with the Bing Ads API