ScavioScavio
ProductPricingDocs
Sign InGet Started
  1. Home
  2. Tutorials
  3. Ground an AI Agent With Real Search Data So It Stops Hallucinating
Tutorial

Ground an AI Agent With Real Search Data So It Stops Hallucinating

Stop your agent inventing GitHub stars and follower counts. Force a search tool call, then make the model narrate only the returned JSON.

Get Free API KeyAPI Docs

To stop an agent fabricating facts like GitHub stars or follower counts, force it to call a search tool and reason only over the returned JSON, never produce numbers from memory. Give the model one tool that hits Scavio's Google SERP endpoint (POST https://api.scavio.dev/api/v1/google) and Reddit search, then write a system prompt that bans inventing any number, date, or proper noun that didn't come back in the tool response. The model becomes a narrator over sourced data instead of a guesser. This grounds public, indexed facts only. A SERP API can confirm what Google already shows publicly; it cannot read a private dashboard or any metric behind a login, so be explicit with the model about that boundary.

Prerequisites

  • A Scavio API key from scavio.dev (free tier gives 50 one-time credits, 1 request/sec)
  • Python 3.10+ or Node 18+ with an LLM client that supports tool/function calling
  • Basic familiarity with how your LLM exposes tools and parses tool results
  • An understanding that the model must be told to never output a number that isn't in the tool JSON

Walkthrough

Step 1: Define the search tool the model is allowed to call

Expose exactly one data-fetch tool to the model: a search function that posts to Scavio's Google SERP endpoint. The tool takes a single query string and returns structured JSON. Keep the schema tight so the model can only ask for a search, not freelance. The point is that any factual claim the agent makes must trace back to a tool call you can audit. If the model wants a GitHub star count, it has to search for it, not recall it.

Python
TOOLS = [{
  "type": "function",
  "function": {
    "name": "web_search",
    "description": "Search Google for current public facts. Returns sourced JSON. Use this for any number, date, or name.",
    "parameters": {
      "type": "object",
      "properties": {
        "query": {"type": "string", "description": "The search query"}
      },
      "required": ["query"]
    }
  }
}]

Step 2: Implement the tool against Scavio's Google endpoint

When the model calls web_search, run a POST to https://api.scavio.dev/api/v1/google with Authorization: Bearer {API_KEY}. Set light_request to false to get organic results, people_also_ask, knowledge_graph, and related_searches in one call (this costs 2 credits instead of 1). Return only the fields the model needs, with each value paired to its source URL so the model can cite where a number came from.

Python
import requests

def web_search(query: str) -> dict:
    r = requests.post(
        "https://api.scavio.dev/api/v1/google",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={"query": query, "light_request": False},
        timeout=30,
    )
    r.raise_for_status()
    data = r.json()
    return {
        "organic": data.get("organic", [])[:5],
        "knowledge_graph": data.get("knowledge_graph"),
        "people_also_ask": data.get("people_also_ask", []),
    }

Step 3: Add Reddit search for community-sourced signal

Some facts live in discussion, not on indexed pages. Add a second tool that posts to https://api.scavio.dev/api/v1/reddit/search for first-hand reports, complaints, and opinions. Treat Reddit results as claims made by people, not verified facts, and tell the model to attribute them ('a Reddit user reported...') rather than state them as ground truth. This keeps the agent honest about where softer signal comes from.

Python
def reddit_search(query: str) -> dict:
    r = requests.post(
        "https://api.scavio.dev/api/v1/reddit/search",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={"query": query},
        timeout=30,
    )
    r.raise_for_status()
    return {"posts": r.json().get("posts", [])[:5]}

Step 4: Write the grounding system prompt

This is the part that actually stops hallucination. Instruct the model: never output a number, date, version, or proper noun unless it appears verbatim in a tool result. If a search returns nothing, say you couldn't verify it rather than guessing. Cite the source URL next to every figure. State plainly that the search tool covers public indexed data only and cannot see private dashboards or behind-login metrics, so for those the answer is 'I can't access that.'

Python
SYSTEM = '''You are a research agent. Rules:
1. Never state a number, date, version, or named entity unless it came back in a tool result. No recalling from memory.
2. For any factual claim, call web_search first, then narrate ONLY the returned values.
3. Put the source URL next to each figure.
4. If a search returns nothing useful, say "I could not verify this" instead of guessing.
5. The search tool sees public indexed data only. It cannot read private dashboards or login-gated metrics. For those, reply that you cannot access them.'''

Step 5: Run the loop and verify the model only narrates returned values

Send the user question with the tools and system prompt, execute any tool calls, feed the JSON back, and let the model write its answer. Then spot-check: every number in the output should be findable in a tool response. If you see a figure that isn't, your prompt is too loose, tighten rule 1. The honest failure mode you want is the agent saying 'I couldn't verify the follower count' rather than confidently printing a wrong one.

