DEV Community

Elowen
Elowen

Posted on

Build a SERP-Based Content Gap Finder for Existing Pages

Updating an existing article is harder than writing a new one in one specific way:

You already have content, but you do not always know what to add.

A keyword tool can show related terms. A content brief can list possible sections. But a live SERP can show what searchers are currently being offered:

  • comparison sections
  • setup steps
  • examples
  • pricing context
  • implementation notes
  • common pitfalls
  • glossary explanations
  • evaluation criteria
  • tool alternatives

This post shows a small pattern for building a content gap finder from SERP result patterns. A SERP API such as TalorData SERP API can collect structured search results; your code can compare those signals with an existing page outline.

What the tool should do

Input:

  • target keyword
  • current page title
  • current page headings
  • optional current page summary
  • top SERP result titles, snippets, and URLs

Output:

  • recurring SERP topics
  • page formats that appear often
  • candidate missing angles
  • gaps that need human review
  • things not worth adding

The last point matters. A gap finder should not turn every SERP detail into an editing task.

A simple input format

Start with your existing page outline:

{
  "page_url": "https://example.com/blog/serp-api-guide",
  "target_keyword": "serp api guide",
  "title": "SERP API Guide for Developers",
  "headings": [
    "What is a SERP API?",
    "How search result APIs work",
    "Common use cases",
    "Basic API workflow"
  ]
}
Enter fullscreen mode Exit fullscreen mode

Then normalize the SERP results:

{
  "query": "serp api guide",
  "location": "United States",
  "captured_at": "2026-07-22T09:00:00Z",
  "results": [
    {
      "position": 1,
      "title": "How to Use a SERP API for SEO Monitoring",
      "url": "https://example.com/blog/serp-api-seo-monitoring",
      "snippet": "Learn how to collect ranking data and monitor search results over time."
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The example is generic. Replace field paths with the actual response schema from your provider.

Normalize text

import re
from collections import Counter
from urllib.parse import urlparse


def normalize_text(value: str) -> str:
    value = value.lower()
    value = re.sub(r"[^a-z0-9]+", " ", value)
    return re.sub(r"\s+", " ", value).strip()


def tokens(value: str) -> set[str]:
    stopwords = {
        "a", "an", "the", "and", "or", "to", "of", "for", "in", "with",
        "how", "what", "why", "is", "are", "your", "from", "by"
    }
    return {token for token in normalize_text(value).split() if token not in stopwords}
Enter fullscreen mode Exit fullscreen mode

This is intentionally simple. For a first version, readable rules are easier to review than a model-driven score nobody trusts.

Extract current page topics

def page_topic_tokens(page: dict) -> set[str]:
    text = " ".join([page.get("title", ""), *page.get("headings", [])])
    return tokens(text)
Enter fullscreen mode Exit fullscreen mode

The outline is not the full article, but it is often enough to find obvious missing areas.

Extract SERP topics

def result_topic_tokens(result: dict) -> set[str]:
    text = " ".join([
        result.get("title", ""),
        result.get("snippet", ""),
        urlparse(result.get("url", "")).path.replace("-", " ")
    ])
    return tokens(text)


def serp_topic_counts(serp: dict) -> Counter:
    counts = Counter()

    for result in serp["results"][:10]:
        weight = max(1, 11 - result.get("position", 10))
        for token in result_topic_tokens(result):
            counts[token] += weight

    return counts
Enter fullscreen mode Exit fullscreen mode

This gives higher weight to topics appearing in higher positions.

Find candidate gaps

def find_candidate_gaps(page: dict, serp: dict, min_score: int = 5) -> list[dict]:
    page_tokens = page_topic_tokens(page)
    serp_counts = serp_topic_counts(serp)

    gaps = []
    for topic, score in serp_counts.most_common():
        if score < min_score:
            continue
        if topic in page_tokens:
            continue

        gaps.append({
            "topic": topic,
            "score": score,
            "reason": "Appears in SERP result titles, snippets, or URL paths but not in the current page outline"
        })

    return gaps[:20]
Enter fullscreen mode Exit fullscreen mode

The output is a candidate list, not an editing plan.

Add page format detection

Content gaps are not only words. They can also be formats.

def detect_page_format(result: dict) -> str:
    text = normalize_text(" ".join([result.get("title", ""), result.get("url", "")]))

    if "pricing" in text:
        return "pricing_context"
    if "alternative" in text or "compare" in text or "comparison" in text:
        return "comparison"
    if "guide" in text or "tutorial" in text or "how to" in text:
        return "guide"
    if "docs" in text or "documentation" in text:
        return "documentation"
    if "review" in text:
        return "review"
    return "unknown"


def format_counts(serp: dict) -> Counter:
    return Counter(detect_page_format(result) for result in serp["results"][:10])
Enter fullscreen mode Exit fullscreen mode

A page can be missing a topic, but it can also be missing a useful format. For example, an article may explain a concept but never include comparison criteria, implementation steps, or review questions.

Build a review output

def content_gap_report(page: dict, serp: dict) -> dict:
    return {
        "page_url": page.get("page_url"),
        "target_keyword": page.get("target_keyword"),
        "candidate_topic_gaps": find_candidate_gaps(page, serp),
        "serp_page_formats": dict(format_counts(serp)),
        "review_note": "Treat these as candidates. Review against search intent, page purpose, and editorial focus before adding sections."
    }
Enter fullscreen mode Exit fullscreen mode

This kind of output is easy to pass to an editor or SEO reviewer.

Add a reject list

Not every gap should be filled.

Add reasons to reject a candidate:

  • off-topic for the page purpose
  • belongs on a separate page
  • appears only because of one outlier result
  • too commercial for an educational page
  • too beginner-level for an advanced guide
  • outside product or editorial scope
  • not useful for the target reader

A useful gap finder should help reduce random additions, not encourage them.

Where the SERP API fits

The SERP API is the collection layer. Your comparison logic is the analysis layer.

One useful workflow:

  1. Collect SERP results for the target keyword
  2. Normalize titles, snippets, URLs, and positions
  3. Extract recurring topics and page formats
  4. Compare them with the current page outline
  5. Generate candidate gaps
  6. Review what to add, ignore, or move to another page

If you need structured search result data for this workflow, TalorData can support the SERP collection step while your team owns the gap rules and editorial decisions.

What I would not automate

I would not automatically rewrite the page from the gap report.

SERP patterns can reveal missing angles, but they do not know your page purpose, brand voice, product limits, or editorial priorities.

Use the finder to create a review list. Let a human decide whether each gap belongs in the update.

Final thought

A content gap is not just "something competitors mention."

A useful content gap is a missing angle that matches the search intent, helps the target reader, and fits the page you are updating.

The SERP can help you find candidates. The editing decision still needs judgment.

Top comments (0)