Local SEO. Search Console

Service Page Gap Analysis: Find the Pages Your Local Business Is Missing

0 clicksSix page-one pages, about 7,400 impressions
3,900Impressions on a service with no page of its own
2 pagesIndexed money pages earning zero impressions
15 queriesFor a service the business doesn’t sell

Written by Ram Kr Shukla, SEO and growth consultant

Quick answer

A service page gap analysis uses your own Search Console data to decide which service pages to build or fix next. Export 90 days of queries with their landing pages, filter to your country and strip out brand terms, then group the queries by service. Each group falls into one of four gaps: demand landing on the wrong page, demand with no page at all, page-one queries earning zero clicks, and services you sell that get no impressions. Check every cluster against what the business actually sells, rank the gaps by service value and intent, then build, retarget or link.

Most content gap guides start with a competitor tool. You enter three rival domains, pull every keyword they rank for, and build whatever’s missing. That works for an online store with thousands of products. For a roofer, a lawn company or a dental practice it mostly produces noise. Local competitors rarely publish much, and third-party tools estimate small local search volumes badly.

Your own Search Console holds better data. It shows the queries Google tested your site against, which page it picked for each one, where that page sat and whether anyone clicked. Read it properly and it’ll tell you which service page to build next. Sometimes it’ll tell you not to build one at all. This is the method I use in local SEO for US service businesses, shown with real numbers from two of them: a US roofing and exteriors contractor and a US lawn care and mosquito control company.

If you run an online store, the competitor version is in my e-commerce competitor gap analysis. This one is local, and it runs on first-party data only.

What a Service Page Gap Analysis Actually Finds

A gap is a mismatch between what people search for and the page you’ve got to answer it. On a local service site the mismatches come in four shapes. Each one needs a different fix, so name them before you open a spreadsheet.

1The wrong page holds the query

Searches for a service land on your homepage, a blog post or another service’s page. Google is showing the closest thing it could find, which usually means the right page doesn’t exist or isn’t clear enough.

2Demand with no page

A cluster of queries earns impressions, often on page two or three, and nothing on the site is built for it. This is the classic missing service page.

3Page one, zero clicks

You rank in the top ten and nobody clicks. Another page won’t help here. The snippet, the intent match or the map pack above you is the problem.

4Invisible money services

Services the business sells, sometimes with a perfectly good page, that earn no impressions at all. They hide well, because Search Console only reports what Google showed.

There’s a fifth case that looks like a gap and isn’t: demand for a service you don’t offer. I’ll come back to it. It nearly caught one of the two businesses in this post.

Four gaps hiding in your own dataThree inputs, one match, four kinds of gap. Illustrative.Search Consolequery and page, 90 daysXML sitemapevery live URLService listranked by the ownerMatch each query cluster to a page and a serviceWrong page holds the queryhomepage, blog post, other serviceBuild or retargetImpressions, no page at alla cluster with nowhere to landBuild the service pagePage one, zero clicksposition 10 or better, no clicksFix title, meta, profileMoney service, no impressionsindexed and ignored, or unbuiltLink it, then retitle itRamKrShukla.com
Three inputs, matched query by query, sort every service into one of four gaps. Each gap has a different fix. Illustrative.

The Three Inputs You Need

You need three lists. Two come from Google. The third comes from the owner, and it’s the one people skip.

1. A query and page export from Search Console

In the Performance report you want queries and pages together, so you can see which URL Google chose for each search. 90 days suits most local businesses. It’s long enough to smooth out a storm week or a seasonal spike, and short enough to reflect the site as it stands.

The interface caps you. Google’s deep dive on performance data says the most you can export from the UI is 1,000 rows, while the Search Analytics API and the Looker Studio connector go up to 50,000 rows per day per site per search type. Once you add the page dimension, rows multiply fast, because one query can show several of your URLs. Use the API, a Looker Studio table or a spreadsheet add-on that calls it.