Python
messages = [
    {"role": "system", "content": SYSTEM},
    {"role": "user", "content": "How many GitHub stars does the project X have, and what do people say about it?"},
]
# 1. model -> requests web_search("project X github stars")
# 2. you run web_search, append the JSON as a tool message
# 3. model -> may also call reddit_search for sentiment
# 4. model writes final answer using ONLY those returned values, with source URLs

Python Example

Python
import os
import requests

API_KEY = os.environ["SCAVIO_API_KEY"]


def web_search(query: str) -> dict:
    r = requests.post(
        "https://api.scavio.dev/api/v1/google",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={"query": query, "light_request": False},
        timeout=30,
    )
    r.raise_for_status()
    data = r.json()
    return {
        "organic": data.get("organic", [])[:5],
        "knowledge_graph": data.get("knowledge_graph"),
        "people_also_ask": data.get("people_also_ask", []),
    }


def reddit_search(query: str) -> dict:
    r = requests.post(
        "https://api.scavio.dev/api/v1/reddit/search",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={"query": query},
        timeout=30,
    )
    r.raise_for_status()
    return {"posts": r.json().get("posts", [])[:5]}


SYSTEM = (
    "You are a research agent. Never state a number, date, version, or named "
    "entity unless it came back in a tool result; do not recall from memory. "
    "For any factual claim, call web_search first and narrate only the returned "
    "values, with the source URL next to each figure. If a search returns nothing "
    "useful, say you could not verify it. The search tool sees public indexed data "
    "only; it cannot read private dashboards or login-gated metrics."
)

if __name__ == "__main__":
    # The LLM calls web_search('project X github stars'); you return this JSON;
    # the model then narrates ONLY these values. Example of the grounding call:
    result = web_search("project X github stars")
    print(result["knowledge_graph"])
    print([item["link"] for item in result["organic"]])

JavaScript Example

JavaScript
const API_KEY = process.env.SCAVIO_API_KEY;

async function webSearch(query) {
  const res = await fetch("https://api.scavio.dev/api/v1/google", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, light_request: false }),
  });
  if (!res.ok) throw new Error(`Scavio ${res.status}`);
  const data = await res.json();
  return {
    organic: (data.organic || []).slice(0, 5),
    knowledge_graph: data.knowledge_graph,
    people_also_ask: data.people_also_ask || [],
  };
}

async function redditSearch(query) {
  const res = await fetch("https://api.scavio.dev/api/v1/reddit/search", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query }),
  });
  if (!res.ok) throw new Error(`Scavio ${res.status}`);
  const data = await res.json();
  return { posts: (data.posts || []).slice(0, 5) };
}

const SYSTEM =
  "You are a research agent. Never state a number, date, version, or named " +
  "entity unless it came back in a tool result; do not recall from memory. " +
  "For any factual claim, call web_search first and narrate only the returned " +
  "values, with the source URL next to each figure. If a search returns nothing, " +
  "say you could not verify it. The search tool sees public indexed data only; " +
  "it cannot read private dashboards or login-gated metrics.";

// The LLM calls webSearch('project X github stars'); feed the JSON back so the
// model narrates ONLY these returned values:
webSearch("project X github stars").then((r) => {
  console.log(r.knowledge_graph);
  console.log(r.organic.map((o) => o.link));
});

Expected Output

JSON
{
  "organic": [
    {
      "title": "project X - GitHub",
      "link": "https://github.com/org/project-x",
      "snippet": "project X is an open-source ... 12.4k stars"
    }
  ],
  "knowledge_graph": {
    "title": "project X",
    "type": "Software repository"
  },
  "people_also_ask": [
    { "question": "Is project X actively maintained?" }
  ]
}

Related Tutorials

  • How to Fact-Check YouTube Videos With an API
  • Build a Real-Time Fact-Checker That Grounds an LLM on Live Search

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.

A Scavio API key from scavio.dev (free tier gives 50 one-time credits, 1 request/sec). Python 3.10+ or Node 18+ with an LLM client that supports tool/function calling. Basic familiarity with how your LLM exposes tools and parses tool results. An understanding that the model must be told to never output a number that isn't in the tool JSON. 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

Hermes Agent Search API Reliability

Read more
Use Case

Pi Coding Agent Multi-Platform Search

Read more
Best Of

Best Search API for Deep Research Agents in 2026

Read more
Best Of

Best Web Search API for Local LLMs in 2026

Read more
Comparison

Brave Search API vs Scavio

Read more
Solution

Ground LLM Responses with Real-Time Search Data

Read more

Start Building

Stop your agent inventing GitHub stars and follower counts. Force a search tool call, then make the model narrate only the returned JSON.

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