Bing’s new Topics Portfolio has changed how I approach AI SEO for one of my clients.
Instead of looking at individual search queries and trying to work out where to create more content, I can now look at entire topics, understand how much AI search activity they attract and identify where competitors are being cited while my client’s website is missing.
This is a much more useful way to plan an AI SEO strategy.
For example, I recently analysed a B2B SaaS company operating in the technology sector. Its main services include vibe coding, Model Context Protocol (MCP) and AI-assisted data modelling.

Using Bing Webmaster Tools, I could see how these topics were performing, which questions were already generating citations for the client and which opportunities were being taken by other websites.
One finding stood out.
Vibe coding was the biggest content opportunity for the business.
The individual search query “vibecoding” had an estimated 10,000 to 50,000 citations, compared with 5,000 to 10,000 for “what is mcp”.
I then downloaded the available data, analysed approximately 100,000 opportunity queries and turned the findings into a content plan.
In this article, I’ll explain how I approached the experiment, what I found and how SEO, content and marketing teams can use the same process to identify opportunities for their own websites.
The main takeaway: Bing’s Topics Portfolio gives SEO teams a better way to understand where AI search opportunities exist. Rather than creating content based on assumptions or a handful of keywords, you can use topic size, citation activity and competitor gaps to decide which subjects deserve your attention.
The Experiment: Finding AI Search Opportunities Using Bing Webmaster Tools Topic Portfolio
Before getting into the details, here is a summary of the experiment.
| Area | Details |
|---|---|
| Client | An anonymised enterprise B2B SaaS company operating in the technology sector |
| Main services | Vibe coding, Model Context Protocol (MCP) and AI-assisted data modelling |
| Tool | Bing Webmaster Tools, AI Performance report, Topics Portfolio beta |
| Data period | September 2026 |
| What I analysed | Topic size, client citations, grounding queries, existing citations and opportunity queries |
| Main finding | Vibe coding offered the biggest opportunity among the client’s three service areas |
| Top opportunity | “vibecoding”, with an estimated 10,000 to 50,000 citations |
| Next step | Build a content cluster around vibe coding and measure its performance across Bing and other AI assistants |
The objective was to find out which of the client’s core services offered the biggest opportunity to gain visibility in AI-generated answers.
Rather than guessing which topic to prioritise, I wanted to use actual citation data to make the decision.
1. What Has Changed in Bing Webmaster Tools?
On 13 October 2026, Bing introduced Topics Portfolio as a beta feature within its AI Performance report in Bing Webmaster Tools.
It provides a different way to look at the search queries behind AI-generated answers.
Instead of showing only the questions where your website already appears, it groups related queries into broader topics and identifies additional opportunities where other websites are cited but yours is not.
This is particularly useful because you can start seeing the size of a topic and the opportunities available before investing time and money in creating content.
What Are Grounding Queries?
Grounding queries are the searches an AI system carries out to find information before producing an answer.
For example, someone might ask Microsoft Copilot:
“Which are the best AI-powered development tools for a B2B software company?”
Before answering, the system may search for information about:
- AI development tools.
- Vibe coding platforms.
- Software development solutions.
- Product comparisons.
- Pricing and available features.
These searches help the AI system find information that it can use when generating its response.
Some of the websites it finds may then be cited as sources.
Bing’s AI Performance report provides information about these grounding queries and the pages receiving citations.
How Has Bing’s AI Performance Report Developed?
Bing initially introduced its AI Performance report as a public preview in February 2026.
The first version provided information about citations by page and grounding query.
A further update in June 2026 introduced additional reporting, including:
- Search intent.
- Topics.
- Citation share.
- Competitor comparisons.
However, the earlier topic reporting mainly grouped queries where a website was already receiving citations.
Topics Portfolio goes further.
It provides a broader view of the topic, including queries where other websites receive citations but your own website does not.
This is the part I find particularly useful for content planning.
You are no longer looking only at the subjects where you already have some visibility. You can also investigate the areas where competitors are getting attention.
Where Can You Find Topics Portfolio?
You can access it through Bing Webmaster Tools.
Open the AI Performance report and select the Topics Portfolio tab next to Citations & Pages.
The report provides one-month, two-month and three-month reporting periods.
The feature only appears when Bing has enough data to build topics for your website.
Smaller or newer websites might therefore see very few topics or none at all.
For those websites, the Citations & Pages report is still available.
What Information Does Topics Portfolio Provide?
There are several measurements worth understanding before analysing the data.
| Metric | What it tells you |
|---|---|
| Topic | A group of related grounding queries that Bing’s AI has placed within the same broader subject. |
| Topic size | The estimated number of citations across all websites for the topic during the selected period. |
| Sites cited | The approximate number of different websites receiving citations for queries within the topic. |
| Queries | The estimated number of distinct grounding queries within the topic. |
| Your citations | How many times your website was cited for queries within the topic. |
| Your grounding queries | The individual questions for which your website is already receiving citations. |
| Opportunity queries | Questions where other websites are receiving citations but yours is not. Bing provides estimated citation ranges rather than exact figures. |

