📊 Bing Ads - Campaign Management
📖 Overview
The Campaign Management Service provides SOAP-based operations for retrieving campaign information and generating performance reports. Supports Search, Shopping, and DynamicSearchAds campaign types with asynchronous report generation.
Source Files:
- Controller:
external/Integrations/BingAds/Controllers/Campaigns/CampaignController.js - Model:
external/Integrations/BingAds/Models/Campaigns/CampaignsModel.js - Routes:
external/Integrations/BingAds/Routes/campaigns.js
External APIs:
- Campaign Management Service:
https://campaign.api.bingads.microsoft.com/Api/Advertiser/CampaignManagement/v13/CampaignManagementService.svc - Reporting Service:
https://reporting.api.bingads.microsoft.com/Api/Advertiser/Reporting/v13/ReportingService.svc
🔄 Data Flow
sequenceDiagram
participant Client as API Client
participant DC as DashClicks API
participant MW as Token Middleware
participant Model as CampaignsModel
participant SOAP as Bing SOAP API
Client->>DC: GET /campaigns
DC->>MW: Check token expiration
MW->>DC: Valid access token
DC->>Model: getCampaignsByAccountId()
Model->>Model: Build SOAP XML request
Model->>SOAP: POST to Campaign Management Service
SOAP-->>Model: SOAP XML response with campaigns
Model->>Model: Parse XML to JSON
Model-->>DC: Return campaign array
DC-->>Client: JSON response
🔧 Campaign Functions
List Campaigns by Account
GET /campaigns
Purpose: Retrieve all campaigns for a specific account, optionally filtered by campaign type
Controller: CampaignController.index()
Model Method: CampaignsModel.getCampaignsByAccountId(customerAccountID, customerID, campaignType)
SOAP Operation: GetCampaignsByAccountId
Request:
GET /v1/integrations/bing/ads/campaigns?customerAccountID=111222333&customerID=123456&campaignType=Search
Authorization: Bearer {jwt_token}
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
customerAccountID | Integer | ✅ | Account ID from /accessible-user-accounts |
customerID | Integer | ⚪ | Customer ID (optional but recommended) |
campaignType | String | ⚪ | Filter: Search, Shopping, DynamicSearchAds |
Validation:
- If
customerAccountIDmissing, returns error: "Please provide customerAccountID optionaly you can provide customerID and campaignType as well as"
SOAP Request XML:
<s:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header xmlns="https://bingads.microsoft.com/CampaignManagement/v13">
<Action mustUnderstand="1">GetCampaignsByAccountId</Action>
<AuthenticationToken i:nil="false">{ACCESS_TOKEN}</AuthenticationToken>
<CustomerAccountId i:nil="false">111222333</CustomerAccountId>
<CustomerId i:nil="true">123456</CustomerId>
<DeveloperToken i:nil="false">{DEVELOPER_TOKEN}</DeveloperToken>
</s:Header>
<s:Body>
<GetCampaignsByAccountIdRequest xmlns="https://bingads.microsoft.com/CampaignManagement/v13">
<AccountId>111222333</AccountId>
<!-- If campaignType not provided, defaults to all 3 types -->
<CampaignType>Search</CampaignType>
<CampaignType>Shopping</CampaignType>
<CampaignType>DynamicSearchAds</CampaignType>
</GetCampaignsByAccountIdRequest>
</s:Body>
</s:Envelope>
Campaign Type Filter:
- If
campaignTypeprovided:<CampaignType>{campaignType}</CampaignType> - If not provided: All three types included (Search, Shopping, DynamicSearchAds)
SOAP Response Structure:
<s:Envelope>
<s:Body>
<GetCampaignsByAccountIdResponse>
<Campaigns>
<Campaign i:type="SearchCampaign">
<CampaignId>987654321</CampaignId>
<Name>Search Campaign Name</Name>
<Status>Active</Status>
<TimeZone>EasternTimeUSCanada</TimeZone>
<BudgetType>DailyBudgetStandard</BudgetType>
<DailyBudget>50.00</DailyBudget>
<Languages>
<string>English</string>
</Languages>
<Settings>
<Setting i:type="TargetSetting">...</Setting>
</Settings>
<BiddingScheme i:type="ManualCpcBiddingScheme">...</BiddingScheme>
</Campaign>
<Campaign i:type="ShoppingCampaign">
<CampaignId>555666777</CampaignId>
<Name>Shopping Campaign</Name>
<Status>Active</Status>
<Settings>
<Setting i:type="ShoppingSetting">
<Priority>0</Priority>
<StoreId>12345</StoreId>
</Setting>
</Settings>
</Campaign>
</Campaigns>
</GetCampaignsByAccountIdResponse>
</s:Body>
</s:Envelope>
JSON Response (Parsed from SOAP XML):
{
"success": true,
"message": "SUCCESS",
"data": [
{
"CampaignId": 987654321,
"Name": "Search Campaign Name",
"Status": "Active",
"TimeZone": "EasternTimeUSCanada",
"BudgetType": "DailyBudgetStandard",
"DailyBudget": 50.00,
"Languages": {
"string": ["English"]
},
"Settings": { ... },
"BiddingScheme": { ... }
},
{
"CampaignId": 555666777,
"Name": "Shopping Campaign",
"Status": "Active",
"Settings": {
"Setting": {
"Priority": 0,
"StoreId": 12345
}
}
}
]
}
Key Response Fields:
| Field | Type | Description |
|---|---|---|
CampaignId | Integer | Unique campaign identifier |
Name | String | Campaign name |
Status | String | Active, Paused, Deleted, Suspended |
TimeZone | String | Campaign timezone |
BudgetType | String | DailyBudgetStandard, DailyBudgetAccelerated |
DailyBudget | Decimal | Daily budget amount |
Languages | Array | Target languages |
BiddingScheme | Object | Bidding strategy details |
Settings | Object | Campaign-specific settings |
Side Effects:
- None (read-only operation)
Get Campaign by ID
GET /campaigns/{campaignID}
Purpose: Retrieve specific campaign(s) by ID(s)
Controller: CampaignController.show()
Model Method: CampaignsModel.getCampaignsByIds(customerAccountID, customerID, campaignID, campaignType)
SOAP Operation: GetCampaignsByIds
Request:
GET /v1/integrations/bing/ads/campaigns/987654321?customerAccountID=111222333&customerID=123456
Authorization: Bearer {jwt_token}
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
customerAccountID | Integer | ✅ | Account ID |
customerID | Integer | ✅ | Customer ID |
campaignID | String | ✅ | Campaign ID(s) comma-separated (max 100) |
campaignType | String | ⚪ | Filter by campaign type |
Validation:
- If any required param missing, returns error: "Please provide customerAccountID,customerID and campaignID."
Multiple Campaign IDs:
GET /campaigns/987654321,555666777,111222333?customerAccountID=...&customerID=...
SOAP Request XML:
<s:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header xmlns="https://bingads.microsoft.com/CampaignManagement/v13">
<Action mustUnderstand="1">GetCampaignsByIds</Action>
<AuthenticationToken i:nil="false">{ACCESS_TOKEN}</AuthenticationToken>
<CustomerAccountId i:nil="false">111222333</CustomerAccountId>
<CustomerId i:nil="false">123456</CustomerId>
<DeveloperToken i:nil="false">{DEVELOPER_TOKEN}</DeveloperToken>
</s:Header>
<s:Body>
<GetCampaignsByIdsRequest xmlns="https://bingads.microsoft.com/CampaignManagement/v13">
<AccountId>111222333</AccountId>
<CampaignIds i:nil="false" xmlns:a1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a1:long>987654321</a1:long>
<a1:long>555666777</a1:long>
</CampaignIds>
<CampaignType>Search</CampaignType>
<CampaignType>Shopping</CampaignType>
<CampaignType>DynamicSearchAds</CampaignType>
</GetCampaignsByIdsRequest>
</s:Body>
</s:Envelope>
Helper Method: generateCampaignIdsXml(campaignID)
- Splits comma-separated IDs
- Generates XML:
<a1:long>{id}</a1:long>for each ID
JSON Response:
{
"success": true,
"message": "SUCCESS",
"data": {
"CampaignId": 987654321,
"Name": "Specific Campaign",
"Status": "Active",
"DailyBudget": 75.00,
"TimeZone": "PacificTimeUSCanada",
"BiddingScheme": {
"@i:type": "ManualCpcBiddingScheme"
}
}
}
Note: If multiple IDs provided, response is array; if single ID, response is object.
Get Campaign Performance Metrics
GET /campaigns/metrics
Purpose: Generate asynchronous performance report for campaigns
Controller: CampaignController.metrics()
Model Method: CampaignsModel.reports(...)
SOAP Operation: SubmitGenerateReport
Service: Reporting Service v13 (different endpoint)
Request:
GET /v1/integrations/bing/ads/campaigns/metrics?customerAccountID=111222333&customerID=123456&aggregation=Daily&predefinedTime=Last7Days&timeZone=EasternTimeUSCanada
Authorization: Bearer {jwt_token}
Query Parameters:
| Parameter | Type | Required | Description | Default |
|---|---|---|---|---|
customerAccountID | Integer | ✅ | Account ID | - |
customerID | Integer | ⚪ | Customer ID | - |
campaignID | Integer | ⚪ | Specific campaign ID | All campaigns |
adGroupID | Integer | ⚪ | Specific ad group ID | All ad groups |
aggregation | String | ⚪ | Aggregation level | Monthly |
predefinedTime | String | ⚪ | Predefined time period | Last30Days |
timeZone | String | ⚪ | Report timezone | EasternTimeUSCanada |
fromDate | String | ⚪ | Custom start date (YYYY-MM-DD) | - |
endDate | String | ⚪ | Custom end date (YYYY-MM-DD) | - |
Aggregation Options:
Hourly- Hourly breakdownsDaily- Daily breakdownsWeekly- Weekly breakdownsMonthly- Monthly breakdowns (default)Yearly- Yearly breakdownsSummary- Total summary
Predefined Time Periods:
Today,YesterdayLastSevenDays,ThisWeek,LastWeekLastFourWeeks,ThisMonth,LastMonthLastThreeMonths,LastSixMonthsThisYear,LastYearLast30Days(default)
Custom Date Range:
# Uses fromDate and endDate instead of predefinedTime
GET /campaigns/metrics?customerAccountID=...&fromDate=2024-01-01&endDate=2024-01-31
SOAP Request XML:
<s:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header xmlns="https://bingads.microsoft.com/Reporting/v13">
<Action mustUnderstand="1">SubmitGenerateReport</Action>
<AuthenticationToken i:nil="false">{ACCESS_TOKEN}</AuthenticationToken>
<CustomerAccountId i:nil="false">111222333</CustomerAccountId>
<CustomerId i:nil="true">123456</CustomerId>
<DeveloperToken i:nil="false">{DEVELOPER_TOKEN}</DeveloperToken>
</s:Header>
<s:Body>
<SubmitGenerateReportRequest xmlns="https://bingads.microsoft.com/Reporting/v13">
<ReportRequest i:nil="false" i:type="CampaignPerformanceReportRequest">
<ExcludeColumnHeaders>false</ExcludeColumnHeaders>
<ExcludeReportFooter>false</ExcludeReportFooter>
<ExcludeReportHeader>false</ExcludeReportHeader>
<Format>Csv</Format>
<Language>English</Language>
<ReportName>CampaignPerformanceReportRequest</ReportName>
<ReturnOnlyCompleteData>false</ReturnOnlyCompleteData>
<Aggregation>Daily</Aggregation>
<Columns>
<CampaignPerformanceReportColumn>AccountName</CampaignPerformanceReportColumn>
<CampaignPerformanceReportColumn>CampaignId</CampaignPerformanceReportColumn>
<CampaignPerformanceReportColumn>CampaignName</CampaignPerformanceReportColumn>
<CampaignPerformanceReportColumn>Impressions</CampaignPerformanceReportColumn>
<CampaignPerformanceReportColumn>Clicks</CampaignPerformanceReportColumn>
<CampaignPerformanceReportColumn>Ctr</CampaignPerformanceReportColumn>
<CampaignPerformanceReportColumn>Spend</CampaignPerformanceReportColumn>
<!-- 60+ more columns available -->
</Columns>
<Filter i:nil="true"/>
<Scope>
<AccountIds xmlns:a1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a1:long>111222333</a1:long>
</AccountIds>
<!-- Optional: Specific campaign and ad group -->
<Campaigns i:nil="false">
<CampaignReportScope>
<AccountId>111222333</AccountId>
<CampaignId>987654321</CampaignId>
</CampaignReportScope>
</Campaigns>
</Scope>
<Time>
<PredefinedTime i:nil="false">Last7Days</PredefinedTime>
<ReportTimeZone>EasternTimeUSCanada</ReportTimeZone>
</Time>
</ReportRequest>
</SubmitGenerateReportRequest>
</s:Body>
</s:Envelope>
Available Report Columns (67 total):
| Column | Description |
|---|---|
AccountName, AccountNumber, AccountId | Account identifiers |
CampaignId, CampaignName, CampaignStatus | Campaign info |
Impressions, Clicks, Ctr | Core metrics |
Spend, AverageCpc, AveragePosition | Cost metrics |
Conversions, ConversionRate, CostPerConversion | Conversion metrics |
Revenue, ReturnOnAdSpend, RevenuePerConversion | Revenue metrics |
QualityScore, ExpectedCtr, AdRelevance, LandingPageExperience | Quality metrics |
AllConversions, AllRevenue, ViewThroughConversions | Advanced conversion tracking |
Goal, GoalType, TimePeriod | Reporting metadata |
Full columns list: See getCampaignColumns() method in source code (67 columns total)
Custom Date XML Generation:
// Helper: generateCustomDateXML(fromDate, endDate, predefinedTime)
// Option 1: Predefined Time
<PredefinedTime i:nil="false">Last30Days</PredefinedTime>
// Option 2: Custom Date Range
<CustomDateRangeEnd>
<Day>31</Day>
<Month>01</Month>
<Year>2024</Year>
</CustomDateRangeEnd>
<CustomDateRangeStart>
<Day>01</Day>
<Month>01</Month>
<Year>2024</Year>
</CustomDateRangeStart>
SOAP Response Structure:
<s:Envelope>
<s:Body>
<SubmitGenerateReportResponse>
<ReportRequestId>987654321012345</ReportRequestId>
</SubmitGenerateReportResponse>
</s:Body>
</s:Envelope>
JSON Response:
{
"success": true,
"message": "SUCCESS",
"data": {
"ReportRequestId": "987654321012345"
}
}
Important: This is an asynchronous operation:
- Returns
ReportRequestIdimmediately - Report generation happens in background
- Must poll
PollGenerateReportto check status - When
Success, download CSV viaReportRequestId
Report Download Flow:
- Submit report request → Get
ReportRequestId - Poll status with
ReportRequestId→ Check ifSuccess - Download CSV report using
ReportRequestId
📊 Campaign Types
| Type | Description | Use Case |
|---|---|---|
Search | Text ads on Bing search results | Keyword-based advertising |
Shopping | Product ads with images and prices | E-commerce product listings |
DynamicSearchAds | Auto-generated ads from website content | Large inventories, dynamic content |
🕒 Time Zones
Common Options:
EasternTimeUSCanada(default)PacificTimeUSCanadaCentralTimeUSCanadaMountainTimeUSCanadaGMTStandardTime(UTC)- See Microsoft Timezone Reference
🔄 Helper Methods
generateCampaignIdsXml(campaignID)
Converts comma-separated campaign IDs to SOAP XML format.
Input: "987654321,555666777"
Output:
<a1:long>987654321</a1:long>
<a1:long>555666777</a1:long>
generateAccountIdXml(accountID)
Converts comma-separated account IDs to SOAP XML format (for reporting scope).
generateCustomDateXML(fromDate, endDate, predefinedTime)
Generates date range XML based on input:
- If
predefinedTimeprovided: Uses predefined period - If
fromDateandendDateprovided: Custom date range XML - If neither: Defaults to
Last30Days
getCampaignColumns()
Returns array of 67 available report column names.
⚠️ Important Notes
- 📊 Async Reporting: Metrics endpoint returns request ID, not actual data
- 🕒 Report Polling: Must implement polling to check report completion
- 📁 CSV Format: Reports returned as CSV files (Format: Csv)
- 🔢 Max Campaign IDs: Can request up to 100 campaign IDs per call
- 🎯 Campaign Type Defaults: If not specified, includes Search, Shopping, DynamicSearchAds
- 📅 Date Format: Custom dates must be YYYY-MM-DD format
- 🔐 Two Tokens Required: OAuth access token + developer token
- 🌍 Timezone: Affects date range interpretation (use account timezone)
- 📊 67 Metrics Available: Full list in
getCampaignColumns() - 🔄 Token Auto-Refresh: Middleware ensures valid tokens
- 🏗️ SOAP Protocol: XML requests/responses (parsed to JSON)
- 📝 Report Download: Requires separate API call (not implemented in this endpoint)
🔗 Related Documentation
- Authentication: OAuth 2.0 with Auto-Refresh
- Accounts: Account Hierarchy and Selection
- Ad Groups: Ad Group Management
- Reports: Async Report Generation and Download
- Microsoft Docs: Campaign Management Service
- Reporting Service: Performance Reports