Skip to main content

📁 Bing Ads - Ad Group Management

📖 Overview

Ad Groups organize ads within campaigns and define targeting settings. The Campaign Management Service provides SOAP operations for retrieving ad group information and generating performance reports. Ad groups can be filtered by campaign or retrieved for entire accounts.

Source Files:

  • Controller: external/Integrations/BingAds/Controllers/AdGroups/AdGroupController.js
  • Model: external/Integrations/BingAds/Models/AdGroups/AdGroupModel.js
  • Routes: external/Integrations/BingAds/Routes/adgroups.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

🏗️ Ad Group Hierarchy

graph TD
A[Account] --> C1[Campaign 1]
A --> C2[Campaign 2]
C1 --> AG1[Ad Group 1]
C1 --> AG2[Ad Group 2]
C2 --> AG3[Ad Group 3]
AG1 --> AD1[Ad 1]
AG1 --> AD2[Ad 2]
AG2 --> AD3[Ad 3]
AG3 --> AD4[Ad 4]

style A fill:#4CAF50
style C1 fill:#2196F3
style C2 fill:#2196F3
style AG1 fill:#FF9800
style AG2 fill:#FF9800
style AG3 fill:#FF9800
style AD1 fill:#9C27B0
style AD2 fill:#9C27B0
style AD3 fill:#9C27B0
style AD4 fill:#9C27B0

🔄 Data Flow

sequenceDiagram
participant Client as API Client
participant DC as DashClicks API
participant MW as Token Middleware
participant Model as AdGroupModel
participant SOAP as Bing SOAP API

Client->>DC: GET /adgroups
DC->>MW: Check token expiration
MW->>DC: Valid access token
DC->>Model: getAdgroupsUnfiltered() or getAdgroupFilteredByCampaignID()
Model->>SOAP: POST GetCampaignsByAccountId (if unfiltered)
SOAP-->>Model: Campaign list
loop For each campaign
Model->>SOAP: POST GetAdGroupsByCampaignId
SOAP-->>Model: Ad groups for campaign
end
Model-->>DC: Campaign + Ad Group pairs
DC-->>Client: JSON response

🔧 Ad Group Functions

List Ad Groups (Unfiltered)

GET /adgroups

Purpose: Retrieve all ad groups across all campaigns for an account

Controller: AdGroupController.index()getAdgroupsUnfiltered()

Model Method: AdGroupModel.getAdgroupsUnfiltered(customerAccountID, customerID, campaignType)

SOAP Operations: GetCampaignsByAccountId + GetAdGroupsByCampaignId (multiple calls)

Request:

GET /v1/integrations/bing/ads/adgroups?customerAccountID=111222333&customerID=123456&campaignType=Search
Authorization: Bearer {jwt_token}

Query Parameters:

ParameterTypeRequiredDescription
customerAccountIDIntegerAccount ID from /accessible-user-accounts
customerIDIntegerCustomer ID (optional but recommended)
campaignTypeStringFilter: Search, Shopping, DynamicSearchAds

Validation:

  • If customerAccountID missing, returns error: "Please provide customerAccountID optionaly you can provide customerID and campaignType as well as"

Business Logic Flow:

  1. Get Campaigns: Call getCampaignsByAccountId() to retrieve all campaigns
  2. Check Array: Determine if single campaign or multiple campaigns returned
  3. Loop Through Campaigns: For each campaign, call getAdGroupByCampaignID()
  4. Aggregate Results: Build array of campaign + ad group pairs
  5. Return: Nested structure with campaigns and their ad groups

SOAP Request XML (GetAdGroupsByCampaignId):

<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">GetAdGroupsByCampaignId</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>
<GetAdGroupsByCampaignIdRequest xmlns="https://bingads.microsoft.com/CampaignManagement/v13">
<CampaignId>987654321</CampaignId>
</GetAdGroupsByCampaignIdRequest>
</s:Body>
</s:Envelope>

JSON Response (Multiple Campaigns):