Two quirks to know before you trust the totals. Anonymized queries (ones not issued by more than a few dozen users over two to three months) never appear in the tables, per the same Google post. On a local site that’s a big share. The roofing contractor had about 67,000 impressions in 28 days, and only about 4,600 of them resolved to named queries. Second, average position is the position of your topmost result, averaged across searches (see the Performance report help). A position of 3.4 tells you nothing about the map pack or the ads sitting above it.

2. Your sitemap, as a plain URL list

Pull every URL from your XML sitemap into a sheet. Google’s sitemap overview covers the format if you’ve never opened one. You’ll use it to spot pages that exist and never show up in the export. The roofing contractor’s sitemap held 168 URLs: 120 pages and 48 posts. A crawl adds word counts and inlinks, which helps later. The sitemap is enough to start.

3. The service list, ranked by the owner

Ask the owner, not the website, what the business sells. Then ask them to sort it into three bands: jobs they want more of, jobs they’ll take, and jobs they’d rather not do. No prices needed. A rough value band is enough to break ties later.

Website menus drift. Owners add services and never build pages. They stop offering something and the page lives on for years. The owner’s list is the only input that says what the business wants to sell this season.

Step 1: Clean the Export Before You Trust It

Raw Search Console data from a local site is padded with impressions that’ll never turn into a call. Strip them first, or every cluster looks bigger than it is.

Start with country. Add a country filter for the United States, then cut searches that name towns you don’t serve. The roofing contractor ranked nationally for generic repair terms. Its soffit and fascia page sat at position 1.0 for “fascia repair” with 878 impressions and one click, and it picked up the same search paired with towns in other states. Those impressions can’t convert. I’ve written up that pattern, and how to filter it out, in why local service sites rack up non-local impressions.

Device data tells the same story from another angle. The contractor’s desktop impressions were 45,472 at 0.20% CTR, against 19,530 on mobile at 0.52%. For a trades business that’s backwards, and it’s what national, research-style traffic looks like.

Then take brand out. Search Console’s advanced filtering lets you add a query filter, choose Custom (regex) and pick Doesn’t match regex. It uses RE2 syntax, matches anywhere in the query by default and ignores case. Something like this removes the brand name and its common misspellings:

(yourbrand|your brand|yourbrnd|yb roofing)

Brand was about 18% of the roofing contractor’s clicks. Real non-brand acquisition worked out at about 5.6 clicks a day. That’s the number to grow, and it’s the one this whole exercise is built around. There’s more on why in branded vs non-branded traffic.

Step 2: Group Queries by Service

Single queries are too small to act on. A local site might show 40 versions of one search: with the city, without it, with “near me”, with “company”, with “cost”. You decide pages at the service level, so cluster at the service level too.

Write one regex per service on the owner’s list. Add one more for anything that keeps appearing that the business doesn’t sell. Run each as a query filter, note total impressions, clicks and the position range, then switch to the Pages tab to see where that demand lands.

ServiceExample regexWhat it catches
Gutter guards(gutter guard|leaf guard|gutter cover|gutter screen)Product searches plus the “best” and “cost” variants
Attic insulation(insulat|blown.?in|attic fan)Install, removal, material and cost searches
Siding repairsiding.*(repair|fix|replac)Repair and replacement together, until the data says split them
Storm damage(hail|wind|storm).*(damage|repair)Insurance-adjacent searches that may need their own page

Expect overlap. A search like “attic insulation removal cost” fits two ideas. Put it where the searcher’s next step is. On the roofing site, removal searches went to a removal and replacement page, and cost searches went to a separate cost article that explains the factors instead of quoting prices.

Keep a catch-all column for queries no regex picks up, and read it once. That’s where the surprises live. The service the owner forgot to mention. Or the one they stopped offering two years ago.

Step 3: Map Each Cluster to the Page Google Chose

Filter to one cluster and open the Pages tab. A single matching service page holding most of the impressions is healthy. A homepage, a blog post or an unrelated service page holding them is a wrong-page gap. No URL that fits at all is a no-page gap.

Here’s what that looked like on the roofing contractor’s site, from a 90-day pull filtered to the US:

