CrawlForge MCP
HomePlaygroundUse CasesIntegrationsPricingDocumentationBlog
How to Run a Keyword Gap Analysis with an MCP Server
Tutorials
Back to Blog
Tutorials

How to Run a Keyword Gap Analysis with an MCP Server

C
CrawlForge Team
Engineering Team
August 23, 2026
11 min read

On this page

Quick Answer

A keyword gap analysis run through an MCP server has three stages: discover candidate terms with search_web, pull real Google organic positions with serp_rank, then classify what kind of page holds each top-ten slot. That last stage is the one most tools skip and the one that decides everything -- a SERP full of vendor homepages is closed no matter what its difficulty score says, while a SERP full of directories and forum threads is open. Budget 5 credits per lookup and expect each live SERP call to take 10 to 40 seconds, because it runs a real-time Google query rather than reading a cache.

Most keyword research tools answer the wrong question. They tell you how hard a term is on a scale of 1 to 100, which is a summary of a summary. What you actually need to know is who holds the top ten and what kind of pages they are, because that determines whether the position can be taken at all.

This guide builds that analysis as a reproducible pipeline over an MCP connection. It is the exact method behind our 2026 web scraping SERP study, including the mistake that invalidates most DIY keyword research.

Table of contents

  • The three stages
  • The mistake that makes most keyword research fiction
  • Stage 1: Discover candidate terms
  • Stage 2: Pull real organic positions
  • Stage 3: Classify the page types
  • Scoring the gap
  • Checking intent before you commit
  • Credit cost and runtime
  • Running it from the REST API
  • Frequently asked questions

The three stages

StageToolCreditsAnswers
Discoversearch_web5Which terms exist and who appears
Measureserp_rank5The real organic top ten
Classifyanalyze_content (optional)3What kind of page each result is

Stage 3 is usually judgement rather than a tool call — you are labelling nine URLs, which is faster by eye than by API.

The mistake that makes most keyword research fiction

Start here, because it invalidates everything downstream.

A search API's result order is not Google's ranking. Search tools retrieve results and then re-rank them. CrawlForge's search_web re-orders what it retrieves using BM25, semantic similarity, authority, and freshness weights, and it reports exactly that in its response:

Json
{
  "processing": {
    "ranking": {
      "algorithmsUsed": ["bm25", "semantic", "authority", "freshness"],
      "weightsApplied": { "bm25": 0.4, "semantic": 0.3, "authority": 0.2, "freshness": 0.1 }
    }
  }
}

That re-ranking makes results better for research and useless as a rank measurement. If you read position 1 out of a search API and record it as a Google ranking, every conclusion built on it is wrong.

Use search_web to learn which domains are in play. Use serp_rank — which returns Google's own rank_group value — for anything you intend to call a position.

Stage 1: Discover candidate terms

Cast wide first. You are looking for the vocabulary of the category, not rankings yet.

Typescript
import { Client } from '@modelcontextprotocol/sdk/client/index.js';

const client = new Client({ name: 'keyword-gap', version: '1.0.0' });

interface Candidate {
  keyword: string;
  indexedPages: number;
  domains: string[];
}

async function discover(keyword: string): Promise<Candidate> {
  const res = await client.callTool({
    name: 'search_web',
    arguments: { query: keyword, limit: 20 },
  });

  const data = JSON.parse(res.content[0].text);

  return {
    keyword,
    // Google's rough index estimate -- a crowding signal, NOT search volume.
    indexedPages: parseInt(data.total_results ?? '0', 10),
    // Presence only. This order is re-ranked and is not a Google ranking.
    domains: [...new Set(data.results.map((r) => r.displayLink))],
  };
}

Two fields matter. total_results is Google's approximate count of competing pages — a crowding signal, not volume. The domain list tells you who is in the conversation.

Stage 2: Pull real organic positions

Now measure. serp_rank runs a live Google query and returns true organic positions, plus the competitor listing.

Typescript
interface RankResult {
  keyword: string;
  found: boolean;
  position: number | null;
  competitors: Array<{ position: number; domain: string; url: string }>;
}

async function measure(keyword: string, target: string): Promise<RankResult> {
  const res = await client.callTool({
    name: 'serp_rank',
    arguments: {
      keyword,
      target,
      location_name: 'United States',
      device: 'desktop',
      depth: 10,
    },
  });

  const data = JSON.parse(res.content[0].text);

  // Unconfigured deployments return configured:false rather than a fake rank.
  if (data.configured === false) {
    throw new Error('serp_rank is not configured on this server');
  }

  return {
    keyword,
    found: data.found,
    position: data.position,
    competitors: (data.results ?? []).map((r) => ({
      position: r.position,
      domain: r.domain,
      url: r.url,
    })),
  };
}

