Most SERP APIs return search results in JSON.
That sounds simple, but the response can still be messy.
A single search result page may include:
- organic results
- ads
- local results
- maps
- shopping results
- news
- images
- videos
- related questions
- related searches
For many SEO, AI, or data workflows, you do not need everything.
You often only need the core fields:
title
link
snippet
position
In this tutorial, we will build a simple Python script that extracts titles, links, snippets, and ranking positions from SERP JSON.
The workflow looks like this:
SERP API JSON → extract organic results → normalize fields → save or use data
Why extract only these fields?
Titles, links, and snippets are the basic building blocks of search result data.
They are useful for many workflows.
For SEO:
keyword → title + link + position → ranking report
For AI agents:
search result → title + snippet + URL → LLM context
For competitor monitoring:
SERP results → domains + snippets → competitor visibility analysis
For content research:
keyword → top result titles → content angle analysis
A raw SERP API response can contain a lot of extra metadata. Extracting only the useful fields makes the data easier to store, analyze, and pass into other systems.
Example SERP JSON
A typical SERP API response may look like this:
{
"query": "best project management software",
"organic_results": [
{
"position": 1,
"title": "Best Project Management Software Tools",
"link": "https://example.com/project-management",
"snippet": "Compare project management tools, pricing, features, and use cases."
},
{
"position": 2,
"title": "Top Project Management Apps for Teams",
"link": "https://example.org/apps",
"snippet": "A guide to the best project management apps for remote and hybrid teams."
}
]
}
From this response, we want to extract:
[
{
"position": 1,
"title": "Best Project Management Software Tools",
"link": "https://example.com/project-management",
"snippet": "Compare project management tools, pricing, features, and use cases."
},
{
"position": 2,
"title": "Top Project Management Apps for Teams",
"link": "https://example.org/apps",
"snippet": "A guide to the best project management apps for remote and hybrid teams."
}
]
Step 1: Create a sample JSON file
Create a file named serp_response.json:
{
"query": "best project management software",
"organic_results": [
{
"position": 1,
"title": "Best Project Management Software Tools",
"link": "https://example.com/project-management",
"snippet": "Compare project management tools, pricing, features, and use cases."
},
{
"position": 2,
"title": "Top Project Management Apps for Teams",
"link": "https://example.org/apps",
"snippet": "A guide to the best project management apps for remote and hybrid teams."
},
{
"position": 3,
"title": "Project Management Software Reviews",
"link": "https://example.net/reviews",
"snippet": "Read reviews of popular project management platforms."
}
]
}
In a real project, this JSON would come from your SERP API provider.
For now, using a local file makes the extraction logic easier to understand.
Step 2: Load the JSON in Python
Create a file named extract_serp_fields.py:
import json
def load_json(filename):
with open(filename, "r", encoding="utf-8") as file:
return json.load(file)
data = load_json("serp_response.json")
print(data)
Run it:
python extract_serp_fields.py
You should see the JSON printed in your terminal.
Step 3: Extract organic results
Most SERP APIs put organic search results under a key like:
organic_results
Let’s extract that list:
organic_results = data.get("organic_results", [])
print(organic_results)
Using .get() is safer than directly accessing the key.
This avoids errors if the response does not contain organic_results.
Step 4: Extract title, link, snippet, and position
Now let’s loop through the organic results and extract only the fields we need.
def extract_basic_fields(results):
extracted = []
for item in results:
extracted.append({
"position": item.get("position"),
"title": item.get("title"),
"link": item.get("link"),
"snippet": item.get("snippet"),
})
return extracted
Use it like this:
organic_results = data.get("organic_results", [])
clean_results = extract_basic_fields(organic_results)
print(clean_results)
Now the output only contains the fields we care about.
Step 5: Handle different field names
Not every SERP API uses the same field names.
Some APIs may use:
rank instead of position
url instead of link
description instead of snippet
So it is a good idea to normalize fields.
def normalize_result(item):
return {
"position": item.get("position") or item.get("rank") or "",
"title": item.get("title") or "",
"link": item.get("link") or item.get("url") or "",
"snippet": item.get("snippet") or item.get("description") or "",
}
Now update the extraction function:
def extract_basic_fields(results):
return [
normalize_result(item)
for item in results
]
This makes the script more flexible across different SERP API providers.
Full script
Here is the complete script so far:
import json
def load_json(filename):
with open(filename, "r", encoding="utf-8") as file:
return json.load(file)
def normalize_result(item):
return {
"position": item.get("position") or item.get("rank") or "",
"title": item.get("title") or "",
"link": item.get("link") or item.get("url") or "",
"snippet": item.get("snippet") or item.get("description") or "",
}
def extract_basic_fields(results):
return [
normalize_result(item)
for item in results
]
def main():
data = load_json("serp_response.json")
organic_results = data.get("organic_results", [])
clean_results = extract_basic_fields(organic_results)
for result in clean_results:
print(result)
if __name__ == "__main__":
main()
Run it:
python extract_serp_fields.py
Example output:
{'position': 1, 'title': 'Best Project Management Software Tools', 'link': 'https://example.com/project-management', 'snippet': 'Compare project management tools, pricing, features, and use cases.'}
{'position': 2, 'title': 'Top Project Management Apps for Teams', 'link': 'https://example.org/apps', 'snippet': 'A guide to the best project management apps for remote and hybrid teams.'}
{'position': 3, 'title': 'Project Management Software Reviews', 'link': 'https://example.net/reviews', 'snippet': 'Read reviews of popular project management platforms.'}
Step 6: Support multiple possible result keys
Different SERP APIs may store organic results under different keys.
Common examples include:
organic_results
organic
results
Let’s create a helper function:
def get_organic_results(data):
possible_keys = [
"organic_results",
"organic",
"results",
]
for key in possible_keys:
value = data.get(key)
if isinstance(value, list):
return value
return []
Now update the main function:
def main():
data = load_json("serp_response.json")
organic_results = get_organic_results(data)
clean_results = extract_basic_fields(organic_results)
for result in clean_results:
print(result)
This makes your parser less dependent on one provider’s response format.
Step 7: Save extracted results to CSV
Printing the results is useful for testing, but you may want to save them.
Python has a built-in csv module.
import csv
def save_to_csv(rows, filename="serp_results.csv"):
fieldnames = [
"position",
"title",
"link",
"snippet",
]
with open(filename, mode="w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for row in rows:
writer.writerow(row)
Use it like this:
save_to_csv(clean_results, "serp_results.csv")
Now you can open the results in Excel, Google Sheets, or any dashboard tool.
CSV version of the full script
import json
import csv
def load_json(filename):
with open(filename, "r", encoding="utf-8") as file:
return json.load(file)
def get_organic_results(data):
possible_keys = [
"organic_results",
"organic",
"results",
]
for key in possible_keys:
value = data.get(key)
if isinstance(value, list):
return value
return []
def normalize_result(item):
return {
"position": item.get("position") or item.get("rank") or "",
"title": item.get("title") or "",
"link": item.get("link") or item.get("url") or "",
"snippet": item.get("snippet") or item.get("description") or "",
}
def extract_basic_fields(results):
return [
normalize_result(item)
for item in results
]
def save_to_csv(rows, filename="serp_results.csv"):
fieldnames = [
"position",
"title",
"link",
"snippet",
]
with open(filename, mode="w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for row in rows:
writer.writerow(row)
def main():
data = load_json("serp_response.json")
organic_results = get_organic_results(data)
clean_results = extract_basic_fields(organic_results)
save_to_csv(clean_results, "serp_results.csv")
print("Saved extracted SERP results to serp_results.csv")
if __name__ == "__main__":
main()
Run it:
python extract_serp_fields.py
You should get:
serp_results.csv
Step 8: Extract domains from links
For SEO and competitor analysis, it is often useful to extract the domain.
For example:
https://www.example.com/project-management
should become:
example.com
Add this helper function:
from urllib.parse import urlparse
def extract_domain(url):
if not url:
return ""
parsed = urlparse(url)
domain = parsed.netloc.lower()
if domain.startswith("www."):
domain = domain[4:]
return domain
Update normalize_result:
def normalize_result(item):
link = item.get("link") or item.get("url") or ""
return {
"position": item.get("position") or item.get("rank") or "",
"title": item.get("title") or "",
"link": link,
"domain": extract_domain(link),
"snippet": item.get("snippet") or item.get("description") or "",
}
Also update the CSV fieldnames:
fieldnames = [
"position",
"title",
"link",
"domain",
"snippet",
]
Now your CSV includes domains, which is useful for competitor analysis.
Step 9: Turn results into LLM context
If you are building an AI agent or RAG workflow, you may want to convert search results into prompt context.
def build_llm_context(results, max_results=5):
blocks = []
for index, result in enumerate(results[:max_results], start=1):
block = f"""
Source [{index}]
Position: {result["position"]}
Title: {result["title"]}
URL: {result["link"]}
Snippet: {result["snippet"]}
""".strip()
blocks.append(block)
return "\n\n".join(blocks)
Example output:
Source [1]
Position: 1
Title: Best Project Management Software Tools
URL: https://example.com/project-management
Snippet: Compare project management tools, pricing, features, and use cases.
Source [2]
Position: 2
Title: Top Project Management Apps for Teams
URL: https://example.org/apps
Snippet: A guide to the best project management apps for remote and hybrid teams.
This is much cleaner than feeding raw JSON into the model.
Step 10: Extract from a live SERP API response
In a real project, you will usually get JSON from an API request instead of a local file.
Install dependencies:
pip install requests python-dotenv
Create a .env file:
SERP_API_KEY=your_api_key_here
SERP_API_URL=https://your-serp-api-endpoint.example.com/search
Then fetch live data:
import os
import requests
from dotenv import load_dotenv
load_dotenv()
SERP_API_KEY = os.getenv("SERP_API_KEY")
SERP_API_URL = os.getenv("SERP_API_URL")
def fetch_serp_json(query, location="United States", language="en"):
if not SERP_API_KEY:
raise ValueError("Missing SERP_API_KEY environment variable")
if not SERP_API_URL:
raise ValueError("Missing SERP_API_URL environment variable")
params = {
"api_key": SERP_API_KEY,
"engine": "google",
"q": query,
"location": location,
"language": language,
"output": "json",
}
response = requests.get(SERP_API_URL, params=params, timeout=30)
response.raise_for_status()
return response.json()
Then use the same extraction functions:
data = fetch_serp_json("best project management software")
organic_results = get_organic_results(data)
clean_results = extract_basic_fields(organic_results)
save_to_csv(clean_results, "live_serp_results.csv")
This is the same pattern:
API response → organic results → normalized fields → CSV or LLM context
Common issues to handle
SERP JSON can vary.
Here are a few things to check in production.
Missing snippets
Some results may not include a snippet.
Your code should default to an empty string:
"snippet": item.get("snippet") or item.get("description") or ""
Missing links
Some SERP blocks may not have a URL.
For example, related questions may contain answers but no direct link.
Use:
"link": item.get("link") or item.get("url") or ""
Non-organic results
Not every result is an organic result.
Ads, local packs, maps, shopping, and news may use different fields.
If you want to extract those too, add a result_type column.
def normalize_result(item, result_type="organic"):
link = item.get("link") or item.get("url") or ""
return {
"result_type": result_type,
"position": item.get("position") or item.get("rank") or "",
"title": item.get("title") or item.get("question") or "",
"link": link,
"snippet": item.get("snippet") or item.get("description") or item.get("answer") or "",
}
Then call it with:
normalize_result(item, result_type="organic")
or:
normalize_result(item, result_type="related_question")
Very large responses
If you are processing many keywords, avoid printing everything to the terminal.
Save to CSV, JSONL, SQLite, or a database instead.
For larger workflows, you may want:
- retry logic
- rate limit handling
- batch processing
- logging
- scheduled runs
- database storage
- duplicate detection
What can you build with extracted SERP fields?
Once you can extract titles, links, snippets, and positions, you can build many useful tools.
Examples:
- keyword rank tracker
- competitor visibility report
- AI search assistant
- market research bot
- content research dashboard
- local SEO tracker
- SERP snapshot archive
- source-grounded LLM workflow
- automated weekly SEO report
The extraction step is small, but it is the foundation for many search data products.
What to check before choosing a SERP API
Before choosing a SERP API provider, test it with your real queries.
Check:
- Does it return clean JSON?
- Are titles, links, snippets, and positions complete?
- Does it support country, city, language, and device targeting?
- Can you request HTML for debugging?
- Are rich SERP features included when needed?
- Are failed requests billed?
- How much cleanup does your code need?
- Does the response fit your SEO or AI workflow?
Providers such as SerpApi, Serper, SearchAPI, Bright Data, DataForSEO, and Talordata can all be tested for this kind of workflow.
The best provider is the one that gives you usable SERP data with the least extra parsing work.
Final thoughts
Extracting titles, links, and snippets from SERP JSON is a small but important task.
The basic process is:
SERP JSON → organic results → title/link/snippet/position → CSV, database, or LLM context
Once you have clean fields, you can build ranking reports, competitor monitors, AI agents, SEO dashboards, or research tools.
For testing this workflow, Talordata is one SERP API worth comparing. It supports structured SERP data, JSON / HTML response formats, geo-targeted search results, and search workflows for SEO monitoring, competitor tracking, market research, and AI agents.
Talordata also offers 1,000 free API responses after signup, which is enough to test real SERP JSON extraction before choosing a provider.
Top comments (0)