ClusterImpressionsPositionClicksWhere it landed
Commercial roofing, home-city variantsabout 3,90020 to 304One generic commercial page, no child pages
Attic insulationabout 1,4507 to 100One general insulation page
Windowsabout 76022 to 331A thin hub with no repair page
Siding repairabout 73025 to 270No dedicated page
Asphalt roof repair plus six suburb namesabout 4508 to 140Residential suburb pages
Skylightsabout 3664 to 130One page for install, repair and commercial
Hail and wind damageabout 3306 to 111A generic storm damage page
Gutter guardsabout 294, plus 910 on a cost post17 to 250Service page at 25.5, its blog twin at 8.5
Impressions with no page built for themRoofing contractor, 90 days, US only, approximate. Clicks in brackets.Commercial roofing3,900 (4 clicks)Attic insulation1,450 (0 clicks)Windows760 (1 click)Siding repair730 (0 clicks)Roof repair, 6 suburbs450 (0 clicks)Skylights366 (0 clicks)Hail and wind damage330 (1 click)Gutter guards294 (0 clicks)Illustrative. Anonymised client data.RamKrShukla.com
Eight service clusters with real impressions and no dedicated page. Red bars earned zero clicks. Illustrative. Anonymised client data.

Two rows show the wrong-page pattern clearly. Gutter guard searches split between a service page at position 25.5 and a cost blog post at 8.5. Google preferred the post, most likely because it answered the cost question those searchers were asking. A later re-pull found “asphalt roof repair near me” (274 impressions) landing on the flat roof page, while the asphalt shingle page only talked about installation. Neither problem shows up in a site crawl. Both jump out once you read query and page side by side.

When a blog post holds a money query, deleting the post is rarely the answer. Build or strengthen the service page, then link to it from the post using the query as anchor text. That routing is covered in when your blog outranks the page that makes money.

And sometimes the page you’d build already exists under another name. “Siding replacement” pulled 767 impressions at position 34.5 on the siding hub, and the obvious replacement URL returned a 404. Tempting. But the hub and four sibling siding pages already covered replacement, so the right move was to consolidate what was there.

Step 4: Pull Out Page-One Queries With Zero Clicks

This filter finds the biggest wins on most local sites, and it’s usually not a build job. In your export, filter to average position 10 or better, clicks equal to zero and impressions above a floor that suits your size. On a small local site, something like 50 impressions over 90 days keeps one-off searches out. The Performance table also accepts direct filters such as clicks below a number, if you’d rather stay in the interface.

On the roofing site, six location and trust pages sat on page one with zero clicks across 90 days:

Page typeImpressionsAvg positionClicks
Residential roofing, suburb A2,3646.50
Residential roofing, suburb B1,4927.20
Commercial roofing, suburb B1,2107.80
Storm damage roofing, suburb B1,0803.40
Roof repair, suburb B9323.80
Roof insurance claim guide3353.60
Total7,4133.4 to 7.80

That was the single biggest opportunity on the site, and it needed no new content at all. A re-pull 56 days later still showed zero clicks on about 6,600 impressions at positions 3 to 7, because the title and meta changes hadn’t shipped yet. The data doesn’t fix itself.

Why would position 3.4 get nothing? There are three usual reasons. A map pack and ads sit above the organic results, so organic position 3 can be well down a phone screen. The title reads like a template (suburb, service, brand) and gives nobody a reason to pick it. Or the searcher wanted a specific competitor or a directory. Google’s title link guidance asks for descriptive, concise titles and warns against boilerplate repeated across pages, which is exactly what a set of templated location pages tends to have.

The map pack side is a profile job. Google says local results are based mainly on relevance, distance and prominence, with reviews and links feeding prominence (Google Business Profile help). If the pack is taking the clicks, that’s where to work. My guide to local and GMB SEO for service businesses covers the profile half.

One more pattern to flag: long, conversational searches that read like someone typed a question into an AI assistant. One asked for the best contractor to handle soffit, fascia and siding together. The roofer ranked 1.8 on it, with 21 impressions and no clicks. A plain-language quick answer block at the top of the matching page is the cheap fix.

The lawn company had its own version. Fifteen dethatching queries, best position 3.2, roughly 280 impressions over 44 days, zero clicks. That one turned out to be a different problem entirely.