{
"success": true,
"message": "SUCCESS",
"data": [
{
"campaign": {
"Id": 987654321,
"Name": "Search Campaign",
"Status": "Active",
"BudgetType": "DailyBudgetStandard",
"DailyBudget": 50.00
},
"adgroup": [
{
"Id": 111222333,
"Name": "Ad Group 1",
"Status": "Active",
"CpcBid": {
"Amount": 1.50
},
"Language": "English",
"Network": "OwnedAndOperatedAndSyndicatedSearch"
},
{
"Id": 444555666,
"Name": "Ad Group 2",
"Status": "Paused",
"CpcBid": {
"Amount": 2.00
},
"Language": "English"
}
]
},
{
"campaign": {
"Id": 555666777,
"Name": "Shopping Campaign",
"Status": "Active"
},
"adgroup": [
{
"Id": 777888999,
"Name": "Product Group",
"Status": "Active"
}
]
}
]
}

JSON Response (Single Campaign):

{
"success": true,
"message": "SUCCESS",
"data": {
"campaign": {
"Id": 987654321,
"Name": "Search Campaign",
"Status": "Active"
},
"adgroup": [
{
"Id": 111222333,
"Name": "Ad Group 1",
"Status": "Active"
}
]
}
}

Key Response Fields:

FieldTypeDescription
campaignObjectCampaign details
adgroupArray/ObjectAd groups for the campaign
IdIntegerAd group identifier
NameStringAd group name
StatusStringActive, Paused, Deleted
CpcBid.AmountDecimalCost-per-click bid amount
LanguageStringTarget language
NetworkStringDistribution network

Side Effects:

  • ⚠️ Multiple API Calls: One call per campaign (can be slow for many campaigns)
  • None (read-only operation)

List Ad Groups (Filtered by Campaign)

GET /adgroups?campaignID={id}

Purpose: Retrieve ad groups for a specific campaign

Controller: AdGroupController.index()getAdgroupFilteredByCampaignID()

Model Method: AdGroupModel.getAdgroupFilteredByCampaignID(customerAccountID, customerID, campaignID)

SOAP Operations: GetAdGroupsByCampaignId + GetCampaignsByIds

Request:

GET /v1/integrations/bing/ads/adgroups?campaignID=987654321&customerAccountID=111222333&customerID=123456
Authorization: Bearer {jwt_token}

Query Parameters:

ParameterTypeRequiredDescription
campaignIDIntegerSpecific campaign ID to filter
customerAccountIDIntegerAccount ID
customerIDIntegerCustomer ID

Business Logic Flow:

  1. Get Ad Groups: Call getAdGroupByCampaignID() for specific campaign
  2. Get Campaign Details: Call getCampaignsByIds() to get campaign info
  3. Combine: Return both campaign and ad groups together
  4. Error Check: If SOAP fault, return fault object

JSON Response:

{
"success": true,
"message": "SUCCESS",
"data": {
"campaign": {
"CampaignId": 987654321,
"Name": "Search Campaign Name",
"Status": "Active",
"BudgetType": "DailyBudgetStandard",
"DailyBudget": 50.00
},
"adgroup": [
{
"Id": 111222333,
"CampaignId": 987654321,
"Name": "Ad Group 1",
"Status": "Active",
"StartDate": {
"Day": 1,
"Month": 10,
"Year": 2025
},
"EndDate": null,
"CpcBid": {
"Amount": 1.50
},
"Language": "English",
"Network": "OwnedAndOperatedAndSyndicatedSearch",
"BiddingScheme": {
"@i:type": "ManualCpcBiddingScheme"
},
"AdDistribution": "Search",
"AdRotation": {
"Type": "RotateAdsEvenly"
}
}
]
}
}

Ad Group Object Fields:

