Tutorial

How to Boost RAG Accuracy with SERP Augmentation

Improve RAG pipeline accuracy by augmenting vector results with live SERP data. Reduce hallucination on time-sensitive queries. Python tutorial.

Standard RAG pipelines retrieve context from a vector store, but that context goes stale the moment your documents are indexed. For queries about pricing, recent events, or current rankings, stale context leads to confidently wrong answers. SERP augmentation fixes this by adding a parallel retrieval path: when the query looks time-sensitive, fetch live search results and merge them with vector results before passing to the LLM. This tutorial shows how to add SERP augmentation to any existing RAG pipeline. Each search call costs $0.005 via the Scavio API.

Prerequisites

  • Python 3.9+ installed
  • An existing RAG pipeline (any vector store)
  • requests library installed
  • A Scavio API key from scavio.dev

Walkthrough

Step 1: Detect time-sensitive queries

Build a classifier that determines whether a query needs live data. Queries about prices, dates, versions, or current events should trigger SERP augmentation.

Python
import re

TIME_SIGNALS = [
    r'\b202[4-9]\b', r'\blatest\b', r'\bcurrent\b', r'\bprice\b',
    r'\bpricing\b', r'\btoday\b', r'\brecent\b', r'\bnew\b',
    r'\bversion\b', r'\brelease\b', r'\bupdate\b'
]

def needs_live_data(query: str) -> bool:
    query_lower = query.lower()
    return any(re.search(p, query_lower) for p in TIME_SIGNALS)

# Examples:
for q in ['What is a transformer?', 'Latest Python version 2026', 'Semrush pricing today']:
    print(f'{q}: live_data={needs_live_data(q)}')

Step 2: Build the SERP retrieval function

Create a retriever that returns documents in the same format as your vector store, so they can be merged seamlessly.

Python
import requests, os

API_KEY = os.environ['SCAVIO_API_KEY']

def serp_retrieve(query: str, k: int = 5) -> list:
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': 'us'})
    resp.raise_for_status()
    results = resp.json().get('organic_results', [])[:k]
    return [{
        'content': f'{r["title"]}\n{r.get("snippet", "")}',
        'source': r['link'],
        'retriever': 'serp',
        'position': r['position']
    } for r in results]

Step 3: Merge vector and SERP results

Combine results from both retrievers, deduplicating by URL and giving SERP results a freshness boost in the ranking.

Python
def merge_results(vector_docs: list, serp_docs: list) -> list:
    seen_urls = set()
    merged = []
    # SERP results first (fresh data priority)
    for doc in serp_docs:
        url = doc.get('source', '')
        if url not in seen_urls:
            seen_urls.add(url)
            merged.append(doc)
    # Then vector results
    for doc in vector_docs:
        url = doc.get('source', '')
        if url not in seen_urls:
            seen_urls.add(url)
            merged.append(doc)
    return merged[:10]  # cap at 10 context docs

# Example:
vector_results = [{'content': 'Old pricing data...', 'source': 'https://example.com/old', 'retriever': 'vector'}]
serp_results = serp_retrieve('Semrush pricing 2026')
merged = merge_results(vector_results, serp_results)
for doc in merged:
    print(f'[{doc["retriever"]}] {doc["content"][:60]}...')

Step 4: Integrate into your RAG pipeline

Wrap the augmentation logic into your existing retrieval step. Only call the SERP API when the query is time-sensitive to keep costs minimal.

Python
def augmented_retrieve(query: str, vector_store) -> list:
    # Always get vector results
    vector_docs = vector_store.similarity_search(query, k=5)
    vector_formatted = [{'content': d.page_content, 'source': d.metadata.get('source', ''),
                         'retriever': 'vector'} for d in vector_docs]
    # Conditionally add SERP results
    if needs_live_data(query):
        serp_docs = serp_retrieve(query, k=5)
        return merge_results(vector_formatted, serp_docs)
    return vector_formatted

# Usage in your chain:
# docs = augmented_retrieve(user_query, my_vector_store)
# context = '\n\n'.join(d['content'] for d in docs)
# answer = llm(f'Context:\n{context}\n\nQuestion: {user_query}')

Python Example

Python
import os, re, requests

API_KEY = os.environ['SCAVIO_API_KEY']
TIME_SIGNALS = [r'\b202[4-9]\b', r'\blatest\b', r'\bprice\b', r'\bcurrent\b']

def needs_live_data(query: str) -> bool:
    return any(re.search(p, query.lower()) for p in TIME_SIGNALS)

def serp_retrieve(query: str, k: int = 5) -> list:
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': 'us'})
    return [{'content': f'{r["title"]}\n{r.get("snippet", "")}',
             'source': r['link'], 'retriever': 'serp'}
            for r in resp.json().get('organic_results', [])[:k]]

def augmented_rag(query: str, vector_docs: list) -> list:
    if needs_live_data(query):
        serp_docs = serp_retrieve(query)
        return serp_docs + vector_docs
    return vector_docs

query = 'Semrush pricing 2026'
result = augmented_rag(query, [{'content': 'old data', 'retriever': 'vector'}])
for r in result:
    print(f'[{r["retriever"]}] {r["content"][:60]}')

JavaScript Example

JavaScript
const API_KEY = process.env.SCAVIO_API_KEY;
const TIME_SIGNALS = [/\b202[4-9]\b/i, /\blatest\b/i, /\bprice\b/i, /\bcurrent\b/i];

function needsLiveData(query) {
  return TIME_SIGNALS.some(p => p.test(query));
}

async function serpRetrieve(query, k = 5) {
  const resp = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST',
    headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, country_code: 'us' })
  });
  const data = await resp.json();
  return (data.organic_results || []).slice(0, k)
    .map(r => ({ content: `${r.title}\n${r.snippet || ''}`, source: r.link, retriever: 'serp' }));
}

async function augmentedRag(query, vectorDocs) {
  if (needsLiveData(query)) {
    const serp = await serpRetrieve(query);
    return [...serp, ...vectorDocs];
  }
  return vectorDocs;
}

augmentedRag('Semrush pricing 2026', [{ content: 'old', retriever: 'vector' }])
  .then(docs => docs.forEach(d => console.log(`[${d.retriever}] ${d.content.slice(0, 60)}`)));

Expected Output

JSON
What is a transformer?: live_data=False
Latest Python version 2026: live_data=True
Semrush pricing today: live_data=True

[serp] Semrush Pricing Plans 2026 - Complete Breakdown...
[serp] Semrush Review: Is the $139.95/mo Pro Plan Worth It?...
[serp] Semrush vs Ahrefs Pricing Comparison (May 2026)...
[vector] old data

Related Tutorials

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.9+ installed. An existing RAG pipeline (any vector store). requests library installed. A Scavio API key from scavio.dev. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, 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.

Start Building

Improve RAG pipeline accuracy by augmenting vector results with live SERP data. Reduce hallucination on time-sensitive queries. Python tutorial.