The cheapest page to rank is the one Google already shows. Fix why nobody clicks it before you write a single new page.

Ram Kr Shukla, SEO and Growth Consultant

Step 5: Find the Money Services Nobody Sees

Steps 2 to 4 only see what Google showed. A service with no impressions never appears in the export, so you have to go looking for it. Take the sitemap list and the service list, and check both against the pages report for the same 90 days.

A sitemap URL with zero impressions is new, not indexed, or indexed and ignored. A service on the owner’s list with no URL at all is a straight build candidate, as long as Step 2 showed some demand for it.

The roofing contractor had two of the first kind, and they were the two pages that mattered most. The roof replacement page (2,069 words) and the residential roofing page (4,061 words) both came back “Submitted and indexed” in URL Inspection. Both earned zero impressions. Google’s URL Inspection help says plainly that “URL is on Google” doesn’t guarantee a page will appear in results. Here was the proof.

Rewriting them would’ve been the wrong call. They had plenty of words. What they lacked was internal links from the pages Google already trusted, and titles that matched how people search. If you find pages like this, start with the fix for orphaned and unindexed location pages and the internal linking system I use to move pages without new backlinks.

The second kind showed up in the crawl. The contractor had no asphalt shingle service page, though asphalt shingles are the most common residential roof in the US. Asphalt installation searches already sat at positions 5.2 and 5.4 with nothing built for them. You’d never spot that gap by staring at Search Console alone, because the searches were landing on blog comparisons.

The Trap: Demand for a Service You Don’t Sell

Back to those fifteen dethatching queries. On paper they were a perfect build target: page-one positions, zero clicks, and no dethatching content at all from the two closest local competitors. The company had already published a dethatching vs aeration post of about 1,900 words, and it was pulling 398 impressions at position 10.6 in a three-week window.

Then we asked the owner. They don’t offer dethatching. For most of their tall fescue lawns they recommend core aeration instead, which relieves soil compaction and gives better seed-to-soil contact when they overseed in the fall. Dethatching is for a heavy thatch layer, more common on warm-season grasses like bermuda or zoysia, and it’s far more aggressive.

The post, as written, read as if the service was bookable. Its FAQ answered whether you can dethatch and aerate together with a cheerful yes. A homeowner who called after reading it would’ve been told no. That’s a lost lead, and a bit of trust gone with it.

The fix kept the rankings and changed the routing. The post now says plainly that the company doesn’t offer dethatching: in the quick answer, in the section on measuring thatch, and in a new FAQ, “Do you offer dethatching?”, added both on the page and in the FAQPage schema. Every mention points fescue owners to the aeration and overseeding program. The post still uses the dethatching terms it ranks on.

So the rule is simple. Before you build anything off a query cluster, read the cluster to the owner. If they don’t sell it, write the informational page and say so on it. You keep the impressions, and the clicks go somewhere the business can actually serve.

The roofer had a version of this too. A TPO page looked like a gap until the data showed zero TPO queries and an existing flat roof page of 4,305 words that already covered every flat system. A hail damage page wasn’t needed either. The storm damage page already ranked 6.4 for hail damage roof repair, on 253 impressions. It needed a better title. A fourth page in the same silo would only have split it.

Step 6: Prioritise by Value and Intent

You’ll end up with more gaps than you can fix in a quarter. Sort them with four questions, in this order.

1
How much is the service worth?

Use the owner’s value band from the inputs. A high-value service with 300 impressions usually beats a low-value one with 3,000.

2
Is the intent commercial?

Repair, replace, install, near me, company and cost searches come from people close to calling. “What is” and “how to” searches are informational. They’re worth a post, but they sit below a service page in the queue.

3
How close is it already?

Page one with zero clicks is a fix you can ship this week. Position 8 to 15 is a strengthen-and-link job. Position 25 or lower with no page is a full build, and it’ll take months to show.

4
What does the fix take?

Retitling an existing page takes an hour. A new service page with FAQs, schema and inbound links takes a few days. Effort breaks ties.

Run the roofing data through those questions and the order falls out like this:

