Competitive analysis template with a data source for every row
A competitive analysis template is a fixed grid of questions you answer the same way for every competitor, with the source and date of each answer written next to it. The source column is what makes it useful: without it, a template mixes last year’s pricing, a salesperson’s impression and a ranking checked this morning, and nobody can tell which is which. Copy the tables below, keep one file per competitor plus a summary, and refresh each section on the cadence at the end.
Source labels used throughout:
- Their site: the competitor’s own pages (pricing, product, docs, changelog, press).
- Public pages: third-party pages such as review sites, app marketplaces, filings, job boards.
- API: this API’s endpoint and response field, collected on a schedule.
- Manual: research that needs a person: demos, trials, sales call notes, win/loss interviews.
0. Header
| Field | Value | Source |
|---|---|---|
| Competitor | Manual | |
| Domains and aliases | Main site, blog, docs, country sites, product names | Their site |
| Segment they lead with | Their site (homepage headline) | |
| Analyst | Manual | |
| Last full review | YYYY-MM-DD | Manual |
| Markets covered in this file | e.g. US, UK, DE | Manual |
Fill in the domains and aliases row first. Every automated row matches on it, so a competitor whose docs live on a separate domain will look invisible in search unless that domain is listed.
1. Positioning
| Question | Their answer | Evidence | Source | Checked |
|---|---|---|---|---|
| Who do they say it is for? | Homepage headline, quote | Their site | ||
| Main problem they claim to solve | Their site | |||
| Top three differentiators they claim | Their site | |||
| Named competitors on their comparison pages | URL | Their site | ||
| Category term they use for themselves | Their site | |||
| How reviewers summarise them | Review site excerpts | Public pages | ||
| How Google’s knowledge panel describes them | knowledgeGraph.type, knowledgeGraph.attributes[] |
Query: brand name | API: Google Search | |
| How ChatGPT describes them when asked directly | First two sentences of text |
Prompt: “What is |
API: ChatGPT |
The last two rows show the gap between what a company says and what search engines and assistants repeat. When ChatGPT’s description of a rival lags their current positioning, start with the pages it cites (sources[].url) to see which outdated descriptions it is drawing on.
2. Pricing and packaging
| Question | Their answer | Source | Checked |
|---|---|---|---|
| Is pricing published? | Yes / No / Partly | Their site | |
| Pricing model (per seat, usage, flat, tiered) | Their site | ||
| Entry plan price and limits | Their site | ||
| Most prominent plan | Their site | ||
| Free plan or trial, and its limits | Their site | ||
| Annual discount | Their site | ||
| Add-ons sold separately | Their site | ||
| Prices seen in sales cycles, if different | Manual (CRM, sales notes) | ||
| Recent price change and date | Their site, news via API: Google News newsResults[] |
||
| Price shown in shopping results (physical products) | shoppingCards[].price.raw, ads[].price.raw by store |
API: Google Search |
Rule: if pricing is not published, write “Not published” rather than a remembered number. A price from a sales call goes in its own row with the date and deal context.
3. Product
| Capability | Us (0–3) | Them (0–3) | Evidence | Source | Checked |
|---|---|---|---|---|---|
| Capability A | Docs URL | Their site | |||
| Capability B | Changelog entry | Their site | |||
| Integrations that matter to our buyers | Integration directory | Their site, public pages (marketplaces) | |||
| Security and compliance claims | Trust page | Their site | |||
| API and developer experience | Docs, trial | Their site, manual | |||
| Onboarding time claimed | Their site, manual (trial) | ||||
| Recent launches (last 90 days) | Changelog, news | Their site, API: Google News |
Score only what you can link. A capability that exists “according to a prospect” is a lead to verify, not a 2.
4. Organic search presence
Fill from a fixed keyword set, tagged by group, for each market in the header. All rows below come from POST /v1/monitor/google (task type GOOGLE).
| Metric | Definition | Field | Value | Checked |
|---|---|---|---|---|
| Category keywords in top 10 | Keywords where any of their domains ranks ≤ 10 ÷ keywords tracked | organicResults[].link, position, page |
||
| Comparison keywords in top 10 | Same, over “vs” and “alternatives” keywords | organicResults[].link |
||
| Share of SERP (organic) | Their top-10 slots ÷ all top-10 slots across the set | organicResults[].link |
||
| Keywords where they outrank us | Their best rank < our best rank | organicResults[] |
||
| Pages that rank most often | Top 5 URLs by keyword count | organicResults[].link |
||
| Questions they own in People Also Ask | PAA items whose link is their domain |
peopleAlsoAsk[].link |
||
| Forum threads about them | Threads in the discussion module on their brand query | peopleAreSaying[].link |
||
| Local pack presence (local businesses) | Keywords where they appear in the pack, desktop | localResults[].title with location |
Not in this section: traffic, search volume and backlinks. The API does not return them. If you need them, add rows sourced from your analytics or a keyword tool, and label those rows as estimates.
5. AI answer presence
Two sources: the Google AI Overview on your keyword set, and assistant answers on a fixed prompt set.
| Metric | Definition | Endpoint and field | Value | Checked |
|---|---|---|---|---|
| AI Overview citation rate | Keywords whose AI Overview cites their domain ÷ keywords with an AI Overview | Google Search + include.aioverview: aioverview.citationPills[].domain |
||
| AI Overview presence on set | Keywords with non-null aioverview ÷ keywords |
Google Search: aioverview |
||
| ChatGPT mention rate | Answers naming them ÷ answers sampled | ChatGPT: entities[].name, text |
||
| ChatGPT first-named rate | Answers naming them first ÷ answers naming any tracked brand | ChatGPT: text |
||
| ChatGPT citation share | Their URLs in sources[] ÷ all source URLs |
ChatGPT: sources[].url |
||
| Other assistants mention rate | Answers whose text names them ÷ answers sampled |
Gemini, Copilot, Grok: text |
||
| Most cited page of theirs | URL cited most across engines | sources[].url |
entities[] exists only on ChatGPT; for other engines, match brand names and aliases in text. Sample each prompt several times per period, because answers vary between runs. AI share of voice has the full metric set.
6. News and announcements
News rows use the Google News endpoint.
| Question | Value | Source | Checked |
|---|---|---|---|
| Articles in the last 30 days | Count of distinct link |
API: Google News newsResults[].link |
|
| Publishers covering them | Distinct source values |
API: Google News newsResults[].source |
|
| Funding, acquisition, leadership news | Headlines with date | API: Google News newsResults[].title, date; confirm on their press page |
|
| Launches announced | Their site (press, changelog) | ||
| Hiring signals | Roles and teams they hire for | Public pages (their careers page, job boards) | |
| Customer wins they publicise | Their site (case studies) |
Headlines are leads. Confirm material facts on the competitor’s own press page or filings before they go in a board summary.
7. Paid search and ads
| Metric | Definition | Endpoint and field | Value | Checked |
|---|---|---|---|---|
| Bids on our brand | Share of samples on our brand query with their ad | Google Search: ads[].domain |
||
| Bids on their own brand | Share of samples on their brand query with their ad | Google Search: ads[].domain |
||
| Category ad presence | Keywords × samples with their ad ÷ keywords × samples | Google Search: ads[].domain |
||
| Top-of-page share | Their blockPosition: top ads ÷ all top ads observed |
Google Search: ads[].blockPosition |
||
| Active ad messages | Distinct title + description pairs this period | Google Search: ads[].title, ads[].description |
||
| Shopping ads by store | Samples with a sponsored card from their store | Google Search: ads[].store where type is SHOPPING_CARD |
||
| Ads in ChatGPT answers | Prompts where their brand ad was rendered | ChatGPT + include.ads: ads[].brand.name, ads[].rendered |
||
| Creatives across Google surfaces | What they ran, by region | Manual: Google’s Ads Transparency Center |
Spend, bids and click data are not observable in any of these. Report presence rates, not “they spend more”.
Filling the automated rows
One batch per run covers sections 4, 5 and 7 for a keyword and prompt set. A minimal TypeScript submitter with fetch:
const API = "https://api.answerline.dev";
const headers = { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" };
const webhook = { url: "https://intel.example.com/hooks/template" };
const day = new Date().toISOString().slice(0, 10);
type Kw = { id: string; text: string; market: string; aio: boolean };
type Prompt = { id: string; text: string; market: string };
function tasks(keywords: Kw[], prompts: Prompt[], adSamples = 3) {
const out: object[] = [];
for (const k of keywords) {
for (let s = 0; s < adSamples; s++) {
out.push({
taskType: "GOOGLE",
idempotencyKey: `tpl:${k.id}:${k.market}:${day}:${s}`,
webhook,
payload: {
query: k.text,
country: k.market,
...(k.aio && s === 0 ? { include: { aioverview: { markdown: false } } } : {}),
},
});
}
}
for (const p of prompts) {
out.push({
taskType: "CHATGPT",
idempotencyKey: `tpl:${p.id}:${p.market}:${day}`,
webhook,
payload: { prompt: p.text, country: p.market },
});
}
return out;
}
async function submit(all: object[]) {
for (let i = 0; i < all.length; i += 500) {
const res = await fetch(`${API}/v1/async/task/batch`, {
method: "POST",
headers,
body: JSON.stringify(all.slice(i, i + 500)),
});
if (!res.ok) throw new Error(`batch ${i}: ${res.status}`);
const body = await res.json();
for (const r of body.results) if (!r.success) console.warn(r.index, r.error?.code);
}
}
Sample 0 carries the AI Overview and feeds organic rows; samples 1 and 2 only add ad observations. Verify webhook signatures before storing results (how). Competitor SEO tracking covers the storage model and change detection.
Cost
Credits per async task: Google Search 3, plus 2 with the AI Overview; ChatGPT 5; Google News 3. For 100 keywords with 3 samples per run (AI Overview on the first) and 30 ChatGPT prompts, run weekly: (100 × 5 + 200 × 3 + 30 × 5) × 4 = 5,000 credits a month. Adding 10 Google News queries weekly adds 120. See pricing.
Scoring
Score each competitor per dimension on 0 to 3, with written criteria so two analysts arrive at the same number.
| Dimension | 0 | 1 | 2 | 3 | Weight |
|---|---|---|---|---|---|
| Positioning overlap with us | Different buyer | Adjacent buyer | Same buyer, different use case | Same buyer and use case | 3 |
| Pricing pressure | Much more expensive | Somewhat more | Similar | Cheaper for the same job | 2 |
| Product parity on our top capabilities | Missing most | Covers some | Covers most | Covers all, plus extras | 3 |
| Organic search presence | Rarely top 10 | Under 20% of set | 20–50% of set | Over 50% of set | 2 |
| AI answer presence | Rarely named or cited | Named in some samples | Named in most samples | Named first in most samples | 2 |
| News momentum | No coverage | Occasional | Regular | Frequent, tier-one publishers | 1 |
| Paid search aggression | No ads observed | Own brand only | Category terms | Our brand terms | 1 |
Threat score = Σ(score × weight) ÷ Σ(3 × weight), from 0 to 1. The thresholds in the search and AI rows are examples; set yours after the first full run so the scale spreads competitors out.
Rules that keep scores honest:
- Every score links to the evidence row it came from.
- Two people score independently; differences over one point get discussed, not averaged.
- A score cannot change without a new evidence row and date.
- Report the threat score with the per-dimension scores beside it. A single number hides why.
Refresh cadence
| Section | Cadence | Trigger for an early refresh | Method |
|---|---|---|---|
| 0. Header | Quarterly | Rebrand, acquisition, new domain | Manual |
| 1. Positioning | Quarterly | Homepage or messaging change | Manual; API rows monthly |
| 2. Pricing | Monthly | Price-change news, sales reports | Manual check of their site |
| 3. Product | Monthly | Launch announcement | Manual, changelog review |
| 4. Organic search | Weekly | Core update, rival content push | API, scheduled batch |
| 5. AI answers | Weekly, repeated samples | Rival launch, pricing change | API, scheduled batch |
| 6. News | Daily to weekly | Funding or M&A rumours | API, scheduled batch; confirm manually |
| 7. Ads | Several samples per week, daily for brand terms | Seasonal campaigns | API, scheduled batch |
| Scoring | Monthly | Any section change of 2+ points | Manual |
Monitoring cadence goes deeper on how often AI answers need sampling. For which tools fill the manual rows, see competitive intelligence tools by job, and for 18 ready questions with their exact fields, competitive intelligence examples.
Pitfalls
- No dates. A template without a “checked” column rots silently.
- Mixing estimates and observations. Traffic and volume figures are modelled; rankings, ads and citations are observed. Label them differently.
- One sample of volatile rows. Ads and AI answers vary between requests. Use rates.
- Missing aliases. Unlisted domains and product names make a competitor look absent.
- Scoring from memory. If there is no evidence link, the cell stays empty.
The metric definitions behind sections 4, 5 and 7 are on the competitor analysis page. To start collecting, see the quickstart.
Questions
What should a competitive analysis template include?
Positioning, pricing and packaging, product capabilities, organic search presence, AI answer presence, news and announcements, and paid search. Each row should name where its value comes from and when it was last checked, so readers can tell observed facts from opinions.
How often should a competitive analysis be updated?
Match the refresh to how fast each section changes: search and ad rows weekly or more often, AI answer rows weekly with repeated samples, pricing and product monthly or on announcement, positioning quarterly.
How do I score competitors objectively?
Score each dimension on a fixed 0 to 3 scale with written criteria per level, weight dimensions by how much they affect your deals, and keep the evidence link next to every score. Two people scoring independently and reconciling reduces bias.
Which rows can be filled automatically?
Rows derived from Google results, Google News and AI answers can be filled from API responses: organic positions, ad presence, AI Overview citations, brands named in ChatGPT answers and news sources. Pricing, product depth and positioning still need reading the competitor's site and manual judgement.
Does the template need traffic or search volume data?
It helps for prioritising keywords, but those numbers come from other sources such as your analytics or a keyword tool. SERP and AI answer data show presence, not traffic or volume.