Bing Webmaster Tools
Category: SEO & Analytics
Integration type: Tenant API key (stored in integrations table)
External API: Bing Webmaster Tools API v1
Purpose
Bing Webmaster Tools provides organic search performance data from Bing and DuckDuckGo (DuckDuckGo partially relies on Bing’s index). While Google Search Console covers the dominant search engine, Bing Webmaster Tools ensures clients with significant Bing traffic are not flying blind.
Key uses:
- Report Writer — Bing organic search section alongside GSC data
- Site Auditor — Bing’s crawl errors and index coverage (complements GSC)
- Keyword Researcher — Bing keyword performance data as a secondary source
Bing Webmaster Tools is an optional integration — it is only relevant for clients who have measurable Bing traffic (typically B2B, finance, enterprise, and older demographic audiences where Bing/DuckDuckGo market share is higher).
Config Structure
Tenant integration
Bing Webmaster Tools uses an API key, not OAuth:
interface BingWebmasterConfig {
apiKey: string; // From Bing Webmaster → User Settings → API Access
siteUrl: string; // Exact URL as registered in Bing Webmaster (e.g. "https://acmeplumbing.com/")
}integrations row:
provider: 'bing_webmaster'
api_key: encrypt(apiKey)
metadata: { siteUrl }Integration Pattern
Tool layer (packages/tools/src/bing-webmaster.ts)
class BingWebmasterTool {
private baseUrl = 'https://ssl.bing.com/webmaster/api.svc/json';
constructor(
private apiKey: string,
private siteUrl: string,
) {}
private params(extra: Record<string, string> = {}) {
return { apikey: this.apiKey, ...extra };
}
async getQueryStats(options: {
startDate: string; // YYYY-MM-DD
endDate: string;
}): Promise<BingQueryStat[]> {
const response = await axios.get(`${this.baseUrl}/GetQueryStats`, {
params: {
...this.params(),
siteUrl: this.siteUrl,
startDate: options.startDate,
endDate: options.endDate,
},
});
const data = response.data.d ?? response.data;
return (data.QueryStats ?? []).map((row: any) => ({
query: row.Query,
clicks: row.Clicks,
impressions: row.Impressions,
ctr: row.Ctr,
avgPosition: row.AvgImpPosition,
}));
}
async getPageStats(options: {
startDate: string;
endDate: string;
page?: string; // Filter to a specific URL; omit for all pages
}): Promise<BingPageStat[]> {
const params: any = {
...this.params(),
siteUrl: this.siteUrl,
startDate: options.startDate,
endDate: options.endDate,
};
if (options.page) params.page = options.page;
const response = await axios.get(`${this.baseUrl}/GetPageStats`, { params });
const data = response.data.d ?? response.data;
return (data.PageStats ?? []).map((row: any) => ({
url: row.Page,
clicks: row.Clicks,
impressions: row.Impressions,
ctr: row.Ctr,
avgPosition: row.AvgImpPosition,
}));
}
async getCrawlStats(): Promise<BingCrawlStats> {
const response = await axios.get(`${this.baseUrl}/GetCrawlStats`, {
params: { ...this.params(), siteUrl: this.siteUrl },
});
const stats = response.data.d?.CrawlStats ?? response.data.CrawlStats ?? {};
return {
crawledUrls: stats.CrawledUrls,
inIndexUrls: stats.InIndexUrls,
blockedByRobots: stats.RobotsBlockedUrls,
crawlErrors: stats.CrawlErrors,
};
}
async getSites(): Promise<{ url: string; verified: boolean }[]> {
const response = await axios.get(`${this.baseUrl}/GetUserSites`, {
params: this.params(),
});
const sites = response.data.d?.Sites ?? response.data.Sites ?? [];
return sites.map((s: any) => ({
url: s.SiteUrl ?? s,
verified: s.IsVerified ?? true,
}));
}
async verify(): Promise<void> {
const sites = await this.getSites();
const match = sites.find(s => s.url === this.siteUrl);
if (!match) throw new Error(`Site ${this.siteUrl} not found in Bing Webmaster Tools`);
}
}Bing API response format quirk
Bing Webmaster API wraps responses in either d or a direct root object depending on the endpoint and API version. The tool layer handles both patterns (response.data.d ?? response.data).
Data Coverage
| Metric | Bing Webmaster | Google Search Console |
|---|---|---|
| Impressions | Yes | Yes |
| Clicks | Yes | Yes |
| CTR | Yes | Yes |
| Average position | Yes | Yes |
| Page-level data | Yes | Yes |
| Device breakdown | Partial | Yes |
| Country breakdown | No (API) | Yes |
| Index coverage | Crawl stats | URL inspection |
| Backlinks | No | No (use DataForSEO/Ahrefs) |
Test Cases
Unit tests (packages/tools/src/bing-webmaster.test.ts)
| Test | Approach |
|---|---|
getQueryStats() builds correct params | Mock axios.get; assert apikey, siteUrl, startDate, endDate |
getQueryStats() handles d wrapper | Mock { d: { QueryStats: [...] } }; assert rows returned |
getQueryStats() handles unwrapped response | Mock { QueryStats: [...] } directly; assert rows returned |
getCrawlStats() maps to typed object | Mock crawl stats; assert field names normalised |
verify() throws when siteUrl not in list | Mock getSites returns list without tenant URL; assert throws |
| Throws on 401 (invalid API key) | Mock 401; assert propagated |
Related
- Google Search Console Provider — primary SEO performance (Google)
- Site Auditor Agent — uses both GSC and Bing crawl data
- Report Writer Agent — includes Bing data when integration active
- Tool Integration Layer — integration management