SerpApi vs Tavily for AI Agents in 2026: Which Search API Should You Use?

TLDR: Pick SerpApi when your agent needs what a search engine displays: Google rankings, local results, ads, AI Overviews, or any of SerpApi's 100-plus search APIs, returned as structured JSON or Markdown. Pick Tavily when your agent needs evidence from the pages themselves: ranked excerpts, optional full page text, and extract, crawl, and research endpoints. As of September 2026, SerpApi bills successful searches on monthly plans, while Tavily bills credits and charges double for advanced searches.
Every product fact below comes from SerpApi's and Tavily's own documentation and pricing pages, checked on September 28, 2026. For category background, see what a SERP API is; for more candidates, see the guide to testing web search APIs for AI agents.
What is the real difference between SerpApi and Tavily?
SerpApi retrieves and parses search engine results pages. Its Google Search API takes a query at https://serpapi.com/search?engine=google and returns JSON: organic_results with position, title, link, and snippet, plus modules such as local results, ads, the knowledge graph, and news. Its API list adds Google verticals such as Maps, Shopping, Scholar, Patents, AI Overview, and AI Mode, plus Bing, Baidu, DuckDuckGo, Yandex, Amazon, and YouTube. A result tells your agent what the engine showed and where, not what the linked page says.
Tavily is search designed for agent consumption. A POST to its Search endpoint returns results ranked by relevance, each with a score and a content field. At the basic, fast, and advanced depths, content joins up to three chunks of at most 500 characters from the source; ultra-fast returns one NLP summary per URL instead. Setting include_raw_content adds cleaned page content as Markdown or text, and include_answer adds an LLM-generated answer. Separate endpoints handle extraction from known URLs, site mapping, crawling, and multi-step research.
The edges overlap. SerpApi's output=md serves any of its 100-plus APIs as Markdown for LLMs, per its Markdown Output page, but that is still the results page: titles, links, and snippets. Its preview Search Index API, described as SerpApi's own LLM-first index, also documents snippets, not page text. Tavily returns no Google positions, ads, or local packs. So the split holds: SerpApi reports the engine's view, and Tavily reports excerpts from the pages.
How do the request and response contracts compare?
These are the fields an agent tool actually sends and reads, as each vendor documents them today.
| Contract detail | SerpApi Google Search API | Tavily Search |
|---|---|---|
| Call and auth | GET https://serpapi.com/search with an api_key query parameter | POST https://api.tavily.com/search with a JSON body and a Bearer token |
| Results per billed unit | One results page; paginate with start in steps of 10 (no num parameter is documented) | Up to 20 results through max_results |
| Evidence per result | Google's snippet, position, and displayed link, with optional date and sitelinks | content excerpt and score, with optional raw_content and published_date |
| Location and language | location (city level recommended), uule, lat and lon, gl, hl, device | country boost (general topic only), language boost or strict filter |
| Freshness and caching | tbs filters; identical requests within one hour are served from cache unless no_cache is true | time_range, start_date, end_date; undated results stay unless filter_by_published_date is set |
| Domain control | Query operators such as site:, plus cr and lr | include_domains (up to 300) in prefer or restrict mode, exclude_domains (up to 150) |
| Payload control | output=md and json_restrictor | chunks_per_source (1 to 3); raw content off by default |
| Failure signal | search_metadata.status plus an error string; HTTP 429 for throughput or exhausted searches | HTTP 429 with Retry-After; 432 for a key or plan limit; 433 for the pay-as-you-go limit |
The last row matters most for reliability. Per SerpApi's status and error codes, an empty Google page is a success: HTTP 200, status Success, plus an error string reading "Google hasn't returned any results for this query." Treat every error key as a failure and a legitimate no-results answer becomes an outage; real failures arrive as HTTP 503 with status Error. SerpApi's 429 covers both the hourly throughput cap and an exhausted balance, so only the message tells a retry loop whether waiting helps. Tavily splits those cases across 429, 432, and 433.
Evidence fields are not interchangeable either. A SerpApi snippet is the text Google chose to display. A Tavily content string is Tavily's selection of chunks, raw_content is the cleaned page, and answer is model output that should never be stored as a source. Tavily's published_date is a beta best estimate that can postdate the original publication. SerpApi's one-hour cache saves money on repeats, but a repeated breaking-news query can return a result up to an hour old unless you set no_cache.
What do SerpApi and Tavily cost for an agent workload?
The billing units differ. SerpApi sells monthly plans and, per its pricing page, counts only successful searches: cached, errored, and failed searches are free, and a response with 100 results or none counts as one search. Each plan also guarantees a number of successful searches per hour. Prices are as of September 2026.
| SerpApi plan | Price per month | Searches per month | Guaranteed searches per hour |
|---|---|---|---|
| Free | $0 | 250 | 50 |
| Starter | $25 | 1,000 | 200 |
| Developer | $75 | 5,000 | 1,000 |
| Production | $150 | 15,000 | 3,000 |
| Big Data | $275 | 30,000 | 6,000 |
| Searcher | $725 | 100,000 | 20,000 |
| Volume | $1,475 | 250,000 | 50,000 |
| Infrastructure | $2,750 | 500,000 | 100,000 |
| Cloud (entry tier) | $3,750 | 1,000,000 | 110,000 |
Plans above Big Data sit behind "View all plans," and Cloud tiers continue past 1,000,000 searches. Ludicrous Speed, SerpApi's lower-latency option, costs twice the standard rate, so an agent that enables it pays double these figures.
Tavily meters credits, per its credits documentation (as of September 2026). Basic, fast, and ultra-fast searches cost 1 credit and advanced costs 2, and auto_parameters can switch a request to advanced unless you pin search_depth. Extract costs 1 credit per 5 successful URLs at basic depth and 2 at advanced. Credits reset on the first of each month. The free Researcher plan includes 1,000 credits; Project is $30 for 4,000, Bootstrap $100 for 15,000, Startup $220 for 38,000, and Growth $500 for 100,000. Pay-as-you-go is $0.008 per credit, including usage past a plan's limit, and Enterprise is custom.
A worked example at list prices, using the cheapest documented combination for each vendor:
| Agent searches per month | SerpApi (Google Search API) | Tavily, basic depth | Tavily, advanced depth |
|---|---|---|---|
| 20,000 | $275 (Big Data) | $140 (Bootstrap plus 5,000 pay-as-you-go credits) | $236 (Startup plus 2,000 pay-as-you-go credits) |
| 100,000 | $725 (Searcher) | $500 (Growth) | $1,300 (Growth plus 100,000 pay-as-you-go credits), or an Enterprise quote |
The dollars buy different things. SerpApi's buy one Google results page per search, with snippets. Tavily's buy up to 20 results with excerpts per call, and its credit table prices search by depth alone, with no separate line for raw content or the generated answer. If your agent reads the pages behind SerpApi links, fetching and extraction are extra. Neither figure includes the tokens your agent spends reading results.
Throughput differs too. SerpApi's guarantee is hourly: a monthly batch of 10,000 searches fits Production's volume, but finishing it inside one hour takes the 20,000-per-hour guarantee that starts at Searcher. Tavily's rate limits are per minute: 100 requests on development keys and 1,000 on production keys, which require a paid plan or pay-as-you-go, with Crawl capped at 100 and Research at 20. On uptime, SerpApi's pricing page cites SLAs of up to 99.97%, and Tavily's FAQ lists uptime and support SLAs on its Enterprise plan.
Which one fits your agent?
Decide by what the agent consumes after the call returns.
| Agent job | Better fit | Documented reason |
|---|---|---|
| Track rankings, ads, local packs, or what Google's AI Overview says | SerpApi | Positions, local and ad modules, and dedicated AI Overview and AI Mode APIs |
| Query a specific engine or vertical such as Scholar, Patents, Amazon, YouTube, or Baidu | SerpApi | Separate APIs per engine and vertical |
| Simulate a searcher in a specific city | SerpApi | location at city level; Tavily documents a country-level boost only |
| Ground answers in page text for RAG | Tavily | Ranked excerpts with scores, and raw_content in the same call |
| Read a known site or documentation tree | Tavily | Map and Crawl endpoints, billed per pages mapped plus extractions |
| Hand off a multi-step research question | Tavily | Research endpoint, 4 to 250 credits per request depending on the model |
If you need both jobs, register them as two tools with distinct descriptions, so the model chooses "what does Google show" separately from "what do the sources say." Both vendors host MCP servers: SerpApi's at mcp.serpapi.com, keyed in the URL path or a Bearer header, and Tavily's at mcp.tavily.com, with a key, OAuth, or keyless access. The Tavily MCP comparison covers that layer, and Tavily vs Exa helps if a neural search API is also on your list.
How do you put both behind one agent tool?
The adapter below builds each vendor's documented request, classifies failures by whether a retry can help, and normalizes both responses into one record with each field's meaning labeled: snippets versus excerpts, page position versus relevance score, and Tavily's generated answer kept apart from sources. It encodes the contract traps. An empty Google page returns status empty, not an error; a SerpApi 429 from an exhausted balance is not retried; and Tavily's 432 and 433 stop the loop.
Save it as search_tool.py and run it with python3. It uses only the standard library and checks itself against synthetic fixtures, so it needs no key and makes no paid calls. Live use requires allow_network=True and a real key.
"""One agent search tool, two backends: SerpApi (Google) and Tavily.
Standard library only. Running this file uses synthetic fixtures shaped like
each vendor's documented responses and makes no network calls.
"""
import io
import json
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import Request, urlopen
MAX_BYTES = 2_000_000 # application budget, not a vendor limit
class SearchError(RuntimeError):
def __init__(self, message, retry=False, retry_after=None):
super().__init__(message)
self.retry = retry
self.retry_after = retry_after
def serpapi_request(query, api_key, location=None):
params = {"engine": "google", "q": query, "api_key": api_key}
if location:
params["location"] = location # e.g. "Austin, Texas, United States"
# The key travels in the query string, so keep request URLs out of logs.
return Request("https://serpapi.com/search?" + urlencode(params), method="GET")
def tavily_request(query, api_key, depth="basic", max_results=5, raw=False):
body = {"query": query, "search_depth": depth, "max_results": max_results,
"include_raw_content": "markdown" if raw else False,
"include_usage": True}
return Request("https://api.tavily.com/search", method="POST",
data=json.dumps(body).encode("utf-8"),
headers={"Authorization": "Bearer " + api_key,
"Content-Type": "application/json"})
def call(request, opener=None, allow_network=False, timeout=20):
if opener is None:
if not allow_network:
raise SearchError("network disabled; inject a fixture opener")
opener = urlopen
try:
with opener(request, timeout=timeout) as response:
raw = response.read(MAX_BYTES + 1)
except HTTPError as exc:
detail = exc.read(2048).decode("utf-8", "replace")
# Tavily: 432 = plan limit, 433 = pay-as-you-go limit. SerpApi: 429 means
# hourly throughput exceeded OR searches used up, so read the message.
exhausted = exc.code in (432, 433) or "run out of searches" in detail
retry = exc.code in (429, 500, 503) and not exhausted
wait = exc.headers.get("Retry-After") if exc.headers else None
raise SearchError("HTTP %d: %s" % (exc.code, detail[:200]), retry, wait) from None
except (URLError, TimeoutError, OSError):
raise SearchError("transport failure", retry=True) from None
if len(raw) > MAX_BYTES:
raise SearchError("response exceeds application byte budget")
try:
return json.loads(raw.decode("utf-8"))
except (UnicodeError, ValueError):
raise SearchError("invalid JSON") from None
def normalize_serpapi(payload):
status = (payload.get("search_metadata") or {}).get("status")
if status != "Success":
raise SearchError("SerpApi status %s: %s" % (status, payload.get("error")), retry=True)
hits = [{"url": r["link"], "title": r.get("title"), "rank": r.get("position"),
"score": None, "evidence": r.get("snippet") or "",
"evidence_kind": "serp_snippet", "full_text": None}
for r in payload.get("organic_results") or [] if r.get("link")]
# An empty Google page is HTTP 200, status Success, plus an "error" string.
return {"provider": "serpapi", "status": "ok" if hits else "empty",
"note": None if hits else payload.get("error"), "results": hits}
def normalize_tavily(payload):
hits = [{"url": r["url"], "title": r.get("title"), "rank": i,
"score": r.get("score"), "evidence": r.get("content") or "",
"evidence_kind": "tavily_content", "full_text": r.get("raw_content")}
for i, r in enumerate(payload.get("results") or [], 1) if r.get("url")]
return {"provider": "tavily", "status": "ok" if hits else "empty",
"generated_answer": payload.get("answer"), # model output, not a source
"credits": (payload.get("usage") or {}).get("credits"), "results": hits}
# ---- Offline checks with synthetic fixtures (not live results) ----
class Fixture:
def __init__(self, payload):
self.raw = json.dumps(payload).encode("utf-8")
def __enter__(self):
return self
def __exit__(self, *exc):
return False
def read(self, size=-1):
return self.raw if size < 0 else self.raw[:size]
def replay(payload):
return lambda request, timeout: Fixture(payload)
def fail(code, body):
def opener(request, timeout):
raise HTTPError(request.full_url, code, "fixture", {}, io.BytesIO(body.encode()))
return opener
def raised(fn):
try:
fn()
except SearchError as err:
return err
raise AssertionError("expected SearchError")
SERP_OK = {"search_metadata": {"status": "Success"}, "organic_results": [
{"position": 1, "title": "Example", "link": "https://example.com/a",
"snippet": "A synthetic snippet."}]}
SERP_EMPTY = {"search_metadata": {"status": "Success"},
"search_information": {"organic_results_state": "Fully empty"},
"error": "Google hasn't returned any results for this query."}
TAVILY_OK = {"query": "q", "answer": None, "images": [], "response_time": 1.1,
"usage": {"credits": 1}, "results": [
{"title": "Example", "url": "https://example.com/a", "score": 0.81,
"content": "First chunk [...] second chunk", "raw_content": None}]}
if __name__ == "__main__":
serp = normalize_serpapi(call(serpapi_request("q", "fixture-key"), replay(SERP_OK)))
assert serp["status"] == "ok" and serp["results"][0]["rank"] == 1
assert normalize_serpapi(SERP_EMPTY)["status"] == "empty" # not an outage
tav = normalize_tavily(call(tavily_request("q", "fixture-key"), replay(TAVILY_OK)))
assert tav["results"][0]["score"] == 0.81 and tav["credits"] == 1
cases = [(tavily_request, 432, '{"detail": {"error": "plan limit"}}', False),
(serpapi_request, 429, '{"error": "Your account has run out of searches."}', False),
(serpapi_request, 429, '{"error": "synthetic throughput message"}', True)]
for build, code, body, retry in cases:
err = raised(lambda: call(build("q", "fixture-key"), fail(code, body)))
assert err.retry is retry, (code, body)
assert "network disabled" in str(raised(lambda: call(serpapi_request("q", "k"))))
print(json.dumps({"serpapi": serp, "tavily": tav}, indent=2))
The fixtures follow the documented shapes but are not live responses, so the checks prove parsing and error handling, not result quality. Before production, add a wall-clock budget per agent step, honor retry_after with capped backoff, and log requests without the SerpApi key, which travels in the URL.
What risks should you weigh beyond features?
SerpApi's Google results come from an engine it does not operate. Google sued SerpApi on December 19, 2025, under the anti-circumvention provisions of the DMCA (N.D. Cal., No. 4:25-cv-10826). On July 20, 2026, the court granted SerpApi's motion to dismiss, partly without leave to amend. Google filed an amended complaint on August 10, 2026, and SerpApi's motion to dismiss it, filed August 24, was pending as of late September, according to the public docket. If your agent relies on Google results, treat the case as a dependency to monitor.
SerpApi's U.S. Legal Shield provides up to $2 million in coverage for the scraping and parsing of search engine data, as long as your use of the data or service is not illegal. It comes only with Production and higher plans, covers only claims under U.S. law in U.S. courts, and does not cover what you do with the data.
For Tavily, the company fact to know is ownership. In February 2026, Nebius announced an agreement to acquire Tavily. Tavily's own announcement said the API and customer data policies stay the same and that zero data retention remains core.
Retention terms differ by plan. SerpApi's zero_trace parameter, which skips storing search parameters, files, and metadata, is documented as Enterprise only; its pricing page lists SOC 2 Type II, SOC 3, and ISO 27001. Tavily's FAQ lists SOC 2 certification and zero data retention. If agent queries carry customer data, confirm the terms for the plan you will buy.
What should you test before you choose?
Run the same workload through both with settings frozen. The web search API evaluation guide covers methodology; these checks target the differences above.
- Replay real agent queries. Use at least 50 from your logs. Freeze SerpApi's engine, location, and device, and Tavily's depth, topic,
max_results, andchunks_per_source. Score whether the facts the agent needs appear in the returned evidence, not whether URLs overlap. - Count tokens per tool result. Compare SerpApi JSON,
json_restrictor, andoutput=md, and Tavily with and withoutraw_content. Tokens the model reads are a cost on top of the API bill. - Force the failure paths. Send a query with no results, exhaust a free-tier test key, and exceed a rate limit. Confirm the agent sees "no results" and "tool failed" as different outcomes, and that nothing retries a 432, a 433, or an exhausted SerpApi balance.
- Check freshness behavior. Repeat a news query within an hour on SerpApi with and without
no_cache. On Tavily, count nullpublished_datevalues under atime_rangefilter before relying on date windows. - Price the observed mix. Tally searches by depth, extractions, and any pages you fetched yourself, then divide by tasks completed, not calls made.
Both offer a free way in. SerpApi's free plan includes 250 searches a month at up to 50 per hour. Tavily's Researcher plan includes 1,000 credits a month with no credit card, and its keyless mode accepts Search and Extract requests carrying an X-Tavily-Access-Mode: keyless header, free but rate-limited.
Where does the You.com Web Search API fit?
If your agent needs page evidence, as with Tavily, but you would rather pay per call with query-relevant excerpts included, the You.com Web Search API is a third option to test. It takes a POST to https://ydc-index.io/v1/search with an X-API-Key header and returns web and news results in one call, with snippets by default. Its extraction parameter adds highlights or full-page Markdown or HTML, per the search reference.
As of September 2026, the billing page lists $5.00 per 1,000 calls with up to 100 results per call, snippets or highlights included. Full-page extraction adds $1.00 per 1,000 pages crawled live, and pages served from cache under the default source are free. At the 20,000-search volume above, that is $100 before extraction. Self-serve keys default to 10 requests per second, per the rate limits page, and new accounts get $100 in credits. The hosted MCP server has a keyless ?profile=free option that exposes you-search and you-discover at 100 queries a day.
It does not return Google rank positions, ads, or local packs, so keep SerpApi for those jobs. For URLs you already have, see the Contents API; for excerpt sizing, see the highlights parameter. If you are replacing Tavily outright, the Tavily alternatives guide covers migration.
LI Test
LI Test
Share Article:
Related resources.

SerpApi Alternative in 2026: When to Switch, What to Switch To, and What to Test
September 18, 2026
Blog

What Is the Gemini Web Search API? Grounding With Google Search, Explained
September 17, 2026
Blog

What Is the Claude Web Search API? A Practical Guide for Developers
September 17, 2026
Blog