FieldTypeDescription
IdIntegerAd group ID
CampaignIdIntegerParent campaign ID
NameStringAd group name
StatusStringActive, Paused, Deleted
StartDateObjectStart date (Day/Month/Year)
EndDateObject/NullEnd date or null for ongoing
CpcBid.AmountDecimalManual CPC bid amount
LanguageStringLanguage targeting
NetworkStringDistribution network
BiddingSchemeObjectBidding strategy type
AdDistributionStringSearch, Content, All
AdRotation.TypeStringAd rotation strategy

Side Effects:

  • None (read-only operation)

Get Ad Group by ID

GET /adgroups/{adGroupID}

Purpose: Retrieve specific ad group by ID

Controller: AdGroupController.show()

Model Method: AdGroupModel.getAdGroupByID(customerAccountID, customerID, campaignID, adGroupID)

SOAP Operation: GetAdGroupsByIds

Request:

GET /v1/integrations/bing/ads/adgroups/111222333?customerAccountID=111222333&customerID=123456&campaignID=987654321
Authorization: Bearer {jwt_token}

Query Parameters:

ParameterTypeRequiredDescription
customerAccountIDIntegerAccount ID
customerIDIntegerCustomer ID
campaignIDIntegerCampaign ID (ad group parent)
adGroupIDIntegerSpecific ad group ID

Validation:

  • If any required param missing, returns error: "Please provide customerAccountID,customerID and adGroupID."

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">GetAdGroupsByIds</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>
<GetAdGroupsByIdsRequest xmlns="https://bingads.microsoft.com/CampaignManagement/v13">
<CampaignId>987654321</CampaignId>
<AdGroupIds i:nil="false" xmlns:a1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a1:long>111222333</a1:long>
</AdGroupIds>
</GetAdGroupsByIdsRequest>
</s:Body>
</s:Envelope>

JSON Response:

{
"success": true,
"message": "SUCCESS",
"data": {
"AdGroups": {
"Id": 111222333,
"CampaignId": 987654321,
"Name": "Ad Group 1",
"Status": "Active",
"CpcBid": {
"Amount": 1.50
},
"Language": "English",
"Network": "OwnedAndOperatedAndSyndicatedSearch"
}
}
}

Get Ad Group Performance Metrics

GET /adgroups/metrics

Purpose: Generate asynchronous performance report for ad groups

Controller: AdGroupController.metrics()

Model Method: AdGroupModel.adGroupReport(...)

SOAP Operation: SubmitGenerateReport

Service: Reporting Service v13

Request:

GET /v1/integrations/bing/ads/adgroups/metrics?customerAccountID=111222333&customerID=123456&campaignID=987654321&adGroupID=111222333&aggregation=Daily&predefinedTime=Last7Days
Authorization: Bearer {jwt_token}

Query Parameters:

ParameterTypeRequiredDescriptionDefault
customerAccountIDIntegerAccount ID-
customerIDIntegerCustomer ID-
campaignIDIntegerSpecific campaign IDAll campaigns
campaignTypeStringCampaign type filterAll types
adGroupIDIntegerSpecific ad group IDAll ad groups
aggregationStringAggregation levelMonthly
predefinedTimeStringPredefined time periodLast30Days
timeZoneStringReport timezoneEasternTimeUSCanada
fromDateStringCustom start date (YYYY-MM-DD)-
endDateStringCustom end date (YYYY-MM-DD)-

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="true">111222333</CustomerAccountId>
<CustomerId i:nil="false">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="AdGroupPerformanceReportRequest">
<ExcludeColumnHeaders>false</ExcludeColumnHeaders>
<ExcludeReportFooter>false</ExcludeReportFooter>
<ExcludeReportHeader>false</ExcludeReportHeader>
<Format>Csv</Format>
<Language>English</Language>
<ReportName>AdGroupPerformanceReportRequest</ReportName>
<ReturnOnlyCompleteData>false</ReturnOnlyCompleteData>
<Aggregation>Daily</Aggregation>
<Columns>
<AdGroupPerformanceReportColumn>AccountName</AdGroupPerformanceReportColumn>
<AdGroupPerformanceReportColumn>AdGroupId</AdGroupPerformanceReportColumn>
<AdGroupPerformanceReportColumn>AdGroupName</AdGroupPerformanceReportColumn>
<AdGroupPerformanceReportColumn>Impressions</AdGroupPerformanceReportColumn>
<AdGroupPerformanceReportColumn>Clicks</AdGroupPerformanceReportColumn>
<!-- 56+ more columns available -->
</Columns>
<Filter i:nil="false">
<CampaignType i:nil="false">Search</CampaignType>
</Filter>
<Scope>
<AccountIds xmlns:a1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a1:long>111222333</a1:long>
</AccountIds>
<!-- Optional: Specific campaign and ad group -->
<AdGroups i:nil="false">
<AdGroupReportScope>
<AccountId>111222333</AccountId>
<CampaignId>987654321</CampaignId>
<AdGroupId>111222333</AdGroupId>
</AdGroupReportScope>
</AdGroups>
<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 (56 total):

