DEV Community

Cecilia Hill
Cecilia Hill

Posted on

How to Extract Titles, Links, and Snippets from SERP JSON

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

For AI agents:

search result → title + snippet + URL → LLM context
Enter fullscreen mode Exit fullscreen mode

For competitor monitoring:

SERP results → domains + snippets → competitor visibility analysis
Enter fullscreen mode Exit fullscreen mode

For content research:

keyword → top result titles → content angle analysis
Enter fullscreen mode Exit fullscreen mode

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.

View API documentation

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."
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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."
  }
]
Enter fullscreen mode Exit fullscreen mode

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."
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Run it:

python extract_serp_fields.py
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Let’s extract that list:

organic_results = data.get("organic_results", [])

print(organic_results)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Use it like this:

organic_results = data.get("organic_results", [])
clean_results = extract_basic_fields(organic_results)

print(clean_results)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 "",
    }
Enter fullscreen mode Exit fullscreen mode

Now update the extraction function:

def extract_basic_fields(results):
    return [
        normalize_result(item)
        for item in results
    ]
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

Run it:

python extract_serp_fields.py
Enter fullscreen mode Exit fullscreen mode

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.'}
Enter fullscreen mode Exit fullscreen mode

Step 6: Support multiple possible result keys

Different SERP APIs may store organic results under different keys.

Common examples include:

organic_results
organic
results
Enter fullscreen mode Exit fullscreen mode

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 []
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Use it like this:

save_to_csv(clean_results, "serp_results.csv")
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

Run it:

python extract_serp_fields.py
Enter fullscreen mode Exit fullscreen mode

You should get:

serp_results.csv
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

should become:

example.com
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 "",
    }
Enter fullscreen mode Exit fullscreen mode

Also update the CSV fieldnames:

fieldnames = [
    "position",
    "title",
    "link",
    "domain",
    "snippet",
]
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Create a .env file:

SERP_API_KEY=your_api_key_here
SERP_API_URL=https://your-serp-api-endpoint.example.com/search
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

This is the same pattern:

API response → organic results → normalized fields → CSV or LLM context
Enter fullscreen mode Exit fullscreen mode

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 ""
Enter fullscreen mode Exit fullscreen mode

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 ""
Enter fullscreen mode Exit fullscreen mode

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 "",
    }
Enter fullscreen mode Exit fullscreen mode

Then call it with:

normalize_result(item, result_type="organic")
Enter fullscreen mode Exit fullscreen mode

or:

normalize_result(item, result_type="related_question")
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)