GapIntentDistance from page oneEffortOrder
Six page-one pages with zero clicksCommercialAlready on page oneLow: titles, metas, profile1
Two indexed money pages with no impressionsCommercialNot showingLow: internal links, titles2
Commercial roofing clusterCommercialPositions 20 to 30High: new child pages3
Attic insulationCommercialPositions 7 to 10Medium: rebuild one page4
Siding repair and windowsCommercialPositions 22 to 33Medium to high5
Asphalt roof repair across six suburbsCommercialPositions 8 to 14Consolidate into the repair pageFolded in

Commercial roofing came third despite being the largest impression pool on the site, because two cheaper fixes sat ahead of it on pages that were already close.

Step 7: Build, Retarget or Consolidate

Every gap gets one of six moves. Pick by what’s holding the query now.

If you seeDo thisFrom the data
The right page ranks top 10 with no clicksRewrite the title, meta and opening, then work the profileSix suburb pages at positions 3.4 to 7.8
A blog post or homepage holds a commercial queryBuild or strengthen the service page, link to it from the holderGutter guards: service page 25.5, blog post 8.5
No page, real commercial demandBuild a dedicated service pageSiding repair, about 730 impressions
Two or more of your pages split the queryConsolidate into one and redirect the restThree fiber cement siding URLs, two ranking 12.7 and 32.5
A service you don’t offerInformational page that says so and routes to what you sellDethatching, routed to aeration
One service, many towns, thin demand in eachStrengthen one service page; build town pages only if each can be genuinely differentAsphalt roof repair across six suburbs, about 450 impressions in total

That last row matters. Spinning up a page for every service in every town is how local sites end up with dozens of near-identical pages. Google’s spam policies list “pages targeted at specific regions or cities that funnel users to one page” as doorway abuse. If you do build location pages, they need real local substance. The method for that is in multi-location SEO without duplicate content.

When two of your own pages split a query, consolidate properly or you’ll lose what both had. The steps are in the keyword cannibalisation guide.

What went into the new pages

On the roofing site, the builds that came out of this were specific. The insulation page went from 1,074 to 2,588 words, with FAQ and HowTo schema and five inbound links. A new siding repair page went live at 2,303 words with seven inbound links. The commercial roofing cluster got three child pages (leak repair, replacement and a maintenance plan) with nine inbound links from seven pages. An attic insulation removal and replacement page and a factor-based cost article picked up the removal and cost searches.

Each new page followed the same pattern. The title and H1 used the cluster’s head term. The FAQ came from real queries in the cluster, phrased the way people typed them. The first inbound links came from the pages Google already showed for that cluster, since those pages carried the impressions. I build these for contractors as part of roofing SEO and construction SEO work.

Step 8: Check That the Impressions Moved

A gap is closed when the cluster’s impressions move to the page you built for it. Rankings alone don’t prove that. Use compare mode (it’s in the same filtering help) to set the 28 days before against the 28 days after, keep the cluster’s regex filter on, and read the Pages tab.

Give it time. On the lawn company’s site, three posts published in July and August produced nothing at first, then pulled about 700 impressions in twelve days once Google started testing them. Five posts published in September showed 15 impressions or fewer each in their first twelve days. That’s normal discovery lag. Don’t call it a failure, and don’t claim credit early.

Read average position carefully once a page starts ranking for more searches. The lawn company’s rebuilt aeration page moved from position 34.6 to 21.3 with impressions up 38%. Its mosquito control page grew impressions 198% while average position slid from 26.6 to 37.3. More queries, many of them deep, drag the average down. That’s growth that looks like a drop on a chart.

Then run the whole thing again in 90 days. New gaps appear as the site ranks for more. On the roofing site, the second pull found three clusters the first one missed: siding replacement, asphalt roof repair landing on the wrong page, and attic insulation removal and cost.

The Method on One Page

1
Export 90 days of query and page data

Pull queries and pages together from the Performance report. Use the API or the Looker Studio connector if you need more than the 1,000 rows the interface exports.

2
Clean the export

Filter to your country, drop out-of-area towns, and exclude brand terms with a Doesn’t match regex query filter.

