OpenAI Function Calling with Web Search APIs
OpenAI's function calling feature lets models execute tools and return structured data. Pairing it with a web search API gives your GPT-4o applications real-time access to the internet -- useful for research agents, RAG pipelines, and customer support bots that need current information.
This guide compares the best web search APIs to use with OpenAI function calling, with code examples and pricing analysis.
Key Takeaways
- Function calling with web search requires defining a search tool schema and handling the model's tool calls
- SearchHive, Tavily, and SerpApi all work cleanly with OpenAI's function calling format
- SearchHive offers the best value for production use -- search, scraping, and deep research from one API key at $9/mo for 5K credits
- The main tradeoff is latency: each search tool call is a round-trip outside the LLM
How OpenAI Function Calling Works with Search
The pattern is straightforward:
- Define a tool schema describing your search function
- Send the schema with your prompt to the OpenAI API
- The model decides whether to call the search tool
- Execute the search and return results to the model
- The model generates a final answer using the search data
Here's the minimal pattern:
from openai import OpenAI
client = OpenAI()
tools = [
{
"type": "function",
"function": {
"name": "web_search",
"description": "Search the web for current information",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "The search query"}
},
"required": ["query"]
}
}
}
]
messages = [
{"role": "user", "content": "What is the current pricing for Anthropic Claude API?"}
]
# First call -- model decides to search
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto"
)
# Handle the tool call
tool_call = response.choices[0].message.tool_calls[0]
print(f"Model wants to search: {tool_call.function.arguments}")
Web Search APIs Compared for Function Calling
| API | Response Speed | Structured Output | Free Tier | Pricing (per 1K) | Best For |
|---|---|---|---|---|---|
| SearchHive | ~200ms | free JSON formatter | 500 credits | $0.18 (Starter) | All-in-one search + scrape |
| SerpApi | ~300ms | JSON | 250/mo | $25 | Google SERP data |
| Tavily | ~400ms | JSON + answer | 1K/mo | $8 | AI-optimized search |
| Serper.dev | ~150ms | JSON | 2,500 | $50 (Starter) | Fast Google results |
| Brave Search | ~200ms | JSON | $5/mo credit | $5 | Privacy-focused search |
| Exa.ai | ~300ms | JSON | 1K/mo | $7 | Neural/semantic search |
Implementation: SearchHive with OpenAI Function Calling
SearchHive provides three complementary APIs -- SwiftSearch for fast lookups, ScrapeForge for page content extraction, and DeepDive for comprehensive research. Here's how to wire all three as function calling tools:
import json
import requests
from openai import OpenAI
client = OpenAI()
SEARCHHIVE_KEY = "your-searchhive-key"
def swift_search(query: str) -> str:
# Fast web search returning top results with snippets
resp = requests.get(
"https://api.searchhive.dev/v1/swift-search",
headers={"Authorization": f"Bearer {SEARCHHIVE_KEY}"},
params={"query": query, "limit": 5}
)
results = []
for r in resp.json().get("results", []):
results.append({"title": r["title"], "url": r["url"], "snippet": r["snippet"]})
return json.dumps(results, indent=2)
def scrape_forge(url: str) -> str:
# Extract clean content from any web page as markdown
resp = requests.post(
"https://api.searchhive.dev/v1/scrape-forge",
headers={"Authorization": f"Bearer {SEARCHHIVE_KEY}", "Content-Type": "application/json"},
json={"url": url, "format": "markdown"}
)
return resp.json().get("content", "Failed to scrape")[:4000]
def deep_dive(query: str) -> str:
# Comprehensive research on a topic with synthesized analysis
resp = requests.get(
"https://api.searchhive.dev/v1/deep-dive",
headers={"Authorization": f"Bearer {SEARCHHIVE_KEY}"},
params={"query": query}
)
return resp.json().get("summary", "No results")[:3000]
# Define the tool schemas
tools = [
{
"type": "function",
"function": {
"name": "swift_search",
"description": "Search the web for current information. Returns titles, URLs, and snippets.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "scrape_forge",
"description": "Extract the full content of a web page as markdown.",
"parameters": {
"type": "object",
"properties": {
"url": {"type": "string", "description": "Full URL to scrape"}
},
"required": ["url"]
}
}
},
{
"type": "function",
"function": {
"name": "deep_dive",
"description": "In-depth research on a topic with synthesized analysis.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Research question"}
},
"required": ["query"]
}
}
}
]
# Map function names to actual functions
tool_map = {
"swift_search": swift_search,
"scrape_forge": scrape_forge,
"deep_dive": deep_dive
}
def run_agent(user_query: str, max_turns: int = 5):
# Run an agent that can search, scrape, and research
messages = [{"role": "user", "content": user_query}]
for turn in range(max_turns):
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto"
)
msg = response.choices[0].message
messages.append(msg)
if not msg.tool_calls:
return msg.content
for tool_call in msg.tool_calls:
fn_name = tool_call.function.name
fn_args = json.loads(tool_call.function.arguments)
print(f"[Tool call] {fn_name}({fn_args})")
result = tool_map[fn_name](**fn_args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
})
return "Max turns reached"
# Example usage
answer = run_agent("Compare pricing of the top 3 AI coding assistants")
print(answer)
Implementation: Tavily with Function Calling
import tavily
def tavily_search(query: str) -> str:
# Search using Tavily AI-optimized search engine
client = tavily.TavilyClient(api_key="your-tavily-key")
result = client.search(query=query, max_results=5)
formatted = []
for r in result.get("results", []):
formatted.append(f"- {r['title']}: {r['content'][:200]}")
return "\n".join(formatted)
Implementation: Serper.dev with Function Calling
import requests
def serper_search(query: str) -> str:
# Search Google via Serper.dev API
resp = requests.post(
"https://google.serper.dev/search",
headers={"X-API-KEY": "your-serper-key", "Content-Type": "application/json"},
json={"q": query, "num": 5}
)
results = []
for r in resp.json().get("organic", []):
results.append(f"- {r['title']}: {r['snippet']}")
return "\n".join(results)
Advanced: Multi-Step Research with Parallel Tool Calls
GPT-4o supports parallel function calls -- the model can request multiple searches at once. This is where SearchHive's unified API shines -- one API key, consistent response format, and no rate limit conflicts between different search providers.
Pricing Deep Dive
Let's look at what 10,000 search queries per month costs across providers:
| Provider | 10K queries/mo | What you get |
|---|---|---|
| Serper.dev | $50 | Google results only |
| Brave Search | $50 | Independent index |
| SerpApi | $75 | Google + formatting |
| Tavily | $80 | AI-optimized results |
| Exa.ai | $70 | Neural search |
| SearchHive Starter | $9 | Search + scrape + deep research |
SearchHive's credit system is flexible -- 5,000 credits on the Starter plan covers a mix of searches, scrapes, and deep dives. At scale, the Builder plan gives you 100K credits for $49/month -- roughly $0.0005 per search.
Verdict
For OpenAI function calling with web search, your best option depends on what you need:
- Google SERP data specifically: SerpApi or Serper.dev -- both deliver clean Google results
- AI-optimized search: Tavily -- designed for LLM consumption
- Semantic search: Exa -- best for conceptual queries
- Production applications needing multiple capabilities: SearchHive -- one API key gives you search, scraping, and deep research. The cost advantage is massive: $9/month vs $50-80/month for a single capability from competitors
The function calling integration is identical across all providers -- they're all just HTTP APIs returning JSON. The difference is in pricing, response quality, and whether you get additional capabilities like page scraping.
Start with SearchHive's free tier -- 500 credits, no credit card needed. See the API documentation for detailed integration guides.
Read more: /blog/openai-function-calling-tutorial | /compare/tavily | /compare/serpapi