Run these sequentially, not in parallel. The live SERP endpoint performs a real-time Google query; firing several at once reliably produces timeouts rather than faster results.

depth: 10 is the right default. Deeper scans cost more and, for gap analysis, positions 11 and beyond rarely change the decision.

Stage 3: Classify the page types

This is the stage that produces the actual insight, and it is mostly judgement.

For each of the nine or ten results, label what kind of page it is:

  • Commercial — homepage, product page, pricing page
  • Vendor content — a company's blog post or documentation
  • Independent — directory, forum thread, code repository, news article

Then count the commercial results. That single number tells you more than any difficulty score:

Typescript
type PageType = 'commercial' | 'vendor-content' | 'independent';

function gapScore(labels: PageType[]): 'closed' | 'contested' | 'open' {
  const commercial = labels.filter((l) => l === 'commercial').length;
  if (commercial >= 4) return 'closed';
  if (commercial >= 1) return 'contested';
  return 'open';
}

Applied to real data from our study:

KeywordCommercial resultsVerdict
web scraping api6 of 9closed
firecrawl alternative2 of 9contested
mcp scraping0 of 8open

"web scraping api" is closed — six vendor homepages and product pages, each backed by years of link building. No amount of content quality moves that.

"firecrawl alternative" lands in the middle. Only two results are commercial landing pages, Apify's alternatives page and a Context comparison page, but the rest is dense with vendor blog posts all chasing the same departing customers. Winnable, crowded, and worth the effort only if you have something specific to say.

"mcp scraping" scores open, and the composition explains why: two directories, a Reddit thread, a GitHub repo, three third-party blogs, and a docs page. Nobody has published a commercial page for it.

Scoring the gap

Combine the two signals. A term is worth pursuing when the SERP is open and the crowding is manageable:

Typescript
interface Scored {
  keyword: string;
  verdict: 'closed' | 'contested' | 'open';
  indexedPages: number;
  priority: 'high' | 'medium' | 'skip';
}

function prioritise(c: Candidate, verdict: Scored['verdict']): Scored['priority'] {
  if (verdict === 'closed') return 'skip';
  if (verdict === 'open' && c.indexedPages < 2_000_000) return 'high';
  return 'medium';
}

The threshold is a judgement call, not a law. The ordering is what matters: an open SERP with moderate crowding beats a contested SERP with low crowding, every time.

Checking intent before you commit

One last gate, and it cannot be automated away.

Read the titles of the top ten and ask whether those searchers want to buy what you sell. In our study, "llm scraper" passed every numeric filter — 329,000 competing pages, clearly on-topic — and failed on contact with the results. A large share were people trying to block scrapers: a YunoHost thread called "Prevent LLM scrapers/trawlers?", an r/sysadmin thread on fighting them, Akamai on bot management.

Same words, opposite intent. Ranking there earns traffic that never converts and a bounce rate that teaches Google your page is a bad answer.

Thirty seconds of reading catches what no score can.

Credit cost and runtime

A ten-keyword study:

ItemCallsCredits
Discovery10 x search_web50
Measurement10 x serp_rank50
Total100

That is a tenth of the free tier's 1,000 one-time credits.

Runtime is the real constraint. Live SERP lookups took between 10 and 41 seconds each in our measurements — the same keyword varied from 11s to 28s across consecutive runs, because the endpoint runs a live Google query whose latency depends on upstream capacity. Ten keywords run sequentially is five to ten minutes. Set a client timeout of at least 60 seconds and run the job in the background.

Running it from the REST API

If you are not inside an MCP client, the same lookup works over HTTP:

Bash
curl -X POST https://www.crawlforge.dev/api/v1/tools/serp_rank \
  -H "X-API-Key: $CRAWLFORGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "keyword": "mcp scraping",
    "target": "crawlforge.dev",
    "location_name": "United States",
    "depth": 10
  }'

The REST response uses rank and all_positions where the MCP tool returns position; both report the same underlying Google organic position. Failed lookups — an unreachable provider, a timeout — return an error and are not charged.

Where to take it next

Once you know which terms are open, the follow-on work is technical: making sure the pages you publish for them are actually crawlable and correctly marked up. Our guide to automating SEO audits with CrawlForge covers that half, and the serp_rank API reference documents every parameter used above.

Start free with 1,000 credits — enough for roughly 200 rank checks.

Try this yourself — no signup needed

Run any of CrawlForge's 28 scraping and extraction tools in the playground, then start free with 1,000 credits.

1,000 free credits • One-time • No credit card required

Tags

SEOkeyword-researchtutorialMCPautomationSERP-analysis

About the Author

C

CrawlForge Team

Engineering Team

Building the most comprehensive web scraping MCP server. We create tools that help developers extract, analyze, and transform web data for AI applications.

Stay updated with the latest insights