3
Cluster queries by service

Write one regex per service on the owner’s list and record impressions, clicks and position range for each cluster.

4
Map each cluster to its landing page

Open the Pages tab for each cluster and mark it healthy, wrong page or no page.

5
Flag page-one queries with zero clicks

Filter to position 10 or better with zero clicks, then fix titles, meta descriptions and the Business Profile before you build anything.

6
Find the invisible money services

Compare the sitemap and the service list against the pages report to find indexed pages with no impressions and services with no page.

7
Check with the owner, then prioritise

Confirm every cluster is a service the business sells, then order the gaps by service value, intent, distance from page one and effort.

8
Build, retarget or consolidate, then re-check

Apply one move per gap, link new pages from the URLs already holding the impressions, and compare the 28 days before and after.

If you only do one thing this week, run Step 4. Page-one queries with zero clicks are the cheapest traffic a local site has. Then check your two or three most valuable services in the pages report. If one is indexed and invisible, you’ve found your next job. For the wider monthly routine around these filters, see the Search Console reports most people never open.

Want this run on your site?

I run this gap analysis for roofers, home service companies and dental practices, and hand back a build queue with each page tied to demand Search Console already shows. It’s the starting point of my local SEO work.

See local SEO services

Service Page Gap Analysis FAQ

What is a service page gap analysis?

It’s a review of your own Search Console data that shows which services people search for that your site doesn’t answer well. It finds four gaps: demand landing on the wrong page, demand with no page, page-one queries with zero clicks, and services that get no impressions at all.

How do I find missing service pages in Search Console?

Export queries and pages together for 90 days, group the queries by service with regex filters, and open the Pages tab for each group. If the impressions land on your homepage, a blog post or nothing relevant, that service is missing a page, or has one Google can’t match to the search.

Why does my page rank on page one but get zero clicks?

Usually a map pack and ads sit above it, the title reads like a template, or the searcher is really looking for someone else. A position of 3 says nothing about what’s stacked above you on a phone. Fix the title and meta description first, then work on your Google Business Profile.

Should I build a service page for every town I serve?

Only where each page can be genuinely different. Google’s spam policies treat city pages that funnel visitors to one destination as doorways. Most local businesses do better with one strong page per service, plus location pages that carry real local proof.

How many impressions justify a new service page?

There’s no fixed number. A high-value service with a few hundred impressions a quarter and commercial intent is worth a page. A low-value service with thousands of informational impressions may only need a blog post. Weigh value and intent before volume.

What if people search for a service I don’t offer?

Don’t build a service page for it. Write an informational page that answers the question, say clearly that you don’t offer the service, and point readers to what you do sell. You keep the rankings without promising work you can’t do.

How is this different from a competitor content gap analysis?

A competitor gap analysis starts from rival domains in a paid tool. This method starts from your own Search Console data, which shows the searches Google is already testing you against. For local businesses that’s usually more reliable, because local competitors rarely have much content to compare.

How long before a new service page shows up in Search Console?

Expect a few weeks before impressions build. On one lawn care site, new posts showed almost nothing in their first twelve days, while posts published a month or two earlier were only then pulling hundreds of impressions. Re-check the cluster after 28 days and again after 90.

Tags: Local SEOSearch ConsoleService PagesContent Strategy

RS

Written by Ram Kr Shukla

SEO and growth consultant with 20+ years in SEO and growth, across 50+ brands. Google Analytics and Google Ads certified. Works with businesses as a consultant or as a Fractional Chief Digital Officer, and runs local SEO for US service businesses, including roofing, lawn care and 9 dental practices. Works daily in Search Console, Looker Studio and Screaming Frog.

Client Results, Not Claims

5x D2C revenue in 18 months via SEO
10x SaaS trials, zero new blog posts
120K Monthly organic visitors from zero

Free Resource

Steal My 40 Point SEO Audit Checklist

The exact list I run on every paid audit. Score your site in 30 minutes.

Get the checklist →

Is your site invisible to AI search?

Ask ChatGPT to recommend brands in your category. If you are not the answer, we should talk.

Book a Free Strategy Call