People Also Ask API: Build a Keyword Research Tool Using Google's PAA Data
When you search on Google, you will often see a box labeled "People also ask" below the first few organic results. It contains 4-6 questions related to your query, each expandable to show a featured answer snippet.
This box is a gold mine for keyword research. Unlike tools that estimate search volume from historical data, PAA questions come directly from Google — they reflect real user intent, real language, and real information gaps that Google has decided are worth surfacing. Extracting PAA data at scale gives you a question intelligence layer that no static keyword database can replicate.
This guide shows how to extract PAA data using a SERP API and build practical keyword research tools on top of it.
What PAA Data Looks Like
A SERP API returns PAA questions in a people_also_ask array. Each entry includes the question, a snippet answer, and the source URL:
{
"people_also_ask": [
{
"question": "What is the difference between a SERP API and a web scraper?",
"snippet": "A SERP API returns structured JSON from Google search results without requiring you to parse HTML or manage proxies. A web scraper...",
"link": "https://example.com/serp-api-guide",
"displayed_link": "example.com"
},
{
"question": "How much does a SERP API cost?",
"snippet": "SERP API pricing varies widely. Budget options start at $0.30 per 1,000 queries...",
"link": "https://example.com/pricing",
"displayed_link": "example.com"
}
]
}
Basic PAA Extraction
import requests
API_KEY = "your_api_key"
def get_paa(query: str, country: str = "us", lang: str = "en") -> list:
resp = requests.post(
"https://api.serpbase.dev/google/search",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={"q": query, "gl": country, "hl": lang, "page": 1},
timeout=15,
)
resp.raise_for_status()
data = resp.json()
return data.get("people_also_ask", [])
# Extract PAA for a seed keyword
questions = get_paa("serp api")
for q in questions:
print(f"Q: {q['question']}")
print(f" Source: {q.get('displayed_link', '')}")
print()
Use Case 1: Question Expansion for Keyword Research
PAA questions are not just content ideas — they are actual search queries people type into Google. Collecting PAA for a seed keyword and then recursively pulling PAA for each question can produce hundreds of real, related queries in minutes.
import time
from collections import deque
def expand_questions(seed: str, depth: int = 2, max_questions: int = 50) -> dict:
"""
BFS expansion: get PAA for seed, then PAA for each question found,
up to a max depth and total question count.
"""
seen = set()
result = {} # question -> source URL
queue = deque([(seed, 0)])
while queue and len(result) < max_questions:
query, level = queue.popleft()
if query in seen or level > depth:
continue
seen.add(query)
questions = get_paa(query)
for q in questions:
text = q["question"]
if text not in result:
result[text] = q.get("link", "")
if level + 1 <= depth:
queue.append((text, level + 1))
time.sleep(0.3)
return result
# Expand from one seed keyword
all_questions = expand_questions("content marketing", depth=2)
print(f"Found {len(all_questions)} unique questions:")
for question in list(all_questions.keys())[:20]:
print(f" - {question}")
A depth-2 expansion from a single seed typically yields 50-200 unique questions, all of them real Google PAA entries.
Use Case 2: Content Brief Generator
Every piece of content should answer the questions real users are asking. PAA data gives you an exact list of those questions for any topic.
def generate_content_brief(topic: str) -> dict:
questions = get_paa(topic)
related_resp = requests.post(
"https://api.serpbase.dev/google/search",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={"q": topic, "gl": "us", "hl": "en"},
timeout=15,
).json()
related_searches = [
r["query"] for r in related_resp.get("related_searches", [])
]
top_urls = [
r["link"] for r in related_resp.get("organic", [])[:5]
]
return {
"topic": topic,
"faq_section": [q["question"] for q in questions],
"related_keywords": related_searches,
"competitor_urls_to_analyze": top_urls,
"recommended_h2s": [
f"What is {topic}?",
f"How to use {topic}",
f"Best practices for {topic}",
] + [q["question"] for q in questions[:3]],
}
brief = generate_content_brief("serp api")
print("=== Content Brief ===")
print(f"FAQ Questions to answer:")
for q in brief["faq_section"]:
print(f" ? {q}")
print(f"\nRelated keywords to include:")
for kw in brief["related_keywords"][:5]:
print(f" - {kw}")
Use Case 3: FAQ Schema Generation
FAQ schema markup can earn your page extra space in Google search results. PAA data is a perfect source for FAQ content because it is drawn directly from real user questions.
import json
def generate_faq_schema(topic: str) -> dict:
questions = get_paa(topic)
faq_items = [
{
"@type": "Question",
"name": q["question"],
"acceptedAnswer": {
"@type": "Answer",
"text": q.get("snippet", "")
}
}
for q in questions if q.get("snippet")
]
schema = {
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": faq_items
}
return schema
schema = generate_faq_schema("serp api")
print(json.dumps(schema, indent=2))
# Embed this in your page's <head> as application/ld+json
Use Case 4: Search Intent Clustering
PAA questions reveal the different angles users care about for a topic. Clustering them by intent type helps you plan content that covers a topic completely.
def classify_intent(question: str) -> str:
q = question.lower()
if any(w in q for w in ["what is", "what are", "what does", "define", "meaning"]):
return "informational"
if any(w in q for w in ["how to", "how do", "how can", "steps to", "guide"]):
return "how-to"
if any(w in q for w in ["best", "top", "which", "vs", "versus", "compare"]):
return "comparative"
if any(w in q for w in ["price", "cost", "cheap", "free", "buy", "pricing"]):
return "commercial"
return "other"
def cluster_by_intent(topic: str) -> dict:
questions = expand_questions(topic, depth=1)
clusters = {"informational": [], "how-to": [], "comparative": [], "commercial": [], "other": []}
for question in questions:
intent = classify_intent(question)
clusters[intent].append(question)
return clusters
clusters = cluster_by_intent("project management software")
for intent, qs in clusters.items():
if qs:
print(f"\n{intent.upper()} ({len(qs)} questions):")
for q in qs[:3]:
print(f" - {q}")
Use Case 5: Content Gap Analysis
Compare PAA questions for your brand versus competitors to spot topics you have not written about yet.
def find_content_gaps(my_keyword: str, competitor_keyword: str) -> dict:
my_questions = {q["question"] for q in get_paa(my_keyword)}
time.sleep(0.5)
comp_questions = {q["question"] for q in get_paa(competitor_keyword)}
return {
"only_mine": my_questions - comp_questions,
"only_competitor": comp_questions - my_questions,
"shared": my_questions & comp_questions,
}
gaps = find_content_gaps("serpbase api", "serpapi alternative")
print("Questions competitors answer but you don't:")
for q in gaps["only_competitor"]:
print(f" - {q}")
Combining PAA with Related Searches
The related_searches field in a SERP response complements PAA well. PAA gives you questions; related searches give you noun phrases. Together they form a comprehensive keyword map.
def full_keyword_map(seed: str) -> dict:
resp = requests.post(
"https://api.serpbase.dev/google/search",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={"q": seed, "gl": "us", "hl": "en"},
timeout=15,
).json()
return {
"seed": seed,
"paa_questions": [q["question"] for q in resp.get("people_also_ask", [])],
"related_terms": [r["query"] for r in resp.get("related_searches", [])],
"top_ranking_urls": [r["link"] for r in resp.get("organic", [])[:5]],
}
keyword_map = full_keyword_map("python web scraping")
print(f"PAA questions: {len(keyword_map['paa_questions'])}")
print(f"Related terms: {len(keyword_map['related_terms'])}")
Cost at Scale
PAA-based keyword research is query-efficient because each search returns multiple questions:
| Task | Queries needed | Cost at SerpBase |
|---|---|---|
| PAA for 100 seed keywords | 100 | $0.03 |
| Depth-2 expansion (100 seeds) | ~1,500 | $0.45 |
| Content briefs for 50 topics | 50 | $0.015 |
| Weekly gap analysis (20 topics) | 40 | $0.012 |
At $0.30/1,000 queries, even aggressive PAA research costs less than a cup of coffee.
Tips for Better PAA Data
- PAA varies by query phrasing — "how to lose weight" and "weight loss tips" can return different PAA sets. Test multiple phrasings.
- PAA changes over time — Run the same queries monthly to catch new questions Google adds.
- PAA is geographic — Use
gl=gbfor UK questions,gl=defor Germany. PAA varies significantly by market. - Combine with organic rankings — A question that appears in PAA AND has low-authority pages ranking for it is a high-opportunity target.
Summary
People Also Ask data is one of the most underused signals in SEO. Every PAA entry is a confirmed user question that Google has validated as relevant and searchable. With a SERP API, you can collect thousands of these questions programmatically, cluster them by intent, use them for content briefs, generate FAQ schema, and identify content gaps — all without any browser automation or proxy infrastructure.
With SerpBase at $0.30 per 1,000 queries, a full keyword research workflow for a 100-keyword project costs less than $0.50 in API calls.
Get your free API key at SerpBase — 100 searches included, no credit card required.