Get tutorials, product updates, and web scraping tips delivered to your inbox.

No spam. Unsubscribe anytime.

Put this into practice

Test CrawlForge's tools on any URL — free, no signup.

On this page

Frequently Asked Questions

What is a keyword gap analysis?+

A keyword gap analysis identifies search terms where demand exists but no competitor holds a defensible position. The common version compares which keywords your rivals rank for and you do not. The more useful version examines what kind of pages occupy the top ten: if they are vendor homepages and product pages, the term is closed regardless of its difficulty score, because content does not displace a company's primary landing page. If they are directories, forum threads, and third-party blog posts, the commercial position is unclaimed and a single well-made page can take it.

Why does search_web order differ from actual Google rankings?+

Because search_web retrieves results and then re-ranks them using BM25, semantic similarity, authority, and freshness weights, and it reports those weights in the processing.ranking field of every response. That re-ordering makes it better for research and unusable as a ranking measurement. Any retrieval tool that re-orders results behaves the same way. To record a position you need an endpoint that returns Google's own organic rank value, which is what serp_rank provides. Treating search API order as rank is the single most common way keyword research becomes fiction.

How many credits does a keyword gap analysis cost?+

Both search_web and serp_rank cost 5 credits per call, so a ten-keyword study using one discovery search and one rank check per term costs 100 credits total. That is a tenth of the 1,000 one-time credits included in the free tier, which works out to roughly 200 rank checks before you spend anything. Optional content classification with analyze_content adds 3 credits per page, though for a ten-result SERP it is usually faster to label the page types by eye than to call the API.

Why do live SERP lookups take so long?+

Because the endpoint runs a real-time Google query rather than reading a cached index, and it holds the connection open while it does. Latency swings widely with upstream capacity: in our measurements the same keyword returned in 11 seconds on one run and 28 seconds on the next, and one lookup took 41 seconds. Set a client timeout of at least 60 seconds, run lookups sequentially rather than in parallel, and treat a multi-keyword study as a background job. Firing several concurrent requests produces timeouts, not speed.

Should I target keywords with high search volume or low competition?+

Neither number answers the question on its own, which is why this method ignores both at first. High volume with a closed SERP is worthless -- six vendor homepages hold "web scraping api", and no amount of volume makes that winnable. Low competition scores routinely mislead too, because they cannot see intent: "llm scraper" looks attractive on every metric while a large share of its results are people trying to block scrapers rather than buy one. Judge the composition of the top ten first, then use crowding estimates to order the terms that survive.

Can I run this analysis without an MCP client?+

Yes. Both tools are available over HTTP, so the same pipeline works from curl, a scheduled job, or any language with an HTTP client -- authenticate with an X-API-Key header or an Authorization Bearer token. The response shapes differ slightly: the REST serp_rank endpoint returns rank and all_positions where the MCP tool returns position and allPositions, though both report the same underlying Google organic position. Failed lookups return an error and are not charged, so a timeout during a large study costs you time rather than credits.

Related Articles

The Web Scraping Keyword Gap: A 2026 SERP Study
Web Scraping

The Web Scraping Keyword Gap: A 2026 SERP Study

We pulled real Google organic positions for the keywords this industry competes on. The legacy terms are a sealed commercial fortress. The MCP-era terms have no commercial defender at all.

C
CrawlForge Team
|
Aug 23
|
10m
How to Actually Use CrawlForge MCP in Claude Code
Tutorials

How to Actually Use CrawlForge MCP in Claude Code

Installed CrawlForge but Claude never calls it? The prompts, permission rules, and CLAUDE.md config that make MCP tools fire reliably in your terminal.

C
CrawlForge Team
|
Aug 13
|
12m
Web Scraping with Claude: The Complete Guide (2026)
Tutorials

Web Scraping with Claude: The Complete Guide (2026)

Web scraping with Claude in 2026: connect CrawlForge MCP to Claude Desktop, Claude Code, or the API and scrape any site -- no scraping code.

C
CrawlForge Team
|
Jun 9
|
12m

Footer

CrawlForge MCP

Enterprise web scraping for AI Agents. 28 specialized MCP tools designed for modern developers building intelligent systems.

Product

  • Features
  • Playground
  • Pricing
  • Use Cases
  • Integrations
  • Alternatives
  • Changelog

Resources

  • Getting Started
  • API Reference
  • Templates
  • Guides
  • Blog
  • Glossary
  • FAQ
  • Sitemap

Developers

  • MCP Protocol
  • Claude Desktop
  • Cursor IDE
  • LangChain
  • LlamaIndex

Company

  • About
  • Contact
  • Privacy
  • Terms
  • Acceptable Use
  • Cookies

Stay updated

Get the latest updates on new tools and features.

Built with Next.js and MCP protocol

© 2025-2026 CrawlForge. All rights reserved.