How do I migrate from Tavily to another search API?
Point Tavily code at Grounder's Tavily-shaped endpoint by changing the URL and sending a scoped OAuth access token; 8 request fields work, Extract, Crawl and answers do not.
Last checked
To move Tavily code to Grounder (grounder.net), change https://api.tavily.com/search to https://api.grounder.net/compat/tavily/search and send a search-scoped OAuth access token. Existing legacy search keys still work.
Last checked: 20 October 2026. Disclosure: Grounder (grounder.net) publishes this page, and Tavily is one of the 10 search providers Grounder routes searches to.
What changes
| Tavily | Grounder's Tavily-shaped endpoint | |
|---|---|---|
| URL | POST https://api.tavily.com/search | POST https://api.grounder.net/compat/tavily/search |
| Authorization | Authorization: Bearer with a Tavily key | Authorization: Bearer with a Grounder OAuth access token scoped to search; an existing legacy key also works, including in Tavily's api_key body field |
| Rate limit | 100 requests a minute (development keys), 1,000 (production keys) | 120 requests a minute per key |
max_results | 0 to 20, default 10 | 1 to 100, default 5 |
| Cost | $0.008 per credit pay as you go (basic search 1 credit, advanced 2); 1,000 free credits a month | First 1,000 searches free per account in total, up to 100 a day |
| Other endpoints | Extract, Crawl, Map, Research | Not available |
Request fields:
| Tavily field | On Grounder | Note |
|---|---|---|
query | Honored | Required |
max_results | Honored | Set it explicitly: the default is 5, and 0 is not accepted |
days | Honored | Rolling window in days, any topic; Tavily's current API reference lists time_range instead, so convert (for example year becomes days: 365) |
start_date, end_date | Honored | Used when days is absent |
include_domains, exclude_domains | Honored | Only one applies; include wins |
include_raw_content | Honored | raw_content is the result's passage text, not the full page |
include_answer | Accepted, ignored | answer is always an empty string |
include_images | Accepted, ignored | images is always an empty list |
search_depth, topic, chunks_per_source, include_usage | Accepted, ignored | |
time_range, country, language, exact_match, safe_search and any other field | Not supported | Ignored |
The response keeps Tavily's keys: query, follow_up_questions (null), answer, images, results, response_time (seconds) and request_id. Each result has id (canonical URL), title, url, content, score, published_date (null when unknown) and raw_content (null unless requested), plus a grounder object with provider, found_by, licensed, rank and, for licensed results, publisher_name. No usage field is listed. As in the native API, score is the finding provider's own and is not comparable across providers; use grounder.rank for order.
Results are merged and deduplicated across providers. Licensed sources only is on by default for signed-up accounts, so a call can return fewer results than max_results.
Steps
1. Authorize HTTP
Read https://api.grounder.net/.well-known/oauth-protected-resource/v1/search, register a public OAuth client, then sign in and request scope=search for resource https://api.grounder.net/v1/search. Store the returned access token as GROUNDER_ACCESS_TOKEN and refresh it before it expires. The API docs describe the flow. Existing legacy search keys still work.
2. Change the URL and access token
- https://api.tavily.com/search
+ https://api.grounder.net/compat/tavily/search
Before:
curl https://api.tavily.com/search \
-H "Authorization: Bearer $TAVILY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "what is the half-life of caffeine", "max_results": 5, "include_domains": ["nih.gov"], "time_range": "year"}'
After:
curl https://api.grounder.net/compat/tavily/search \
-H "Authorization: Bearer $GROUNDER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "what is the half-life of caffeine", "max_results": 5, "include_domains": ["nih.gov"], "days": 365}'
3. Update your code
Python:
import os
import requests
resp = requests.post(
"https://api.grounder.net/compat/tavily/search",
headers={"Authorization": f"Bearer {os.environ['GROUNDER_ACCESS_TOKEN']}"},
json={
"query": "what is the half-life of caffeine",
"max_results": 5,
"include_domains": ["nih.gov"],
"days": 365,
},
timeout=30,
)
resp.raise_for_status()
for r in resp.json()["results"]:
print(r["url"], r["grounder"]["provider"], r["grounder"]["licensed"])
TypeScript (Node 18 or later, as an ES module):
const res = await fetch("https://api.grounder.net/compat/tavily/search", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GROUNDER_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "what is the half-life of caffeine",
max_results: 5,
include_domains: ["nih.gov"],
days: 365,
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const data = await res.json();
for (const r of data.results) console.log(r.url, r.grounder.provider, r.grounder.licensed);
With the official tavily-python SDK (version 0.8.5, released 6 October 2026), pass the current OAuth access token as api_key and api_base_url="https://api.grounder.net/compat/tavily" to TavilyClient; the client appends /search and sends it as a Bearer header. Refresh the token outside that client before it expires. Grounder's notes say the endpoint's response was parsed by the unmodified SDK without error (undated), so test it on your own code in step 5.
4. Keep Tavily's results
By default Grounder sends each call to the provider, or pair of providers, most likely to answer it well. To keep Tavily, set Tavily to Always and Licensing to Any source on the account page, under Routing. To have Tavily bill you directly, add your own Tavily key in the same table. To pin one search, call the native endpoint, which returns Grounder's own response shape:
curl https://api.grounder.net/v1/search \
-H "Authorization: Bearer $GROUNDER_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "what is the half-life of caffeine", "top_k": 5, "requested_provider": "tavily", "routing_settings": {"licensed_only": false}}'
5. Verify
Send 10 to 20 real queries to both APIs and compare result counts and URLs. Check grounder.provider and grounder.licensed on each result; fewer results than expected usually means Licensed sources only is on. Each call is a recorded search and counts toward the 1,000 free ones. curl https://api.grounder.net/v1/providers needs no key and lists the 10 providers.
6. Use MCP instead
Add https://api.grounder.net/mcp to your MCP client and sign in with Google, GitHub or Apple. The search tool takes query, top_k, domains, days, include_text and requested_provider; steps are in Docs.
What Tavily credits cost
Tavily's search credits, from its pricing and API reference pages: basic, fast and ultra-fast depths cost 1 credit and advanced costs 2. Pay as you go is $0.008 per credit, so a basic search is $8 per 1,000 and an advanced one $16 per 1,000; the Project plan ($30 a month for 4,000 credits) works out to $0.0075 per credit. Credits reset on the first day of each calendar month.
| Option | Price per 1,000 searches | Free tier |
|---|---|---|
| Tavily | $8 (basic); $16 (advanced) | 1,000 credits a month |
| Exa | $7 (auto); $4 (lite) | $10 credit a month |
| Brave Search API | $5 | $5 credit a month |
| Perplexity Search API | $5; $1 (fast search) | Not published |
| Grounder | First 1,000 searches free | Up to 100 a day, 120 a minute |
If you move to another API directly
| Tavily | Brave | Exa | Perplexity |
|---|---|---|---|
query | q | query | query |
max_results (0 to 20, default 10) | count (maximum 20, default 20) | numResults (1 to 100, default 10) | max_results (1 to 20, default 10) |
search_depth (basic, advanced, fast, ultra-fast) | No equivalent; LLM Context is a separate endpoint | type (auto, lite, deep-lite, deep) | search_type set to fast |
include_domains (300), exclude_domains (150) | site: and -site: inside q | includeDomains, excludeDomains (1,200 each) | search_domain_filter (20 domains, allow or deny) |
time_range: day, week, month, year | freshness: pd, pw, pm, py | startPublishedDate, endPublishedDate | search_recency_filter: hour, day, week, month, year |
start_date, end_date | freshness as YYYY-MM-DDtoYYYY-MM-DD | startPublishedDate, endPublishedDate | search_after_date_filter, search_before_date_filter (M/D/YYYY) |
country (a country name, topic general only) | country (2-character code) | userLocation | country (ISO 3166-1 alpha-2) |
language, filter_by_language | search_lang | No request field found | search_language_filter (10 codes) |
include_raw_content | LLM Context endpoint (extracted chunks) | contents.text | max_tokens_per_page caps the page text in snippet |
include_answer | Answers plan ($4 per 1,000 plus tokens) | Answer endpoint ($5 per 1,000) | Agent API (Sonar chat completions ended 27 September 2026) |
| Extract | None | Contents API ($1 per 1,000 pages) | fetch_url tool in the Agent API |
results[].content | web.results[].description | results[].highlights or text | results[].snippet |
Side by side, in more depth: Tavily vs Exa, Tavily vs Brave and Tavily alternatives.
Where Grounder fits
Grounder is a web search API and MCP server for AI agents: one search goes across 10 web search providers, results are deduplicated, and each is marked with its provider and whether it is licensed. It returns sources, not an answer. Grounder's benchmark, run by Grounder, searched 887 questions (table as of 20 October 2026 05:22 UTC):
| Row | SimpleQA correct given attempted (671 questions) | SealQA correct given attempted (216 questions) |
|---|---|---|
| Grounder optimized | 14.3% | 12.1% |
| Grounder optimized, all sources | 97.9% | 36.6% |
| Perplexity | 96.1% | 36.8% |
| Exa | 88.6% | 32.1% |
| Tavily | 86.6% | 24.5% |
| Brave | 85.2% | 16.4% |
Each provider's row is its default search. Figures are correct given attempted: the share of the questions a search answered that it got right, with a "don't know" counted as not attempted. Grounder optimized answers only from licensed sources, so it gets fewer answers right than the all-sources row, which searches every source. Grounder optimized is what a signed-up account gets by default; the all-sources row is the same routing with nothing filtered out. Both Grounder rows are routed as Grounder routes on 19 October 2026, and that routing was tuned on these same searches. Read against Tavily: the all-sources row is ahead of Tavily on both benchmarks, but the default row (14.3% and 12.1%) is far behind it. Each question was searched once on every provider's default search (top 10 results, 60 seconds), answered by Z.ai's GLM-5.3-Flash and graded with OpenAI's SimpleQA grader, from 8 to 13 October 2026. Full method: /benchmarks. For a wider comparison, see the best search API for AI agents.
FAQ
Is there a drop-in Tavily-compatible endpoint?
Yes: POST https://api.grounder.net/compat/tavily/search takes Tavily's request shape and returns Tavily's response shape plus a grounder object on each result. It accepts an OAuth access token with the search scope; existing legacy search keys still work.
Does it support search_depth, include_answer, Extract or Crawl?
No. search_depth and include_answer are accepted and ignored, and answer is always empty. Extract, Crawl, Map and Research are not available.
Do I need a new key?
No. Register a public OAuth client, sign in, request the search scope for https://api.grounder.net/v1/search and send the returned access token. Existing legacy search keys still work.
How does billing change from Tavily's credits?
Tavily charges credits: $0.008 each on pay as you go, 1 for a basic search and 2 for an advanced one, so $8 and $16 per 1,000 searches. Grounder's first 1,000 searches are free, up to 100 a day, and a search counts once however many providers it reaches.
Which fields map if I move from Tavily to Exa, Brave or Perplexity instead?
The core fields map directly: query becomes q (Brave) or stays query (Exa, Perplexity); max_results becomes count (Brave, maximum 20), numResults (Exa, up to 100) or max_results (Perplexity, 1 to 20); include_domains and exclude_domains become site: operators (Brave), includeDomains and excludeDomains (Exa) or search_domain_filter (Perplexity, 20 domains). The table in this guide lists the rest.
Next step
Your first 1,000 searches are free, up to 100 a day. No card needed. Connect an MCP client to https://api.grounder.net/mcp, or read the HTTP API docs to use OAuth.
Sources
- https://grounder.net/docs (20 October 2026)
- https://docs.tavily.com/documentation/api-reference/endpoint/search (20 October 2026)
- https://docs.tavily.com/documentation/api-credits (20 October 2026)
- https://docs.tavily.com/documentation/rate-limits (20 October 2026)
- https://www.tavily.com/pricing (20 October 2026)
- https://pypi.org/project/tavily-python/ and https://github.com/tavily-ai/tavily-python/blob/master/tavily/tavily.py (20 October 2026)
- https://api-dashboard.search.brave.com/app/documentation/web-search/query and https://brave.com/search/api/ (20 October 2026)
- https://exa.ai/pricing and https://exa.ai/docs/reference/search (20 October 2026)
- https://docs.perplexity.ai/docs/search/quickstart , https://docs.perplexity.ai/docs/search/filters/date-time-filters and https://docs.perplexity.ai/docs/getting-started/pricing (20 October 2026)
- https://grounder.net/benchmarks and https://api.grounder.net/v1/demo/results (20 October 2026, as of 05:22 UTC)
- https://grounder.net/pricing (20 October 2026)
- https://arxiv.org/abs/2411.04368 and https://arxiv.org/abs/2506.01062 (20 October 2026)