The Technology topic for the anonymised client, September 2026. Website names, existing citations and grounding queries have been hidden.
For my client, the Technology topic showed:
- An estimated 1 to 5 billion citations across all websites.
- Approximately 1 to 5 million distinct grounding queries.
- Existing client citations and grounding queries.
- Additional opportunity queries where other websites were being cited.
Two queries immediately caught my attention:
| Opportunity query | Estimated citations |
|---|---|
| vibecoding | 10,000–50,000 |
| what is mcp | 5,000–10,000 |
The difference was interesting because both topics were directly related to the client’s services.
It provided an early indication that vibe coding deserved more attention.
However, I didn’t stop at the headline figures.
I downloaded the topic data and used it to identify the individual questions that could become part of a much larger content strategy.
2. Why Is Bing’s Topics Portfolio Important for SEO Teams?
The biggest benefit of Topics Portfolio is that it changes how you can approach AI search content planning.
Before this type of reporting was available, understanding AI visibility often meant looking at individual grounding queries and trying to group them manually.
You might have hundreds or thousands of questions, but it was difficult to understand which broader subjects were worth investing in.
Topics Portfolio gives you a way to look at the bigger picture.
Moving from Individual Queries to Entire Topics
Traditional keyword research often starts with individual search terms.
You might find a keyword, check its search volume and decide whether to create a page around it.
The same approach can be applied to AI search, but grounding queries introduce another challenge.
There can be thousands of different questions around the same subject.
For example, a technology company offering vibe coding services might encounter questions such as:
- What is vibe coding?
- How does vibe coding work?
- Which vibe coding tools are best?
- Is vibe coding suitable for enterprise development?
- What are the risks of vibe coding?
- Vibe coding vs traditional software development.
These are different questions, but they all relate to the same broader topic.
Rather than creating a separate content strategy around every query, I would group them into a content cluster.
This is where Topics Portfolio becomes useful.
| Content planning question | What individual queries tell you | What Topics Portfolio adds |
|---|---|---|
| Is this topic worth investing in? | A selection of individual questions | Estimated topic size and query count. |
| How competitive is the topic? | Difficult to establish from isolated queries | The estimated number of websites being cited. |
| Where are we already visible? | Individual queries where your website receives citations | Existing citations and grounding queries grouped by topic. |
| Where are competitors getting visibility? | Often difficult to identify through citation reporting alone | Opportunity queries where other websites are cited instead. |
| Which subjects should we prioritise? | Keyword research and estimated search demand | Opportunity queries with estimated citation ranges. |
For SEO teams, this makes it easier to move from research into actual content planning.
You Can Identify Where Competitors Are Getting Cited and Your Website Is Missing
This is probably the most interesting part of the feature.
Most AI visibility reports focus on where your website already receives citations.
That information is useful, but it only tells you part of the story.
Opportunity queries show where other websites are being cited instead.
Bing does not identify the exact competitors receiving citations for every opportunity query, but it does show the questions and estimated citation ranges.
For my client, this meant I could identify questions about vibe coding and MCP where other websites were already appearing in AI-generated answers.
I could then investigate whether the client had suitable content covering those subjects.
This is a much better starting point for finding content gaps.
You Can Understand the Size of an Opportunity Before Creating Content
Another benefit is that you can assess the scale of a topic before committing resources.
For example, a topic containing billions of estimated citations across websites and millions of distinct grounding queries is clearly attracting substantial AI search activity.
Likewise, an individual opportunity query with an estimated 10,000 to 50,000 citations deserves more attention than a query with very limited citation activity, assuming both are relevant to the business.
This helps when presenting content recommendations to marketing directors.
Instead of saying that a particular subject looks interesting, you can explain:
- How large the topic is.
- How many grounding queries it contains.
- How many websites are receiving citations.
- How much citation activity is already associated with your website.
- Where additional opportunities exist.
It gives your content plan a stronger commercial starting point.
Can Bing’s Data Help with ChatGPT, Gemini and Perplexity?
This is something I find particularly interesting.
Bing’s Topics Portfolio provides information relating to Microsoft Copilot, Bing’s AI experiences and selected partner integrations.
It does not directly report what happens in ChatGPT, Gemini or Perplexity.
However, these AI assistants also carry out searches to find information before generating answers.
That means a topic attracting considerable citation activity in Bing is a sensible place to investigate across other assistants.
For example, if vibe coding is generating substantial citation activity in Bing, I would want to understand whether ChatGPT, Gemini, Perplexity and Claude are also responding to similar questions.
This is a hypothesis, not something the Bing report proves.
The way to test it is to track the same questions across those assistants and compare the results.
Important Limitations of Topics Portfolio
There are a few things I would keep in mind when interpreting the data.
The numbers are estimates
Bing describes the figures as directional information for planning rather than an exact record of every citation.
Treat the citation ranges as a guide to the size of an opportunity, not as guaranteed traffic or a target for reporting.
Topic names are generated by AI
The topics are automatically grouped and labelled.
They might not always match the way your business organises its services or products.
You may need to combine, separate or filter topics when preparing your content strategy.
Competitors’ brand names can appear as opportunities
Bing can include branded queries relating to other businesses.
These are not necessarily suitable content opportunities.
I would remove them unless there is a genuine reason to create an honest comparison page.
The feature is still in beta
Topics Portfolio is a beta feature, and Bing has not provided a confirmed date for when it will leave beta.
The reporting and available information may change.
I would therefore use it as one source of research rather than making it the only measure of AI search performance.
3. Which Businesses Should Use Bing’s Topics Portfolio?
Any website with enough AI citation activity for Bing to generate topic data can benefit from the feature.
In practice, I would pay particular attention to established brands, publishers, B2B businesses and companies with substantial content libraries.
The way you use the report will depend on your industry and the services or products you sell.
How Different Industries Can Use Topics Portfolio
| Business type | What I would investigate | How I would use the information |
|---|---|---|
| B2B SaaS and technology | Product category topics, definitions, technical guides and how-to questions where competing websites receive citations. | Identify the biggest opportunity related to core services and build a supporting content cluster. |
| Ecommerce and retail | Product categories, buying guides and competitor-related queries. | Prioritise product categories based on topic size and remove irrelevant competitor-branded queries. |
| Publishers and media | Large topics with many competing websites. | Look at the number of sites cited and prioritise subjects where there is a realistic opportunity to gain visibility. |
| Professional services and agencies | Topics related to individual services and competing businesses. | Connect topic opportunities to actual services and compare results across clients. |
| Finance, healthcare and other regulated industries | Topics where AI assistants rely heavily on comparison websites and established information sources. | Create accurate, expert-reviewed content around important topics. |
| Smaller or newer websites | Whether sufficient topic data is available. | Continue using Citations & Pages until Bing has enough information to generate topics. |
Which Teams Should Be Involved?
I would involve several teams when turning Topics Portfolio data into a content strategy.
SEO team
The SEO team should download the reports, organise the opportunity queries, assess their commercial value and manage the overall content plan.
Content team
The content team should create the main topic page and supporting articles based on the selected opportunities.
Product marketing
Product marketing needs to make sure that the proposed topics are relevant to what the business actually sells.
They should also provide technical knowledge, product information and examples.
Marketing leadership
Marketing directors should decide which topics deserve investment and provide the budget and resources required to produce the content.
Development team
Development should make sure that the new pages are accessible to search engines and AI crawlers, load properly and are included in the website’s sitemaps.
Communications team
Communications should look at opportunities to get the business mentioned on relevant third-party websites that are already receiving citations for the topic.
4. How to Turn Bing Topics Portfolio Data into a Content Strategy
I approached this experiment as a content planning exercise.
The objective was not simply to download a list of queries and give it to a writer.
I wanted to understand which of the client’s services had the biggest opportunity, which questions were worth targeting and what content needed to be created.
Here is the process I followed.
Step 1: Open Topics Portfolio and Choose Your Reporting Period
Open Bing Webmaster Tools and navigate to the AI Performance report.
Select the Topics Portfolio tab next to Citations & Pages.
You can choose between one-month, two-month and three-month reporting periods.
For this experiment, I used one month of data from September 2026.
I wanted a relatively recent view of the available opportunities, and I’ll continue using the same reporting period each month to measure changes.
I would also recommend starting with the topics where your website already receives citations.
Bing suggests looking at these areas first.
Pay attention to the Sites cited measurement.
A topic with fewer competing websites may be easier to gain visibility in than one where thousands of different websites are already receiving citations.
Step 2: Download the Topic Data
Once you’ve identified the relevant topics, open each one and select Download.
The report used in my experiment included two main groups of information:
Your grounding queries
These are the questions where your website is already receiving citations, alongside the number of citations attributed to your site.
Opportunity queries
These are the questions where other websites are being cited but yours is not.
Bing provides estimated citation ranges for these queries.
For my client, the Technology topic contained approximately 100,000 opportunity queries.
That’s far too many to review manually.
And that’s exactly why the next step is important.
The value is not in reading every query individually. It’s in grouping the information into subjects that match what the business actually sells.
Step 3: Clean the Opportunity Query List
Topics are automatically grouped by AI, which means a broad subject such as Technology can contain many questions that have nothing to do with your business.
Before scoring the opportunities, I recommend removing three types of queries.
1. Competitors’ branded queries
Bing can include questions containing competitors’ brand names.
For example, someone might ask:
“What is the best alternative to Competitor X?”
You do not necessarily need to create a page targeting every competitor name that appears in the report.
Only keep these opportunities where a comparison page would genuinely help potential customers make a decision.
2. Queries outside your services
A Technology topic might include:
- General programming questions.
- Consumer technology.
- Software development news.
- Programming tutorials.
- Technical questions unrelated to your products.
A B2B SaaS company should not create content around every query simply because it appears in the same topic.
Focus on subjects that have a clear connection to the services you sell.
3. Queries with different spellings that mean the same thing
Look for variations of the same query.
For example:
- vibecoding.
- vibe coding.
- vibe coder.
These should be grouped together rather than counted as completely separate opportunities.
Otherwise, you risk overstating the amount of demand associated with a topic.
If Bing has grouped a query under the wrong topic, you can also use the thumbs-down feedback option next to it.
Bing says this feedback helps it decide which issues to address.
Step 4: Group Opportunities by Your Products and Services
This is where the analysis becomes useful.
My client had three main service areas:
- Vibe coding.
- Model Context Protocol.
- AI data modelling.
I created a list of related terms for each service and used them to group the opportunity queries.
Here is how I approached the initial classification.
| Service area | Matching terms | Main opportunity identified | What I assessed |
|---|---|---|---|
| Vibe coding | vibe coding, vibecoding, vibe coder | vibecoding (10,000–50,000 citations) | Combined citation ranges across matching opportunity queries. |
| Model Context Protocol | MCP, Model Context Protocol | what is mcp (5,000–10,000 citations) | Share of the total opportunity and existing client citations. |
| AI data modelling | data model, data modelling, schema | Related queries from the export | Relevant opportunities and the client’s existing citations. |
I then compared the opportunities for each service area.
Because Bing provides citation ranges rather than exact figures, I used the midpoint of each range on a multiplying scale.
The calculation was:
[
\text{Central estimate} = \sqrt{\text{Lower range} \times \text{Upper range}}
]
For example, a query with an estimated 10,000 to 50,000 citations would receive a central estimate of approximately 22,400.
I then added the estimates for queries belonging to the same service area.
The objective was not to produce an exact number of expected citations.
It was to give each service area a comparable score so I could see which one offered the biggest opportunity.
Vibe coding came out on top by a clear margin.
I would use this information to prioritise the first content cluster, while keeping the other service areas in the plan for future work.
Step 5: Check Whether the Opportunity Is Worth Pursuing
Before committing a content budget, I recommend assessing the opportunity from three perspectives.
1. Is there enough demand?
Look at the overall topic size, the number of grounding queries and the citation ranges for the most important opportunities.
In this experiment, the Technology topic contained an estimated:
- 1 to 5 billion citations across all websites.
- 1 to 5 million distinct grounding queries.
The top vibe coding query alone had an estimated 10,000 to 50,000 citations.
This suggested that there was enough activity to justify further investigation.
2. Can your business realistically gain citations?
Look at the number of different websites receiving citations within the topic.
A topic with fewer competing websites may present a more realistic opportunity.
You should also consider whether your website already receives citations for related queries.
If Bing is already using your website for similar questions, you may have a useful starting point.
This doesn’t guarantee that new pages will receive citations, but it can help you decide where to invest your resources.
3. Does the topic match what your business sells?
This is particularly important.
You should not build a content cluster simply because a topic has a large number of citations.
The subject needs to connect to your products or services.
For my client, vibe coding was one of its core service areas.
This meant that the supporting pages could explain the subject, answer technical questions and lead interested visitors towards relevant product pages or a demo.
Vibe coding passed all three checks, so it became the first priority.
Step 6: Build a Content Cluster Around the Winning Topic
Once you’ve selected the topic, the next job is to turn the opportunity queries into a content plan.
For my client, the vibe coding queries could be grouped into several different search intents:
- What vibe coding is.
- How to use vibe coding.
- Which tools and platforms are available.
- How vibe coding compares with traditional software development.
- Examples and use cases.
- Security and governance.
I would use the main topic as the pillar page and create supporting articles around each important subject.
Here is an example of how I would structure the cluster.
| Page | Purpose | Example queries to answer | Main team |
|---|---|---|---|
| Vibe coding (pillar page) | Explain the topic, how it works, available tools, risks and how the client’s product fits. | vibecoding, vibe coding | Content and product marketing |
| What is vibe coding? | Provide a clear explanation and practical examples. | what is vibe coding, vibe coding meaning | Content |
| How to start vibe coding | Explain the process for developers and software teams. | how to start vibe coding, vibe coding tutorial | Content and engineering |
| Vibe coding tools and platforms | Compare available tools, including the client’s product. | best vibe coding tools, vibe coding platforms | Product marketing |
| Vibe coding vs traditional development | Explain the differences, costs and situations where each approach makes sense. | vibe coding vs coding, alternatives to vibe coding | Content |
| Vibe coding examples and use cases | Show practical business applications and customer examples. | vibe coding examples, vibe coding projects | Product marketing |
| Vibe coding security and governance | Explain security risks, code reviews, data handling and controls. | vibe coding security risks, is vibe coding safe | Content and security |
The example questions demonstrate the different search intents.
The final page plan should use the actual queries and citation ranges from the Topics Portfolio export.
I would prioritise pages based on their commercial relevance, citation opportunity and how well they support the main pillar page.
How I Would Write Content for AI Search
There is an important difference between creating a list of articles and creating content that genuinely answers the questions AI assistants are trying to resolve.
I would follow five writing rules.
1. Answer the main question immediately
The first two sentences should answer the main question in straightforward language.
If someone searches for “What is vibe coding?”, they should not have to read five paragraphs about the development of AI before finding the definition.
2. Give each section one clear purpose
Each H2 should answer a specific question or cover one subject.
Avoid headings such as “More information” or “Things to consider”.
Make each section understandable on its own.
3. Use practical examples rather than making general claims
Include information that helps readers understand the subject.
Depending on the topic, this might include:
- Step-by-step instructions.
- Code examples.
- Comparison tables.
- Screenshots.
- Original research.
- Company data.
- Comments from engineers.
- Customer examples.
For my client, input from its own engineers would be particularly useful because it provides technical information that competing websites cannot simply reproduce.
4. Use consistent product and company names
Make sure the company name, product names and technical terminology are used consistently across the content.
For this client, that means using the terms vibe coding, MCP and data modelling correctly.
5. Keep the content up to date
Vibe coding is a fast-moving subject.
I would add a last updated date to each page and review the content at least quarterly.
Technical examples, tools and recommendations can become outdated quickly.
Step 7: Implement the Content Cluster Across Your Website
Once the content is ready, the development and marketing teams need to make sure it is properly implemented.
I would split the work between development, content and marketing.
Development Team
Publish the content under one main folder
For example:
/vibe-coding/
The pillar page and supporting articles should sit within a clear website structure.
Use appropriate breadcrumbs and BreadcrumbList structured data.
Make the content available in the initial HTML
Where possible, make sure the main content is included in server-rendered HTML.
Some AI crawlers have limited JavaScript capabilities. Content that only appears after scripts run may not be accessible to every crawler.
Connect the pages
The pillar page should reference its supporting articles, and those articles should point back to the pillar where relevant.
Each page should also provide a clear route towards the relevant product or service page.
You should also update existing pages that already receive citations for related grounding queries, so visitors and search engines can discover the new content.
Implement the relevant structured data
Use suitable structured data for each page type.
For example:
- Article structured data for supporting articles.
- Organization structured data for the business.
- SoftwareApplication structured data for relevant software products.
Include accurate authorship and publication dates where appropriate.
Submit the new pages to Bing
Update your XML sitemap with the new and modified URLs.
Make sure the last modification dates are correct and submit the URLs through IndexNow, which Bing supports.
You can then check the URLs using URL Inspection in Bing Webmaster Tools.
Check crawler access
Review robots.txt and your CDN settings to make sure Bingbot can access the new pages.
Also decide which other AI crawlers you want to allow access to your website.
Content and Product Marketing Teams
Write the pillar page first
Start with the main topic before publishing the supporting articles.
This gives the content cluster a clear foundation.
Prioritise supporting pages
Use the citation ranges and commercial relevance of each opportunity to decide which articles should be written first.
Get technical input
Ask the client’s engineers to review technical claims and contribute examples.
This is particularly important for subjects such as vibe coding, MCP and AI-assisted development.
Update existing content
Review pages that already receive citations.
Where appropriate, update them with relevant information and make sure they connect to the new content cluster.
Marketing and Communications Teams
Bing’s opportunity queries show where other websites receive citations, but they do not identify every competitor website responsible.
I would use another AI visibility tool to investigate which websites assistants rely on when answering the main questions.
We use SD SEO Suite for this type of research.
Once you have identified the important websites, look for opportunities to get the business and its expertise featured.
For a technology company focusing on vibe coding, this could include:
- Guest tutorials.
- Developer documentation.
- Open-source examples.
- Technical contributions.
- Conference presentations.
- Specialist technology publications.
- Developer communities.
The objective is to build a stronger presence across the websites and communities influencing the answers.
Step 8: Measure the Results of Your Content Experiment
The September 2026 data provides the starting point for this experiment.
Once the new content has been published, I would continue measuring the same areas each month.
The important thing is to keep the reporting consistent so you can compare changes over time.
| What I would measure | Where to measure it | Frequency |
|---|---|---|
| Client citations across the Technology topic and vibe coding queries | Bing Webmaster Tools, Topics Portfolio, using the same one-month reporting window | Monthly |
| Opportunity queries that become grounding queries | Comparison of monthly Topics Portfolio downloads | Monthly |
| Citations received by each new content page | Bing AI Performance, Citations & Pages | Weekly |
| Brand mentions and citations across ChatGPT, Gemini, Perplexity and Claude | SD SEO Suite AI Visibility | Weekly |
| Impressions and clicks for new cluster pages, including AI Overview and AI Mode appearances | Google Search Console Performance and Generative AI Performance reports | Monthly |
| Demo requests and sign-ups generated by the new content | Website analytics and CRM | Monthly |
There is also an important hypothesis I want to test.
If Bing’s topic data is a useful indicator of wider AI search demand, the same questions should also provide opportunities across ChatGPT, Gemini, Perplexity and Claude.
I will therefore track the same questions across all four assistants and compare the results.
The objective is to find out whether the opportunities identified through Bing translate into improved visibility elsewhere.
5. Implementation Checklists for SEO, Content and Marketing Teams
For businesses planning to use Topics Portfolio regularly, I recommend making this a shared process between SEO, content, development and marketing.
These checklists provide a starting point.
SEO Team
- Open Topics Portfolio and record topic size, sites cited and query counts.
- Use the same reporting period every month.
- Download the topics relevant to the business.
- Remove competitor-branded queries that are not suitable content opportunities.
- Remove queries that do not relate to your products or services.
- Group opportunity queries by service area.
- Compare citation opportunities with your existing citations.
- Assess topic size, competition and commercial relevance.
- Choose the first content cluster.
- Build the page plan using the actual opportunity queries.
- Compare monthly reports and record changes in citations.
Content and Product Marketing Teams
- Write the main pillar page first.
- Create supporting articles based on the opportunity queries.
- Answer each page’s main question in the opening paragraphs.
- Give each H2 a clear purpose.
- Include original examples, code, data and expert input.
- Check technical information with the relevant product or engineering teams.
- Update existing pages that already receive citations.
- Keep product names, company names and technical terms consistent.
- Review important content at least quarterly.
Development Team
- Publish the content cluster under one clear website folder.
- Implement breadcrumbs and appropriate BreadcrumbList structured data.
- Make the main content available in server-rendered HTML where possible.
- Connect pillar pages, supporting articles and relevant product pages.
- Implement Article structured data with correct author and date information.
- Update XML sitemaps and last modified dates.
- Submit new and updated URLs through IndexNow.
- Check new URLs using Bing Webmaster Tools.
- Confirm robots.txt and CDN settings allow Bingbot access.
Marketing and Communications Teams
- Identify third-party websites that AI assistants cite for important queries.
- Look for relevant publishing and partnership opportunities.
- Build relationships with specialist websites and communities.
- Track whether ChatGPT, Gemini, Perplexity and Claude mention and cite the business.
- Review AI visibility changes alongside traditional organic search performance.
6. Frequently Asked Questions About Bing Topics Portfolio
What Is Bing’s Topics Portfolio?
Topics Portfolio is a beta feature within the AI Performance report in Bing Webmaster Tools; it groups the grounding queries behind AI-generated answers into broader topics.
It shows the estimated size of each topic, how many websites receive citations, how often your website is cited and which additional queries present opportunities to gain visibility.
What Is the Difference Between Topic Size and Your Citations?
Topic size estimates the total number of citations across all websites for a particular topic during the selected period.
Your citations show how many of those citations went to your own website.
Together, these measurements help you understand how much AI search activity is associated with a topic and how much visibility your website currently receives.
What Are Opportunity Queries?
Opportunity queries are questions within a topic where other websites receive citations but your own website does not.
Bing provides estimated citation ranges rather than exact numbers.
For example, an opportunity query might have an estimated 10,000 to 50,000 citations.
These queries can help you identify subjects where additional content may improve your AI search visibility.
Why Can’t I See Topics Portfolio in Bing Webmaster Tools?
The feature only appears when Bing has enough information to generate topic data for your website.
Smaller or newer websites may see very few topics or none at all.
You can still use the Citations & Pages section of AI Performance to review your existing citations and grounding queries.
Can I Download the Topics Portfolio Data?
Yes.
Each topic has a Download option.
The exported data includes your existing grounding queries and opportunity queries, along with their available citation information.
You can then analyse the data using spreadsheets or a suitable analysis tool.
Does Bing’s Data Show What Happens in ChatGPT, Gemini, Perplexity and Claude?
Not directly.
Topics Portfolio reports on AI search activity associated with Microsoft Copilot, Bing and selected partner integrations.
However, the topics can provide useful ideas for investigating other AI assistants.
For example, if vibe coding attracts substantial citation activity in Bing, you can track similar questions across ChatGPT, Gemini, Perplexity and Claude to see whether the same opportunities exist.
This needs to be tested rather than assumed. I recommend starting with SD SEO Suite.
Should I Create Content Targeting Competitors’ Branded Searches?
Not automatically.
Bing can include competitor brand names in opportunity queries.
I would normally remove these from the main content plan unless there is a genuine reason to create a comparison or alternatives page.
Any comparison content should provide useful and accurate information rather than simply targeting another company’s name.
How Often Should I Review Topics Portfolio?
I recommend reviewing it monthly using the same reporting period.
This makes it easier to compare downloads and understand which opportunities are changing.
After publishing new content, I would also check the Citations & Pages report weekly to see whether individual pages are beginning to receive citations.
7. The Free Python Toolkit for Bing Topics Portfolio
To make this process easier, I’ve also created a Python script for SEO leads called topic_portfolio_planner.py.
It is designed to help analyse Topics Portfolio downloads and turn the results into a content plan.
The script uses Python’s standard library, so there is nothing additional to install.
It has four main commands.
1. The Summary Command
The summary command provides an overview of a Topics Portfolio download.
It shows:
- Main topic figures.
- Number of grounding queries.
- Number of opportunity queries.
- Highest-priority opportunities.
- Competitor-branded queries that may need removing.
This gives you a quick overview before starting the analysis.
2. The Clusters Command
The clusters command groups opportunity queries based on your products or services.
For example, a technology company might define the following themes:
- Vibe coding.
- Model Context Protocol.
- Data modelling.
The script then groups relevant queries and ranks the opportunities based on their estimated citations, share of the opportunity and existing citations.
Any remaining queries can be grouped automatically.
3. The Plan Command
The plan command turns a selected topic into a content cluster.
It suggests:
- A main pillar page.
- Supporting articles.
- A main query for each page.
- Other queries each page should answer.
- Suggested URLs.
This gives your content team a starting point for building an article plan from the actual opportunity data.
4. The Compare Command
The compare command compares two monthly Topics Portfolio downloads.
It identifies:
- Opportunity queries that now receive citations for your website.
- Changes in existing citations.
- Opportunities that are no longer appearing.
- Changes in citation activity for selected topics.
This is particularly useful when measuring whether your new content is improving AI visibility.
How to Run the Toolkit
Place the Python script in the same folder as your Topics Portfolio download.
Open Terminal on Mac or Command Prompt on Windows and navigate to that folder.
You can then run the following commands.
Group opportunities into content themes:
python3 topic_portfolio_planner.py clusters Technology.csv --themes "vibe coding=vibe cod,vibecod|mcp=mcp,model context protocol|data modelling=data model" --exclude "rivalbrand"
Create a content plan for a selected topic:
python3 topic_portfolio_planner.py plan Technology.csv --theme "vibe coding=vibe cod,vibecod" --exclude "rivalbrand"
Compare two monthly downloads:
python3 topic_portfolio_planner.py compare September.csv October.csv --theme "vibe coding=vibe cod,vibecod"
The words after each equals sign define which terms should be used to identify a service area.
For example:
vibe coding=vibe cod,vibecod
This allows the script to identify queries containing variations such as vibe coding, vibecoding and vibe coder.
Short terms such as mcp are matched as whole words to avoid including unrelated queries.
Each command also creates a CSV file that you can open in Excel and share with your content team.
Example Output: Grouping the Opportunity Queries
The following example uses a small test file based on the type of data shown in the client experiment.
This is sample data, not the client’s actual results.
CLUSTERS 4 opportunity theme(s)
cluster kind queries citations* share you top query
vibe coding your service 9 35.8K 73% - vibecoding (10K-50K)
mcp your service 3 10K 20% 1.5K what is mcp (5K-10K)
data modelling your service 1 2.2K 5% 800 ai data modeling (1K-5K)
python auto 2 931 2% - python list comprehension (500-1K)
* Central estimate from Bing's citation ranges.
'you' = your current citations in that cluster.
The result identifies vibe coding as the largest opportunity among the three service areas in this test dataset.
It accounts for 73% of the opportunity.
Example Output: Building a Content Cluster
The plan command takes the selected topic and turns it into a suggested page structure.
Here is an abbreviated example using the same test data.
PLAN content cluster for "vibe coding":
8 page(s), 9 opportunity queries, about 35.8K citations
[pillar] overview
/vibe-coding/
main query: vibecoding (10K-50K)
9 queries, about 35.8K citations
[supporting] Tools and platforms
/vibe-coding/vibe-coding-tools/
main query: vibe coding tools (5K-10K)
2 queries, about 9.3K citations
also answer: best vibe coding platform for teams
[supporting] What it is
/vibe-coding/what-is-vibe-coding/
main query: what is vibe coding (1K-5K)
1 query, about 2.2K citations
This gives your team an initial content structure based on the opportunities in the export.
Rather than manually reviewing thousands of queries, you can identify the main themes, prioritise them and build a page plan.
Full Kit:
#!/usr/bin/env python3
"""
topic_portfolio_planner.py - turn a Bing Webmaster Tools Topics Portfolio
download into a prioritised AI SEO content plan.
Szymaniak Digital. Standard library only (Python 3.8+). Nothing to install.
Built for SEO leads: it works on the CSV you download from a topic in
Bing Webmaster Tools > AI Performance > Topic Portfolio (beta).
Four commands:
summary What's in the download: the topic's headline figures (if the
file includes them), how many of your grounding queries and
opportunity queries it holds, the opportunity queries with the
highest citation ranges, and any branded queries to remove.
python3 topic_portfolio_planner.py summary Technology.csv
clusters Groups the opportunity queries into themes and ranks them by
opportunity. Give it your service areas as themes, so it can
tell you which of YOUR services has the biggest gap; anything
that doesn't match a theme is grouped automatically.
python3 topic_portfolio_planner.py clusters Technology.csv \
--themes "vibe coding=vibe cod,vibecod|mcp=mcp,model context protocol|data modelling=data model"
plan Builds a content cluster plan for one theme: a pillar page and
supporting pages grouped by what people want (definitions,
how-to guides, tools, comparisons, examples, pricing, security),
each with its main query, the queries it should answer, and a
suggested URL. Also lists the queries you're already cited for,
so the new pages can link to and from them.
python3 topic_portfolio_planner.py plan Technology.csv \
--theme "vibe coding=vibe cod,vibecod"
compare Compares two downloads of the same topic (for example
September and October) and lists the opportunity queries you're
now cited for, the citations you've lost, and how your citations
changed. Use it to measure whether the new content worked.
python3 topic_portfolio_planner.py compare September.csv \
October.csv --theme "vibe coding=vibe cod,vibecod"
How the numbers work
Bing shows opportunity citations as ranges such as "10K-50K". The script
reads each range as low and high values and uses the geometric midpoint
(the square root of low x high; 22,361 for 10K-50K) as a central estimate,
because the ranges grow by multiples. "<1K" counts as 0 to 1,000 and
"50K+" as 50,000. Bing says to treat these figures as directional, so use
the ranking, not the totals, for decisions.
The download
The script finds the columns itself. It looks for a column containing
"query" and one containing "citation", and works out whether each row is
one of your grounding queries or an opportunity query from a "section" or
"type" column, or from the table headings in the file ("Your Grounding
Query", "Opportunity Query"). It also accepts several files at once.
Plain-English run guide: put this file in the same folder as your download,
open Terminal (Mac) or Command Prompt (Windows) in that folder and type one
of the commands above ("python" instead of "python3" on Windows). Add
--help after a command to see its options. Each command prints a summary and
writes a CSV you can open in Excel.
"""
import argparse
import csv
import math
import re
import sys
from collections import Counter, defaultdict
STOP = set("""a an and are as at be best by can do does for from get how i in
into is it its me my of on or our the to vs what when where which who why will
with you your using use used""".split())
INTENTS = [
("definition", "What it is",
r"^(what is|what are|what's|whats|define|definition of)\b|\bmeaning\b|"
r"\bdefinition\b|\bexplained\b"),
("how-to", "How-to guides",
r"\bhow (to|do|can|does)\b|\btutorial\b|\bguide\b|\bstep by step\b|"
r"\bset ?up\b|\binstall\b|\bgetting started\b|\blearn\b|\bcourse\b"),
("comparison", "Comparisons and alternatives",
r"\bvs\b|\bversus\b|\bcompare\b|\bcomparison\b|\balternatives?\b|"
r"\bdifference\b"),
("tools", "Tools and platforms",
r"\btools?\b|\bsoftware\b|\bplatforms?\b|\bapps?\b|\bbest\b|\btop\b|"
r"\bide\b|\beditors?\b|\bextensions?\b|\bservers?\b|\bframeworks?\b"),
("examples", "Examples and use cases",
r"\bexamples?\b|\buse cases?\b|\btemplates?\b|\bideas\b|\bprojects?\b"),
("pricing", "Pricing and cost",
r"\bprices?\b|\bpricing\b|\bcosts?\b|\bfree\b|\bcheap\b"),
("security", "Security and risks",
r"\bsecur\w*\b|\brisks?\b|\bsafe\w*\b|\bvulnerab\w*\b|\bprivacy\b"),
]
INTENT_NAMES = dict((k, n) for k, n, _ in INTENTS)
INTENT_NAMES["general"] = "General questions"
SUFFIX = {"k": 1e3, "m": 1e6, "b": 1e9}
KPI_NAMES = {"topic size": "topic_size", "your citations": "your_citations",
"queries": "queries", "sites cited": "sites_cited"}
# --------------------------------------------------------------------------
# reading the download
# --------------------------------------------------------------------------
def parse_number(s):
s = s.strip().lower().replace(",", "").replace("~", "")
m = re.fullmatch(r"(\d+(?:\.\d+)?)\s*([kmb])?", s)
if not m:
return None
return float(m.group(1)) * SUFFIX.get(m.group(2) or "", 1)
def parse_range(value):
"""'10K-50K' -> (10000, 50000); '1,234' -> (1234, 1234); '<1K' ->
(0, 1000); '50K+' -> (50000, 50000); anything else -> None."""
if value is None:
return None
s = str(value).strip().replace("–", "-").replace("—", "-")
s = s.replace(" to ", "-").replace(" ", "")
if not s or s in ("-", "--"):
return None
m = re.fullmatch(r"<(.+)", s)
if m:
hi = parse_number(m.group(1))
return (0.0, hi) if hi is not None else None
if "-" in s.strip("-"):
lo, _, hi = s.partition("-")
lo, hi = parse_number(lo), parse_number(hi)
if lo is not None and hi is not None:
return (min(lo, hi), max(lo, hi))
return None
n = parse_number(s.lstrip(">").rstrip("+"))
return (n, n) if n is not None else None
def midpoint(rng):
lo, hi = rng
if lo > 0:
return math.sqrt(lo * hi)
return hi / 2.0
def section_of(text):
t = (text or "").lower()
if "opportun" in t:
return "opportunity"
if "grounding" in t or "your" in t or "cited" in t:
return "grounding"
return None
def read_rows(path):
for enc in ("utf-8-sig", "cp1252", "latin-1"):
try:
with open(path, newline="", encoding=enc) as fh:
return list(csv.reader(fh))
except UnicodeDecodeError:
continue
return []
def is_header(cells):
low = [c.strip().lower() for c in cells]
return (any("query" in c for c in low) and
any("citation" in c for c in low))
def load(paths, warn=print):
"""Return (rows, kpis). Each row: query, section, low, high, mid, raw,
topic, file."""
rows, kpis = [], {}
for path in paths:
qcol = vcol = scol = tcol = None
current = None
for cells in read_rows(path):
cells = [c.strip() for c in cells]
if not any(cells):
continue
first = cells[0].lower().rstrip(":")
if first in KPI_NAMES and len(cells) > 1 and not is_header(cells):
kpis[KPI_NAMES[first]] = cells[1]
continue
if is_header(cells):
low = [c.lower() for c in cells]
qcol = next(i for i, c in enumerate(low) if "query" in c)
vcol = next(i for i, c in enumerate(low) if "citation" in c)
scol = next((i for i, c in enumerate(low)
if c in ("section", "type", "table", "query type")),
None)
tcol = next((i for i, c in enumerate(low) if c == "topic"),
None)
current = section_of(cells[qcol]) or section_of(cells[vcol])
continue
if qcol is None or qcol >= len(cells) or not cells[qcol]:
continue
sec = current
if scol is not None and scol < len(cells):
sec = section_of(cells[scol]) or sec
raw = cells[vcol] if vcol is not None and vcol < len(cells) else ""
rng = parse_range(raw)
rows.append({
"query": cells[qcol], "section": sec or "unknown",
"low": rng[0] if rng else 0.0, "high": rng[1] if rng else 0.0,
"mid": midpoint(rng) if rng else 0.0, "raw": raw,
"topic": cells[tcol] if tcol is not None and tcol < len(cells)
else "", "file": path})
unknown = sum(r["section"] == "unknown" for r in rows)
if unknown:
warn(" WARN {} row(s) had no section; they're treated as "
"opportunity queries. Check the file's headings.".format(unknown))
for r in rows:
if r["section"] == "unknown":
r["section"] = "opportunity"
return rows, kpis
# --------------------------------------------------------------------------
# themes, brands, clustering
# --------------------------------------------------------------------------
def parse_themes(spec):
"""'vibe coding=vibe cod,vibecod|mcp=mcp' -> [(name, [patterns])]."""
themes = []
for part in [p for p in (spec or "").split("|") if p.strip()]:
name, _, pats = part.partition("=")
pats = [x.strip().lower() for x in (pats or name).split(",")
if x.strip()]
themes.append((name.strip(), pats))
return themes
def matches(query, patterns):
q = query.lower()
for p in patterns:
if re.fullmatch(r"[a-z0-9]+", p) and len(p) <= 4:
if re.search(r"\b" + re.escape(p) + r"\b", q):
return True
elif p in q:
return True
return False
def theme_of(query, themes):
for name, pats in themes:
if matches(query, pats):
return name
return None
def brand_flag(query, brands):
q = query.lower()
return next((b for b in brands if re.search(
r"\b" + re.escape(b.lower()) + r"\b", q)), "")
def tokens(query):
out = []
for t in re.findall(r"[a-z0-9]+", query.lower()):
if t in STOP or len(t) < 3 or t.isdigit():
continue
if len(t) > 4 and t.endswith("s") and not t.endswith("ss"):
t = t[:-1]
out.append(t)
return out
def auto_cluster(queries, min_size=2):
"""Group queries by their most common meaningful word."""
df = Counter(t for q in queries for t in set(tokens(q)))
groups = defaultdict(list)
for q in queries:
ts = sorted(set(tokens(q)), key=lambda t: (-df[t], t))
groups[ts[0] if ts and df[ts[0]] >= min_size else "other"].append(q)
small = [k for k, v in groups.items() if len(v) < min_size and k != "other"]
for k in small:
groups["other"].extend(groups.pop(k))
return groups
def intent_of(query):
q = query.lower()
for key, _, rx in INTENTS:
if re.search(rx, q):
return key
return "general"
def slugify(text, words=6):
parts = re.findall(r"[a-z0-9]+", text.lower())[:words]
return "-".join(parts)
# --------------------------------------------------------------------------
# commands
# --------------------------------------------------------------------------
def fmt(n):
n = float(n)
for div, suf in ((1e9, "B"), (1e6, "M"), (1e3, "K")):
if n >= div:
v = n / div
return ("{:.1f}".format(v).rstrip("0").rstrip(".")) + suf
return "{:.0f}".format(n)
def write(path, rows, fields):
with open(path, "w", newline="", encoding="utf-8") as fh:
w = csv.DictWriter(fh, fieldnames=fields, extrasaction="ignore")
w.writeheader()
w.writerows(rows)
def split_list(s):
return [x.strip() for x in re.split(r"[|;]", s or "") if x.strip()]
def cmd_summary(a):
rows, kpis = load(a.files)
opp = [r for r in rows if r["section"] == "opportunity"]
own = [r for r in rows if r["section"] == "grounding"]
print("SUMMARY {} row(s) from {} file(s)".format(len(rows), len(a.files)))
for k, label in (("topic_size", "Topic size"), ("your_citations",
"Your citations"), ("queries", "Queries"),
("sites_cited", "Sites cited")):
if k in kpis:
print(" {:15} {}".format(label, kpis[k]))
print(" Your grounding queries: {:,} ({} citations)".format(
len(own), fmt(sum(r["mid"] for r in own))))
print(" Opportunity queries: {:,} (about {} citations, range {} to "
"{})".format(len(opp), fmt(sum(r["mid"] for r in opp)),
fmt(sum(r["low"] for r in opp)),
fmt(sum(r["high"] for r in opp))))
unparsed = [r for r in rows if r["raw"] and parse_range(r["raw"]) is None]
if unparsed:
print(" WARN {} value(s) couldn't be read, e.g. '{}'".format(
len(unparsed), unparsed[0]["raw"]))
buckets = Counter(r["raw"] for r in opp if r["raw"])
if buckets:
print("\n Opportunity queries by citation range:")
for raw, n in sorted(buckets.items(), key=lambda kv: -(
parse_range(kv[0]) or (0, 0))[1]):
print(" {:>12} {:,}".format(raw, n))
print("\n Top opportunity queries:")
for r in sorted(opp, key=lambda r: (-r["mid"], r["query"]))[:a.show]:
print(" {:>10} {}".format(r["raw"], r["query"]))
brands = split_list(a.exclude)
if brands:
flagged = [r for r in opp if brand_flag(r["query"], brands)]
print("\n {} opportunity quer{} name an excluded brand and will be "
"left out of clusters and plans".format(
len(flagged), "y" if len(flagged) == 1 else "ies"))
return 0
def score_clusters(rows, themes, brands):
opp = [r for r in rows if r["section"] == "opportunity"
and not brand_flag(r["query"], brands)]
own = [r for r in rows if r["section"] == "grounding"]
groups = defaultdict(list)
loose = []
for r in opp:
t = theme_of(r["query"], themes)
if t:
groups[("service", t)].append(r)
else:
loose.append(r)
by_q = {r["query"]: r for r in loose}
for label, qs in auto_cluster([r["query"] for r in loose]).items():
groups[("auto", label)] += [by_q[q] for q in qs]
total = sum(r["mid"] for r in opp) or 1.0
out = []
for (kind, name), rs in groups.items():
mine = [r for r in own if (theme_of(r["query"], themes) == name
if kind == "service" else
name in tokens(r["query"]))]
opp_mid = sum(r["mid"] for r in rs)
own_mid = sum(r["mid"] for r in mine)
out.append({
"cluster": name, "kind": "your service" if kind == "service"
else "auto", "opportunity_queries": len(rs),
"opportunity_mid": opp_mid,
"opportunity_low": sum(r["low"] for r in rs),
"opportunity_high": sum(r["high"] for r in rs),
"share_of_opportunity": opp_mid / total,
"your_queries": len(mine), "your_citations": own_mid,
"gap": opp_mid / (opp_mid + own_mid) if opp_mid + own_mid else 0,
"top_query": max(rs, key=lambda r: r["mid"])["query"] if rs else "",
"top_range": max(rs, key=lambda r: r["mid"])["raw"] if rs else ""})
out.sort(key=lambda c: (c["cluster"] == "other", -c["opportunity_mid"]))
return out
def cmd_clusters(a):
rows, _ = load(a.files)
themes = parse_themes(a.themes)
clusters = score_clusters(rows, themes, split_list(a.exclude))
print("CLUSTERS {} opportunity theme(s)".format(len(clusters)))
print(" {:20} {:>13} {:>7} {:>11} {:>6} {:>8} {}".format(
"cluster", "kind", "queries", "citations*", "share", "you", "top query"))
for c in clusters[:a.show]:
print(" {:20} {:>13} {:>7,} {:>11} {:>6.0%} {:>8} {} ({})".format(
c["cluster"][:20], c["kind"], c["opportunity_queries"],
fmt(c["opportunity_mid"]), c["share_of_opportunity"],
fmt(c["your_citations"]) if c["your_queries"] else "-",
c["top_query"][:40], c["top_range"]))
print(" * central estimate from Bing's citation ranges; 'you' = your "
"current citations in that cluster")
svc = [c for c in clusters if c["kind"] == "your service"]
if svc:
best = max(svc, key=lambda c: c["opportunity_mid"])
print("\n Biggest opportunity among your services: {} ({:.0%} of the "
"opportunity in this download)".format(
best["cluster"], best["share_of_opportunity"]))
if a.out:
write(a.out, clusters, ["cluster", "kind", "opportunity_queries",
"opportunity_low", "opportunity_mid",
"opportunity_high", "share_of_opportunity",
"your_queries", "your_citations", "gap",
"top_query", "top_range"])
print(" wrote", a.out)
return 0
def build_plan(rows, theme, brands, base="", per_page=8):
name, pats = theme
opp = [r for r in rows if r["section"] == "opportunity"
and matches(r["query"], pats) and not brand_flag(r["query"], brands)]
own = [r for r in rows if r["section"] == "grounding"
and matches(r["query"], pats)]
if not opp:
return None, own
root = "/" + (base.strip("/") + "/" if base.strip("/") else "") + \
slugify(name) + "/"
pillar = sorted(opp, key=lambda r: (-r["mid"], len(r["query"])))[0]
by_intent = defaultdict(list)
for r in opp:
if r is not pillar:
by_intent[intent_of(r["query"])].append(r)
pages = [{"page": "pillar", "intent": "overview", "url": root,
"main_query": pillar["query"], "main_range": pillar["raw"],
"queries": 1 + sum(len(v) for v in by_intent.values()),
"citations_mid": sum(r["mid"] for r in opp),
"answers": "; ".join(r["query"] for r in sorted(
opp, key=lambda r: -r["mid"])[1:per_page + 1])}]
for key, rs in sorted(by_intent.items(),
key=lambda kv: -sum(r["mid"] for r in kv[1])):
rs.sort(key=lambda r: (-r["mid"], r["query"]))
main = rs[0]
pages.append({
"page": "supporting", "intent": INTENT_NAMES[key],
"url": root + slugify(main["query"]) + "/",
"main_query": main["query"], "main_range": main["raw"],
"queries": len(rs), "citations_mid": sum(r["mid"] for r in rs),
"answers": "; ".join(r["query"] for r in rs[1:per_page + 1])})
return pages, own
def cmd_plan(a):
rows, _ = load(a.files)
themes = parse_themes(a.theme)
if not themes:
print(" Give a theme, e.g. --theme \"vibe coding=vibe cod,vibecod\"")
return 2
pages, own = build_plan(rows, themes[0], split_list(a.exclude), a.base,
a.per_page)
if not pages:
print("PLAN no opportunity queries match '{}'. Check the patterns "
"after '='.".format(themes[0][0]))
return 1
print("PLAN content cluster for \"{}\": {} page(s), {:,} opportunity "
"queries, about {} citations".format(
themes[0][0], len(pages), pages[0]["queries"],
fmt(pages[0]["citations_mid"])))
for p in pages:
print("\n [{}] {} {}".format(p["page"], p["intent"], p["url"]))
print(" main query: {} ({})".format(p["main_query"], p["main_range"]))
print(" {:,} quer{}, about {} citations".format(
p["queries"], "y" if p["queries"] == 1 else "ies",
fmt(p["citations_mid"])))
if p["answers"]:
print(" also answer: " + p["answers"][:160] + (
"..." if len(p["answers"]) > 160 else ""))
if own:
print("\n Already cited for {} quer{} in this theme: link the new "
"pages to and from the pages cited for them:".format(
len(own), "y" if len(own) == 1 else "ies"))
for r in sorted(own, key=lambda r: -r["mid"])[:a.show]:
print(" {:>8} {}".format(r["raw"], r["query"]))
if a.out:
write(a.out, pages, ["page", "intent", "url", "main_query",
"main_range", "queries", "citations_mid",
"answers"])
print("\n wrote", a.out)
return 0
def compare(before, after, pats=None):
"""Which opportunity queries became grounding queries between two
downloads, and which grounding queries were lost."""
norm = lambda q: re.sub(r"\s+", " ", q.lower()).strip()
keep = (lambda q: matches(q, pats)) if pats else (lambda q: True)
b = {norm(r["query"]): r for r in before if keep(r["query"])}
a = {norm(r["query"]): r for r in after if keep(r["query"])}
out = []
for q in sorted(set(b) | set(a)):
rb, ra = b.get(q), a.get(q)
sb = rb["section"] if rb else "absent"
sa = ra["section"] if ra else "absent"
if sb == "opportunity" and sa == "grounding":
status = "won"
elif sb == "absent" and sa == "grounding":
status = "new citation"
elif sb == "grounding" and sa != "grounding":
status = "lost"
elif sb == "grounding" and sa == "grounding":
status = "kept"
elif sa == "opportunity" and sb == "absent":
status = "new opportunity"
elif sa == "opportunity":
status = "still open"
else:
status = "gone"
out.append({"query": (ra or rb)["query"], "status": status,
"before": rb["raw"] if rb else "", "before_section": sb,
"after": ra["raw"] if ra else "", "after_section": sa,
"before_mid": rb["mid"] if rb else 0.0,
"after_mid": ra["mid"] if ra else 0.0})
return out
def cmd_compare(a):
before, _ = load(split_list(a.before) or [a.before])
after, _ = load(split_list(a.after) or [a.after])
themes = parse_themes(a.theme)
pats = themes[0][1] if themes else None
rows = compare(before, after, pats)
st = Counter(r["status"] for r in rows)
own_b = sum(r["before_mid"] for r in rows if r["before_section"] ==
"grounding")
own_a = sum(r["after_mid"] for r in rows if r["after_section"] ==
"grounding")
print("COMPARE {} quer{}{}".format(
len(rows), "y" if len(rows) == 1 else "ies",
" in \"{}\"".format(themes[0][0]) if themes else ""))
for k in ("won", "new citation", "kept", "lost", "still open",
"new opportunity", "gone"):
if st[k]:
print(" {:16} {:,}".format(k, st[k]))
print(" your citations {} -> {}".format(fmt(own_b), fmt(own_a)))
won = [r for r in rows if r["status"] in ("won", "new citation")]
if won:
print("\n Now cited for:")
for r in sorted(won, key=lambda r: -r["after_mid"])[:a.show]:
print(" {:>8} {} (was {})".format(
r["after"], r["query"], r["before"] or "not listed"))
lost = [r for r in rows if r["status"] == "lost"]
if lost:
print("\n No longer cited for:")
for r in sorted(lost, key=lambda r: -r["before_mid"])[:a.show]:
print(" {:>8} {}".format(r["before"], r["query"]))
if a.out:
write(a.out, rows, ["query", "status", "before_section", "before",
"after_section", "after"])
print("\n wrote", a.out)
return 0
def main(argv=None):
ap = argparse.ArgumentParser(description="Bing Topics Portfolio "
"download to AI SEO content plan.")
sub = ap.add_subparsers(dest="cmd", required=True)
common = argparse.ArgumentParser(add_help=False)
common.add_argument("files", nargs="+", help="Topics Portfolio CSV(s)")
common.add_argument("--exclude", default="",
help="brand names to leave out, separated by |")
common.add_argument("--show", type=int, default=15)
s = sub.add_parser("summary", parents=[common],
help="what's in the download")
s.set_defaults(fn=cmd_summary)
s = sub.add_parser("clusters", parents=[common],
help="rank opportunity themes")
s.add_argument("--themes", default="",
help="your services: 'name=pattern,pattern|name=pattern'")
s.add_argument("--out", default="topic_clusters.csv")
s.set_defaults(fn=cmd_clusters)
s = sub.add_parser("plan", parents=[common],
help="content cluster plan for one theme")
s.add_argument("--theme", required=True,
help="'name=pattern,pattern', e.g. 'vibe coding=vibe cod,"
"vibecod'")
s.add_argument("--base", default="",
help="folder for the URLs, e.g. 'learn'")
s.add_argument("--per-page", type=int, default=8)
s.add_argument("--out", default="content_cluster_plan.csv")
s.set_defaults(fn=cmd_plan)
s = sub.add_parser("compare", help="what changed between two downloads")
s.add_argument("before", help="earlier download(s), separated by |")
s.add_argument("after", help="later download(s), separated by |")
s.add_argument("--theme", default="",
help="only queries in this theme, 'name=pattern,pattern'")
s.add_argument("--show", type=int, default=15)
s.add_argument("--out", default="topic_changes.csv")
s.set_defaults(fn=cmd_compare)
a = ap.parse_args(argv)
return a.fn(a)
if __name__ == "__main__":
sys.exit(main())
8. What We’re Watching Next in AI Search
In my view, Bing is currently providing SEO teams with some of the most useful reporting on AI citations.
Topics Portfolio is particularly interesting because it gives us information about opportunities, not just existing visibility.
However, there are several developments I would continue monitoring.
1. The Results of This Content Experiment
The first thing I want to understand is whether the vibe coding content cluster turns opportunity queries into actual grounding queries for the client.
September 2026 provides the baseline.
I will compare the October, November and December downloads to see:
- Whether the client starts receiving citations for new queries.
- Whether existing citations increase.
- Which supporting pages receive the most citations.
- Whether new content contributes to wider visibility across the Technology topic.
The objective is to measure actual changes rather than simply report the number of articles published.
2. Does Bing’s Topic Data Reflect Demand in Other AI Assistants?
This is another interesting question.
If topics that attract citation activity in Copilot also attract attention in ChatGPT, Gemini, Perplexity and Claude, Topics Portfolio could become a useful planning tool for AI search more broadly.
I’m testing this by tracking the same questions across those assistants.
It will be interesting to see whether the opportunities identified through Bing translate into improved visibility elsewhere.
3. When Will Topics Portfolio Leave Beta?
Bing has not announced a date for the feature to leave beta.
I would like to see more detailed information about its export formats, topic classification and the way citation opportunities are calculated.
It will also be interesting to see how Bing improves topic labels as more website owners provide feedback.
4. Will Bing Show Which Competitors Receive Citations?
One of the current limitations is that opportunity queries show where other websites are cited without always identifying exactly which websites they are.
That makes the competitor analysis less direct.
If Bing introduces reporting showing the individual websites cited for each opportunity query, it would make content gap analysis much more useful.
SEO teams could identify exactly which competitors and publications are getting visibility for particular questions. SD SEO Suite provides data for this by the way.
5. Will Google Introduce Topic-Level AI Search Reporting?
Google’s Generative AI Performance report currently shows pages appearing in AI Overviews and AI Mode.
However, it does not provide the same topic-level and grounding-query opportunity information available through Bing’s Topics Portfolio.
More detailed topic reporting from Google would give SEO teams another way to plan content around AI search demand.
It would also help businesses understand how their content performs across Google’s AI experiences.
6. How Quickly Will Vibe Coding Search Behaviour Change?
Vibe coding is a relatively new and fast-moving subject.
The terminology people use to research AI-assisted development is likely to change as new tools, platforms and working methods become more popular.
I will use the monthly comparison process to identify which queries are increasing, which are becoming less relevant and whether the content cluster needs to change.
A good AI SEO strategy should not remain fixed when the questions people ask are changing.
9. About Szymaniak Digital
Szymaniak Digital is a UK-based enterprise AI SEO consultancy led by Konrad Szymaniak.
We help B2B SaaS and enterprise marketing teams improve their visibility across Google and AI assistants, using search data to identify opportunities, develop content strategies and support customer acquisition.
Our approach combines traditional SEO, AI search measurement, technical SEO and content strategy.
We also run experiments using tools such as Bing Webmaster Tools to understand how AI search is changing and how businesses can use the available data to make better marketing decisions.
We’ve developed SD SEO Suite to help businesses monitor their visibility across ChatGPT, Gemini, Perplexity and Claude, alongside Google rankings and AI Overview citations.
Pro includes a 30-day free trial, while custom plans provide additional projects, daily AI tracking and team access.
For businesses that want to go beyond tracking, our consultancy team works with in-house marketing departments on AI search strategy, content and technical SEO.
The objective is to help businesses understand where AI search opportunities exist, create content that answers the questions their customers ask and improve their chances of being found, cited and recommended across AI assistants.