ColumnDescription
AccountName, AccountNumber, AccountIdAccount identifiers
CampaignName, CampaignId, CampaignStatusCampaign info
AdGroupName, AdGroupId, StatusAd group info
Impressions, Clicks, CtrCore metrics
Spend, AverageCpc, AveragePositionCost metrics
Conversions, ConversionRate, CostPerConversionConversion metrics
Revenue, ReturnOnAdSpend, RevenuePerConversionRevenue metrics
QualityScore, ExpectedCtr, AdRelevance, LandingPageExperienceQuality metrics
DeviceType, Language, NetworkTargeting dimensions
AllConversions, AllRevenue, ViewThroughConversionsAdvanced metrics

Full columns list: See getAdgroupColumns() method in source code (56 columns total)

JSON Response:

{
"success": true,
"message": "SUCCESS",
"data": {
"ReportRequestId": "987654321012345"
}
}

Important: This is an asynchronous operation:

  1. Returns ReportRequestId immediately
  2. Report generation happens in background
  3. Must poll PollGenerateReport to check status
  4. When Success, download CSV via ReportRequestId

📊 Ad Group Status Values

StatusDescription
ActiveAd group is active and serving ads
PausedAd group is manually paused
DeletedAd group is deleted (soft delete)

🎯 Ad Distribution Options

DistributionDescription
SearchBing search results only
ContentSyndicated partner sites only
AllBoth search and content network

🌐 Network Options

NetworkDescription
OwnedAndOperatedAndSyndicatedSearchBing, AOL, Yahoo + syndicated partners
OwnedAndOperatedOnlyBing, AOL, Yahoo only
SyndicatedSearchOnlySyndicated partners only

🔄 Bidding Scheme Types

TypeDescription
ManualCpcBiddingSchemeManual cost-per-click bidding
EnhancedCpcBiddingSchemeEnhanced CPC (automatic adjustments)
MaxClicksBiddingSchemeMaximize clicks within budget
MaxConversionsBiddingSchemeMaximize conversions
TargetCpaBiddingSchemeTarget cost per acquisition

📝 Helper Method

getAdgroupColumns()

Returns array of 56 available report column names for ad group performance reports.

⚠️ Important Notes

  • 🔄 Multiple API Calls: Unfiltered endpoint makes one call per campaign (performance consideration)
  • 📊 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)
  • 🎯 Campaign Required: Ad groups always belong to campaigns
  • 🔢 Campaign Filter: Use campaignID query param for faster, targeted results
  • 📊 56 Metrics Available: Full list in getAdgroupColumns()
  • 🌍 Timezone: Affects date range interpretation
  • 🔐 Three IDs Required: customerAccountID, customerID, and campaignID for specific ad group retrieval
  • 🔄 Token Auto-Refresh: Middleware ensures valid tokens
  • 🏗️ SOAP Protocol: XML requests/responses (parsed to JSON)
  • 📋 Campaign Type Filter: Can filter reports by Search, Shopping, or DynamicSearchAds
💬

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:30 AM