ScavioScavio
ProductPricingDocs
Sign InGet Started
  1. Home
  2. Tutorials
  3. How to Ground AI Keyword Research in Real SERP Data
Tutorial

How to Ground AI Keyword Research in Real SERP Data

Stop AI inventing fake keywords like "slow made ceramic plates". Feed an LLM real PAA and related searches from a SERP API. Python + JS code.

Get Free API KeyAPI Docs

AI keyword research is unreliable because the model invents plausible-sounding terms no one searches - the r/SEO example of Claude suggesting "slow made ceramic plates" and "mismatched ceramic dinnerware set" for a pottery shop is the whole problem in one line. The fix is grounding: feed the LLM real People Also Ask questions, related searches, and competitor titles from a live SERP, then constrain it to expand only from those. This tutorial pulls that signal from the Scavio Google endpoint and hands it to any model as a grounded brief. The model stops guessing and starts working from terms the SERP proves exist.

Prerequisites

  • Python 3.8+ or Node 18+ installed
  • A Scavio API key from scavio.dev (50 free credits on signup)
  • requests installed (pip install requests) for Python
  • Any LLM API (the grounding step is model-agnostic)

Walkthrough

Step 1: Pull the full SERP for your seed term

Call https://api.scavio.dev/api/v1/google with light_request:false (2 credits) so the response includes the questions (People Also Ask) and related_searches blocks alongside organic results. These are real queries Google itself surfaces - the ground truth the model lacks.

Python
import os, requests
H = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}", "Content-Type": "application/json"}

def serp(seed):
    r = requests.post("https://api.scavio.dev/api/v1/google",
        headers=H, json={"query": seed, "light_request": False})
    r.raise_for_status()
    return r.json().get("data", {})

Step 2: Extract the real query signal

Pull the questions block, related_searches, and the top organic titles. Together these are the vocabulary your buyers and competitors actually use - not the model's guess at it. For the pottery shop, this returns terms like 'handmade ceramic dinner plates' that real people search, not invented phrases.

Python
def signal(data):
    return {
        "paa": [q.get("question") for q in data.get("questions", [])],
        "related": data.get("related_searches", []),
        "titles": [o.get("title") for o in data.get("organic", [])[:10]],
    }

Step 3: Build a constrained grounding prompt

Hand the model the real signal and forbid invention explicitly. The instruction matters: expand only from the provided questions, related searches, and titles; do not generate keywords that are not grounded in this data. This single constraint is what kills the 'slow made ceramic plates' failure mode.

Python
def grounding_prompt(seed, sig):
    return f'''You are doing keyword research for: {seed}.
Use ONLY the real search data below. Do not invent keywords.

People Also Ask: {sig['paa']}
Related searches: {sig['related']}
Competitor titles: {sig['titles']}

Return 20 keyword clusters grounded strictly in the terms above.'''

Step 4: Generate and verify against the source

Run the prompt through your LLM, then verify: every keyword the model returns should trace back to a term in the PAA, related searches, or titles. Drop anything that does not. This verification step is cheap insurance - it catches the model when it slips back into inventing.

Python
def verify(keywords, sig):
    pool = " ".join(sig["paa"] + sig["related"] + sig["titles"]).lower()
    return [k for k in keywords if any(w in pool for w in k.lower().split())]

Step 5: Localize the grounding for non-US markets

The r/SEO thread also flagged US-skewed AI output ('change your life' copy for a Swiss audience). Grounding fixes this too: pass a country or language hint to the SERP call so the real signal comes from the right market, then constrain the model to it. The data, not the model's training bias, sets the regional vocabulary.

Python
def localized_serp(seed, country="ch"):
    r = requests.post("https://api.scavio.dev/api/v1/google",
        headers=H, json={"query": seed, "light_request": False, "country": country})
    return r.json().get("data", {})

Python Example

Python
import os, requests
H = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}", "Content-Type": "application/json"}

def grounded_keywords(seed):
    data = requests.post("https://api.scavio.dev/api/v1/google", headers=H,
        json={"query": seed, "light_request": False}).json().get("data", {})
    sig = {
        "paa": [q.get("question") for q in data.get("questions", [])],
        "related": data.get("related_searches", []),
        "titles": [o.get("title") for o in data.get("organic", [])[:10]],
    }
    # feed sig to your LLM with: 'expand only from these real terms, do not invent'
    return sig

print(grounded_keywords("handmade ceramic dinner plates"))

JavaScript Example

JavaScript
const H = { Authorization: `Bearer ${process.env.SCAVIO_API_KEY}`, 'Content-Type': 'application/json' };

async function groundedKeywords(seed) {
  const res = await fetch('https://api.scavio.dev/api/v1/google', {
    method: 'POST', headers: H,
    body: JSON.stringify({ query: seed, light_request: false }),
  }).then(r => r.json());
  const data = res.data || {};
  return {
    paa: (data.questions || []).map(q => q.question),
    related: data.related_searches || [],
    titles: (data.organic || []).slice(0, 10).map(o => o.title),
  };
}

groundedKeywords('handmade ceramic dinner plates').then(console.log);

Expected Output

JSON
{'paa': ['Are handmade plates food safe?', 'How much do ceramic dinner plates cost?', ...],
 'related': ['handmade ceramic dinnerware set', 'stoneware dinner plates handmade', ...],
 'titles': ['Handmade Ceramic Dinner Plates - ...', ...]}
# Real search terms the model can expand from - no invented 'slow made ceramic plates'.

Related Tutorials

  • How to Extract People Also Ask Data from Google SERP
  • How to Track SEO Rankings Daily with the Scavio API

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.8+ or Node 18+ installed. A Scavio API key from scavio.dev (50 free credits on signup). requests installed (pip install requests) for Python. Any LLM API (the grounding step is model-agnostic). A Scavio API key gives you 50 free credits on signup.

Yes. The free tier includes 50 credits on signup, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Related Resources

Use Case

Keyword Volume API Verification

Read more
Best Of

Best Queue-Based SERP API in 2026

Read more
Best Of

Best Keyword Volume APIs in 2026

Read more
Comparison

Semrush API vs Raw SERP API

Read more
Use Case

Local LLM Search Grounding via API

Read more
Glossary

People Also Ask (PAA)

Read more

Start Building

Stop AI inventing fake keywords like "slow made ceramic plates". Feed an LLM real PAA and related searches from a SERP API. Python + JS code.

Get Free API KeyRead the Docs
ScavioScavio

Real-time search API for AI agents. Search every platform, not just Google.

Product

  • Features
  • Pricing
  • Dashboard
  • Affiliates

Developers

  • Documentation
  • API Reference
  • Quickstart
  • MCP Integration
  • Python SDK

Alternatives

  • Tavily Alternative
  • SerpAPI Alternative
  • Firecrawl Alternative
  • Exa Alternative

Tools

  • JSON Formatter
  • cURL to Code
  • Token Counter
  • All Tools

© 2026 Scavio. All rights reserved.

Featured on TAAFT
Terms of ServicePrivacy Policy