A single Google query can show which domains are visible for one phrasing at one moment. It cannot establish which sources dominate an entire topic.
To answer the broader question, collect a defined group of related queries, convert result URLs into registrable domains, and measure how consistently each domain appears across the query set.
Begin with a topic, not a favorite keyword
Write the query corpus before collecting results. A useful corpus covers distinct ways someone might approach the topic:
- Basic definitions
- Practical how-to questions
- Costs or requirements
- Problems and troubleshooting
- Products, services, or institutions involved
- Local-language terminology where relevant
For a topic such as home heat pumps, one query is too narrow. A small corpus might include:
how heat pumps work
heat pump running costs
heat pump cold weather performance
heat pump installation requirements
air source vs ground source heat pump
heat pump maintenance
Do not add or remove queries after seeing which domains perform well. If the corpus changes, create a new version.
Keep the search context consistent
Use the same:
- Country and language settings
- Number of pages or offsets
- Result-unit definition
- Collection window
- URL-normalization rules
If the research compares countries or languages, treat each combination as a separate cohort before combining anything.
Extract registrable domains correctly
The hostname is easy to obtain:
const hostname = new URL(result.url).hostname;
The registrable domain is harder. Taking the last two labels fails for structures such as example.co.uk. Use a maintained implementation of the Public Suffix List when grouping subdomains under their registrable domain.
Keep both values:
| Field | Example |
|---|---|
| Hostname | research.example.co.uk |
| Registrable domain | example.co.uk |
Hostname-level analysis distinguishes sections or platforms. Registrable-domain analysis describes the broader publisher.
Use query coverage as the primary metric
Query coverage asks how many valid queries contained at least one result from a domain:
query coverage = queries containing the domain / valid queries
If a domain appears five times for one query and nowhere else, it has 10% coverage in a ten-query corpus—not 50%.
This prevents a page with many sitelinks or repeated URLs from dominating the analysis.
Add complementary measurements
Unique URL share
Count the domain's unique normalized URLs as a share of all unique URLs. This shows whether the domain contributes many different pages.
First visible position
For each query, record the first defined position at which the domain appears. If the response contains visible blocks rather than classified organic results, call this a visible-block position rather than an organic rank.
Top-k coverage
Measure the share of queries where the domain appears within the first k units under your declared result definition.
Cohort coverage
Calculate coverage separately for definitions, costs, troubleshooting, and other query groups. One domain may dominate introductory explanations while another dominates technical support.
Concentration
Report how much total query coverage belongs to the five or ten most visible domains. This reveals whether the topic is concentrated among a few publishers or dispersed across many.
Build a domain summary
Assume the derived rows contain one row per query-domain pair:
function median(values) {
const sorted = [...values].sort((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);
return sorted.length % 2
? sorted[middle]
: (sorted[middle - 1] + sorted[middle]) / 2;
}
function summarizeDomains(rows, validQueryIds) {
const byDomain = new Map();
for (const row of rows) {
if (!byDomain.has(row.domain)) {
byDomain.set(row.domain, {
queries: new Set(),
urls: new Set(),
firstPositions: [],
});
}
const summary = byDomain.get(row.domain);
summary.queries.add(row.queryId);
summary.urls.add(row.normalizedUrl);
summary.firstPositions.push(row.firstPosition);
}
return [...byDomain.entries()]
.map(([domain, summary]) => ({
domain,
queryCoverage: summary.queries.size / validQueryIds.size,
queryCount: summary.queries.size,
uniqueUrlCount: summary.urls.size,
medianFirstPosition: median(summary.firstPositions),
}))
.sort((a, b) => b.queryCoverage - a.queryCoverage);
}
Deduplicate each domain within each query before calculating coverage. Otherwise one query can contribute multiple votes.
Make the table inspectable
A useful output contains more than a leaderboard:
| Domain | Query coverage | Unique URLs | Median first position | Strongest cohort |
|---|---|---|---|---|
| example.org | 72% | 18 | 3 | Definitions |
| example.net | 48% | 11 | 5 | Troubleshooting |
Link each aggregate back to the query-level rows. Readers should be able to see which exact queries produced a domain's coverage.
Avoid claims the data cannot support
Frequent visibility does not establish:
- Accuracy
- Trustworthiness
- Popularity among the public
- Market share
- Causal influence
- Coverage of the complete web
It establishes visibility within the specified Google query corpus, locale, depth, and collection window.
That narrower claim is still useful. It can reveal which publishers repeatedly mediate a topic and which query cohorts produce a more diverse source landscape.
Reserp returns ordered destination URLs and nested result blocks for each submitted Google Search URL. See the Google Search API documentation for the response format used to create the query-level rows.