
Most AI agent hallucinations aren’t model failures. They’re tool failures. When your agent uses web search for real-time data, it gets headlines instead of numbers, and fills the gap with a confident guess.
My AI agent searched for “S&P 500 today” and got back:
Markets rally as investors digest Fed comments - Reuters
A headline. Not a number.
The agent needed 6,830.71.
Instead, it guessed. Confidently. From training data that was months old.
In testing, this happened 10 out of 15 times.
The fix wasn’t the model. It was the tool.
The architectural lesson: giving an agent search is not the same as giving it data access.
TL;DR: Search returns headlines. APIs return numbers. If your agent uses search for facts, it’s guessing. Give it structured APIs instead.
This is based on running a production AI agent that generates daily market briefings. The full experiment documents how search fails even with capable models.
| Web Search | Structured API | |
|---|---|---|
| Returns | Headlines, snippets | JSON with exact values |
| Accuracy | 33% (5/15 correct) | 100% (15/15 correct) |
| Consistency | Varies by query phrasing | Deterministic |
| Best for | Context, sentiment | Facts, numbers, scores |
When to Use Search vs APIs
Use APIs when your agent needs exact values: prices, scores, temperatures, exchange rates. Anything with a ground truth number.
Use search when your agent needs context: explanations, sentiment, trending topics, background analysis.
Most production agents need both, but for different tasks. The mistake is using search for everything.
Search Returns Context, Not Data
My agent Athena handles stock prices, macro indicators, sector analysis, and watchlist updates. The problem isn’t limited to local models. Even frontier models get this wrong.
The root cause is simpler than model quality. It’s what search actually returns.
When an agent searches for “AMZN stock price,” DuckDuckGo returns something like:
Amazon stock drops amid broader tech selloff - CNBC
AMZN: Amazon.com Inc - Latest news and analysis
Tech stocks mixed as market digests earnings season
Headlines. Snippets. Context about the stock. But not the price.
When the same agent calls a financial API:
{
"Global Quote": {
"01. symbol": "AMZN",
"05. price": "186.49",
"08. previous close": "189.83",
"10. change percent": "-1.76%"
}
}
The exact number. No interpretation required. No gap for the model to fill with a guess.
This distinction matters because LLMs don’t fail gracefully when data is missing. They don’t return “I couldn’t find the price.” They generate the most plausible number from their training data and present it as fact. In my testing, Gemma 3 reported gold at $2,155/oz when the actual price was $5,175/oz. It reported Amazon up 9.2% when it was actually down 9.5%. Not just wrong. Wrong in the opposite direction.
Recent benchmarks confirm this gap. Even the best search APIs score only 63-73% on financial queries across providers like Exa, Parallel, and Google. On time-sensitive questions, accuracy drops further: Exa hits just 24% on FreshQA, Google manages 39%. Search was not built to return the number your agent needs right now.
Three Domains Where Search Fails
The search-vs-API gap isn’t unique to finance. It shows up in every domain where your agent needs a specific, current data point.
Financial Data
Search output (human optimized):
“Markets rally as investors digest Fed comments.” “S&P 500 rises amid broader market recovery.”
What your agent needs: The S&P 500 closed at 6,830.71, up 0.78%.
API output (machine optimized):
{
"close": 6830.71,
"change_percent": 0.78,
"volume": 3842917600
}
Financial APIs like Alpha Vantage, Finnhub, and Yahoo Finance return structured JSON with exact prices, volumes, and percentage changes. There is no ambiguity. The model reads "close": 6830.71 and reports 6,830.71. No interpretation needed.
When search is the only tool available, the model reads “markets rally” and infers a positive number. If training data says the S&P was around 6,000 last time it checked, it might report 6,000 plus a guess. That’s how you get numbers that look plausible but are quietly wrong.
Weather
Search output (human optimized):
“Rain expected in San Francisco this weekend. Temperatures dropping across the Bay Area.”
What your agent needs: San Francisco is currently 58F with 72% humidity and a 40% chance of rain.
API output (machine optimized):
{
"main": {
"temp": 58.2,
"humidity": 72,
"pressure": 1013
},
"weather": [{"description": "light rain"}],
"rain": {"1h": 0.4}
}
OpenWeatherMap updates every 10 minutes with structured temperature, humidity, and precipitation data. The model doesn’t need to estimate the temperature from a headline about “dropping temperatures.” It reads the number directly.
This matters for any agent that makes decisions based on weather: a logistics agent routing deliveries, a farming agent scheduling irrigation, an event planning agent checking conditions. Headlines about “rain expected” are not the same as "rain": {"1h": 0.4}.
Sports
Search output (human optimized):
“Lakers defeat Celtics in overtime thriller. LeBron James leads with 34 points.”
What your agent needs: Lakers 118, Celtics 115. LeBron James: 34 points, 8 rebounds, 6 assists.
API output (machine optimized):
{
"home": {"name": "Lakers", "score": 118},
"away": {"name": "Celtics", "score": 115},
"players": [
{
"name": "LeBron James",
"points": 34,
"rebounds": 8,
"assists": 6
}
]
}
Sports data APIs return exact scores, player statistics, and game metadata. Search results tell you who won and maybe mention a standout performance. But if your agent needs to populate a stats dashboard or calculate fantasy points, “leads with 34 points” is not enough. The rebounds and assists are missing. The final score is buried in a narrative sentence the model has to parse.
Why Search Grounding Breaks Down
The failure isn’t random. There’s a structural reason why search doesn’t work for factual, real-time data tasks.
Search results are optimized for humans, not machines
Google and DuckDuckGo return results ranked for human readers. Headlines are written to attract clicks, not to convey structured data. The snippet “Markets rally as investors digest Fed comments” is useful to a human scanning headlines. It’s useless to an agent that needs a number.
Research on data format confirms this. Flat JSON gives LLMs the best extraction accuracy, with an F1 score of 0.9567 compared to raw HTML (Structured Data Improves LLM Extraction Accuracy, 2024). When you feed an LLM structured JSON, it extracts the right value almost every time. When you feed it a news article, it has to parse, interpret, and hope.
The gap between context and data invites hallucination
When the model receives search results that discuss a topic without containing the specific data point, it faces a choice: admit the data is missing or fill the gap. Most models fill the gap. They generate the most statistically likely number based on training data. No uncertainty flag. No “I couldn’t find this.” Just a confident, stale answer.
Research on tool use in adversarial settings confirms this pattern. Even frontier models equipped with specialized data tools achieve only 67.4% accuracy on adversarial crypto trading tasks. Models preferentially choose web search (55.5% of tool invocations) over authoritative data tools like blockchain analytics APIs, even when the correct answer is directly accessible through the specialized tool. The same dynamic plays out across financial domains: when a structured data source is available, models still default to search.
This is why many AI agent hallucinations trace back to models relying on web search for real-time data instead of structured APIs.
Search results vary by query phrasing
The same question phrased differently returns different results. “AMZN stock price” might surface a finance page with a number. “How is Amazon stock doing” returns editorial content. Your agent’s query construction determines what it gets back, and small phrasing changes can mean the difference between a number and a narrative.
APIs don’t have this problem. GET /quote?symbol=AMZN returns the same structured response every time.
Search vs API: The Architecture Gap
Search architecture
Agent -> Search API -> Headlines -> LLM interpretation -> Guess
API architecture
Agent -> Financial API -> JSON -> Exact value
Search adds an interpretation step where the model must extract a number from narrative text. APIs eliminate that step entirely. The model reads the value directly from structured output.
How to Fix It: Replace Search with Structured APIs
The solution is straightforward. For every domain where your agent needs specific, current data, replace the search tool with a structured data API.
Map your data needs to API endpoints
Start by listing every data point your agent produces. For my market briefing agent, that’s:
| Data Point | Wrong Tool | Right Tool |
|---|---|---|
| Stock prices | DuckDuckGo search | Alpha Vantage / Finnhub API |
| Market indices | Google search | Financial data API |
| Macro indicators | News search | FRED API (Federal Reserve) |
| Weather conditions | Web search | OpenWeatherMap API |
| Sports scores | News search | ESPN / API-Sports |
| Currency exchange rates | Web search | FX API (exchangerate-api) |
| Flight status | News search | Aviation API (FlightAware) |
Each row is a decision point. If the data has a ground truth number, use the tool that returns the number.
Implement APIs as agent tools
In practice, this means defining tool schemas that map directly to API endpoints:
@tool
def get_stock_price(symbol: str) -> dict:
"""Return the latest stock price for a ticker symbol."""
response = requests.get(
f"https://api.example.com/quote/{symbol}",
headers={
"Authorization": f"Bearer {API_KEY}"
},
)
data = response.json()
return {
"symbol": symbol,
"price": data["price"],
"change_percent": data["change_percent"],
}
The model receives {"symbol": "AMZN", "price": 186.49, "change_percent": -1.76}. There is nothing to interpret. Nothing to infer. The number is the number.
Keep search for what it’s good at
Search isn’t useless. It’s good at things APIs can’t do:
- Sentiment and narrative: “What are analysts saying about the Fed decision?” Search is the right tool here. There’s no API for market sentiment in natural language.
- Discovery: “What are the trending topics in AI this week?” Search surfaces emerging information that no structured API tracks.
- Context and background: “Why did Amazon stock drop today?” The explanation lives in news articles, not in a price API.
The rule is simple: if your agent needs a fact (a number, a status, a score), use an API. If it needs context (an explanation, a trend, an opinion), use search.
Production Results: From 10 Wrong to 15 Correct
After switching my Athena agent from search to structured APIs for all numerical data, the results were immediate:
- Financial accuracy: 10 of 15 claims wrong with search. 15 of 15 correct with APIs.
- Latency: Search required multiple queries and result parsing. API calls returned exact data in a single request.
- Consistency: Search results varied by phrasing and time of day. API responses were deterministic for the same input.
The model didn’t change. The prompt didn’t change. Only the tools changed.
APIs remove ambiguity. The model reads the number instead of inferring it from narrative text. With no gap to fill, there is nothing to hallucinate.
The architecture of your agent’s data access matters more than the model’s reasoning capability when the task requires factual accuracy. Financial data has a shelf life measured in minutes. Training data has a shelf life measured in months.
Search forces the model to bridge that gap with a guess. In production, that guess becomes a wrong answer with no error flag attached.
If you’re building error handling into your agent, API responses are also easier to validate. A stock price is either a valid number or it isn’t. A search snippet requires semantic parsing to determine whether it actually answered the question.
If you’re testing your agent, API-based tools are deterministic and testable. You can mock the response, assert on the output, and verify accuracy against ground truth. Search-based tools return different results every time, making test assertions unreliable.
If you’re building a monitoring layer, API responses give you structured data to log and compare. You can detect drift by comparing the API value to the model’s output. With search, you’d need to parse the snippets to figure out what the “right” answer even was.
Key Takeaways
-
Search returns context, APIs return data. Headlines and snippets are useful for understanding a topic. They’re unreliable for extracting specific numbers. If your agent needs a fact, give it an API.
-
LLMs fill data gaps with confident guesses. When search results don’t contain the exact number, the model generates one from training data. No uncertainty markers. No admission of missing data. Just a plausible, stale answer.
-
The fix is architectural, not model-level. Switching from search to structured APIs fixed 10 of 15 wrong claims without changing the model or prompt. Tool selection is an architecture decision.
-
Use search for context, APIs for facts. Search is the right tool for sentiment, discovery, and background. APIs are the right tool for prices, scores, temperatures, and any data point with a ground truth value.
-
Structured responses are easier to test and monitor. API responses are deterministic, mockable, and comparable against ground truth. Search results are variable and require semantic parsing to validate.
If your agent needs facts, search will fail silently. Not because the model is weak, but because the data was never there. Give your agent structured data, and most hallucinations disappear.
This is part of my series on building AI agents in production. For a deeper look at what happens when confident hallucination passes every structural check, see My AI Agent Passed Every Check. 67% of It Was Wrong.. If you’re designing agent tooling, start with error handling patterns and testing strategies. For the experiment that exposed this architectural gap, see Why Local LLMs Hallucinate When Your AI Agent Has Search.
Discussion
Comments are powered by GitHub Discussions. Sign in with GitHub to join the conversation.