
Tavily
- 34 installs
- 22 repo stars
- Updated August 1, 2026
- itechmeat/llm-code
Add real-time web search to LLM apps with Tavily: search, content extraction, site crawling, mapping and autonomous research.
About
A guide to the Tavily AI search API covering web search, content extraction, site mapping, crawling and autonomous research for LLM applications. Use it when building RAG pipelines with web data, extracting URL content, or giving agents current information.
- Five core APIs (Search, Extract, Map, Crawl, Research) with per-request credit costs
- Python and JavaScript SDKs plus REST, free tier of 1,000 credits/month
Tavily by the numbers
- 34 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #8,855 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/itechmeat/llm-code --skill tavilyAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 34 |
|---|---|
| repo stars | ★ 22 |
| Last updated | August 1, 2026 |
| Repository | itechmeat/llm-code ↗ |
What it does
Add real-time web search to LLM apps with Tavily: search, content extraction, site crawling, mapping and autonomous research.
Files
Tavily
AI-optimized search engine for building LLM applications with real-time web data.
Links
Quick Navigation
| Topic | Reference |
|---|---|
| REST API | api.md |
| Python SDK | python.md |
| JavaScript SDK | javascript.md |
| Best Practices | best-practices.md |
| Integrations | integrations.md |
When to Use
- Building RAG applications with real-time web data
- AI agents that need current information
- Content extraction from web pages
- Site crawling with AI-guided instructions
- Autonomous research tasks
Installation
Install: pip install tavily-python (Python) or npm i @tavily/core (JavaScript).
Quick Start
Python
from tavily import TavilyClient
client = TavilyClient(api_key="tvly-YOUR_API_KEY")
response = client.search("What is the latest news about AI?")
print(response)JavaScript
import { tavily } from "@tavily/core";
const client = tavily({ apiKey: "tvly-YOUR_API_KEY" });
const response = await client.search("What is the latest news about AI?");
console.log(response);cURL
curl -X POST https://api.tavily.com/search \
-H "Content-Type: application/json" \
-H "Authorization: Bearer tvly-YOUR_API_KEY" \
-d '{"query": "What is the latest news about AI?"}'Core APIs
| API | Purpose | Credits |
|---|---|---|
| Search | Web search optimized for LLMs | 1-2 per request |
| Extract | Extract content from URLs | 1-2 per 5 URLs |
| Map | Map website structure | 1-2 per 10 pages |
| Crawl | Crawl + extract from sites | Map + Extract |
| Research | Autonomous deep research (beta) | 4-250 per task |
Pricing & Credits
Free tier: 1,000 credits/month (no credit card required)
| Plan | Credits/month | Price/credit |
|---|---|---|
| Researcher | 1,000 | Free |
| Project | 4,000 | $0.0075 |
| Bootstrap | 15,000 | $0.0067 |
| Startup | 38,000 | $0.0058 |
| Growth | 100,000 | $0.005 |
| Pay-as-go | Per usage | $0.008 |
Credit Costs
| API | Basic | Advanced |
|---|---|---|
| Search | 1 | 2 |
| Extract | 1/5 URLs | 2/5 URLs |
| Map | 1/10 pages | 2/10 pages |
| Crawl | Map + Extract costs | |
| Research (mini) | 4-110 | - |
| Research (pro) | 15-250 | - |
Rate Limits
| Environment | RPM (requests/min) |
|---|---|
| Development | 100 |
| Production | 1,000 |
Note: Crawl endpoint limited to 100 RPM for both environments.
Production keys require paid plan or PAYGO enabled.
Search API
Primary endpoint for LLM-optimized web search.
response = client.search(
query="Latest AI developments",
search_depth="advanced", # "basic" (1 credit) or "advanced" (2 credits)
max_results=10, # 1-20 results
include_answer=True, # Include AI-generated answer
include_raw_content=False, # Include raw HTML
include_domains=["arxiv.org"], # Filter to specific domains
exclude_domains=["pinterest.com"] # Exclude domains
)Response Structure
{
"query": "...",
"answer": "AI-generated summary...", # if include_answer=True
"results": [
{
"title": "Page Title",
"url": "https://...",
"content": "Extracted relevant content...",
"score": 0.95,
"raw_content": "..." # if include_raw_content=True
}
]
}Extract API
Extract content from specific URLs.
response = client.extract(
urls=["https://example.com/article1", "https://example.com/article2"],
extract_depth="basic" # "basic" or "advanced"
)Crawl API
Crawl websites with AI-guided instructions.
response = client.crawl(
url="https://docs.example.com",
instructions="Find all pages about Python SDK", # Optional AI guidance
max_depth=2,
limit=50
)Map API
Get website structure without extracting content.
response = client.map(
url="https://docs.example.com",
instructions="Find documentation pages" # Optional
)Research API (Beta)
Autonomous deep research on complex topics.
response = client.research(
input="What are the implications of quantum computing on cryptography?",
model="pro" # "pro" (15-250 credits) or "mini" (4-110 credits)
)Why Tavily?
| Feature | Traditional Search | Tavily |
|---|---|---|
| Output | URLs + snippets | Full content |
| Scraping | Manual | Built-in |
| LLM optimization | None | Purpose-built |
| Filtering | Manual | AI-powered |
| Context limits | Not handled | Optimized |
Best Practices
1. Use `search_depth="basic"` for simple queries (saves credits) 2. Use `include_answer=True` for quick summaries 3. Filter domains to improve relevance 4. Use Extract when you know specific URLs 5. Use Research for complex, multi-step queries 6. Use Python keyless mode only for trials; it supports search() and extract() only and remains rate-limited
Prohibitions
- Do not expose API keys in client-side code
- Do not exceed rate limits (implement backoff)
- Do not scrape sites that block Tavily crawler
Tavily REST API Reference
Base URL: https://api.tavily.com
Authentication
All requests require Bearer token in header:
Authorization: Bearer tvly-YOUR_API_KEYPOST /search
Web search optimized for LLMs.
Request Body
| Parameter | Type | Default | Description |
|---|---|---|---|
| query | string | required | Search query |
| search_depth | string | "basic" | "basic" (1 credit), "advanced" (2 credits), "fast" (1 credit), "ultra-fast" (1 credit) |
| topic | string | "general" | "general", "news", "finance" |
| max_results | int | 5 | 1-20 results |
| include_answer | bool/string | false | true/"basic" (quick), "advanced" (detailed) |
| include_raw_content | bool/string | false | true/"markdown", "text" (plain text) |
| include_images | bool | false | Include related images |
| include_image_descriptions | bool | false | Add descriptions to images |
| include_domains | string[] | [] | Filter to specific domains (max 300) |
| exclude_domains | string[] | [] | Exclude domains (max 150) |
| time_range | string | - | "day", "week", "month", "year" (or "d", "w", "m", "y") |
| start_date | string | - | YYYY-MM-DD format |
| end_date | string | - | YYYY-MM-DD format |
| country | string | - | Boost results from specific country |
| chunks_per_source | int | 3 | 1-3, only with search_depth="advanced" |
| auto_parameters | bool | false | Auto-configure based on query intent |
| include_favicon | bool | false | Include favicon URLs |
| include_usage | bool | false | Include credit usage info |
Response
{
"query": "Who is Leo Messi?",
"answer": "AI-generated answer...",
"results": [
{
"title": "Page Title",
"url": "https://...",
"content": "Relevant content...",
"score": 0.95,
"raw_content": "...",
"published_date": "2024-01-15",
"favicon": "https://.../favicon.ico"
}
],
"images": [{ "url": "...", "description": "..." }],
"response_time": 1.67,
"usage": { "credits": 1 },
"request_id": "123e4567-..."
}---
POST /extract
Extract content from specific URLs.
Request Body
| Parameter | Type | Default | Description |
|---|---|---|---|
| urls | string/string[] | required | URL(s) to extract (max 20) |
| extract_depth | string | "basic" | "basic" (1/5 URLs), "advanced" (2/5 URLs) |
| format | string | "markdown" | "markdown" or "text" |
| include_images | bool | false | Include extracted images |
| query | string | - | Rerank chunks by relevance to query |
| chunks_per_source | int | 3 | 1-5, only when query provided |
| timeout | float | auto | 1-60 seconds |
| include_favicon | bool | false | Include favicon URLs |
| include_usage | bool | false | Include credit usage info |
Response
{
"results": [
{
"url": "https://...",
"raw_content": "Extracted markdown content...",
"images": ["https://..."],
"favicon": "https://.../favicon.ico"
}
],
"failed_results": [{ "url": "https://...", "error": "Timeout" }],
"response_time": 0.02,
"usage": { "credits": 1 },
"request_id": "123e4567-..."
}---
POST /crawl
Crawl websites with AI-guided instructions.
Request Body
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | required | Starting URL |
| instructions | string | - | Natural language guidance (2x credit cost) |
| max_depth | int | 1 | 1-5 levels deep |
| max_breadth | int | 20 | Links per page |
| limit | int | 50 | Total pages to process |
| select_paths | string[] | - | Regex patterns to include (e.g., "/docs/.*") |
| exclude_paths | string[] | - | Regex patterns to exclude (e.g., "/private/.*") |
| select_domains | string[] | - | Regex for allowed domains |
| exclude_domains | string[] | - | Regex for excluded domains |
| allow_external | bool | true | Follow external links |
| extract_depth | string | "basic" | "basic" or "advanced" |
| format | string | "markdown" | "markdown" or "text" |
| include_images | bool | false | Extract images |
| chunks_per_source | int | 3 | 1-5, only with instructions |
| timeout | float | 150 | 10-150 seconds |
| include_favicon | bool | false | Include favicon URLs |
| include_usage | bool | false | Include credit usage info |
Response
{
"base_url": "docs.tavily.com",
"results": [
{
"url": "https://docs.tavily.com/welcome",
"raw_content": "Extracted content...",
"images": [],
"favicon": "https://.../favicon.ico"
}
],
"response_time": 1.23,
"usage": { "credits": 1 },
"request_id": "123e4567-..."
}---
POST /map
Get website structure (URLs only, no content).
Request Body
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | required | Starting URL |
| instructions | string | - | Natural language guidance (2x credit cost) |
| max_depth | int | 1 | 1-5 levels deep |
| max_breadth | int | 20 | Links per page |
| limit | int | 50 | Total links to process |
| select_paths | string[] | - | Regex patterns to include |
| exclude_paths | string[] | - | Regex patterns to exclude |
| select_domains | string[] | - | Regex for allowed domains |
| exclude_domains | string[] | - | Regex for excluded domains |
| allow_external | bool | true | Include external links |
| timeout | float | 150 | 10-150 seconds |
| include_usage | bool | false | Include credit usage info |
Response
{
"base_url": "docs.tavily.com",
"results": ["https://docs.tavily.com/welcome", "https://docs.tavily.com/documentation/about"],
"response_time": 1.23,
"usage": { "credits": 1 },
"request_id": "123e4567-..."
}---
POST /research (Beta)
Autonomous deep research on complex topics.
Request Body
| Parameter | Type | Default | Description |
|---|---|---|---|
| input | string | required | Research question |
| model | string | "auto" | "mini" (4-110 credits), "pro" (15-250 credits), "auto" |
| stream | bool | false | Stream results via SSE |
| output_schema | object | - | JSON Schema for structured output |
| citation_format | string | "numbered" | "numbered", "mla", "apa", "chicago" |
Response
{
"request_id": "123e4567-...",
"created_at": "2025-01-15T10:30:00Z",
"status": "pending",
"input": "What are the latest developments in AI?",
"model": "mini",
"response_time": 1.23
}---
GET /research/{request_id}
Poll for research task status and results.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| request_id | string | Unique research task ID |
Response (200 - completed)
{
"request_id": "123e4567-e89b-12d3-a456-426614174111",
"created_at": "2025-01-15T10:30:00Z",
"status": "completed",
"content": "Research Report: Latest Developments in AI\n\n## Executive Summary\n\n...",
"sources": [
{
"title": "Latest AI Developments",
"url": "https://example.com/ai-news",
"favicon": "https://example.com/favicon.ico"
}
],
"response_time": 1.23
}Status Codes
| Code | Description |
|---|---|
| 200 | Research completed or failed |
| 202 | Research still in progress |
| 401 | Unauthorized |
| 404 | Research task not found |
| 500 | Server error |
---
Research Streaming
Enable real-time progress with stream: true in POST /research request.
Event Types
| Event Type | Description |
|---|---|
| tool_call | Agent performing action (WebSearch, Planning, etc.) |
| tool_response | Tool completed with sources |
| content | Streamed report chunks |
| sources | Final list of all sources |
| done | Stream complete |
Tool Types
| Tool | Description | Model |
|---|---|---|
| Planning | Initialize research plan | Both |
| WebSearch | Execute web searches | Both |
| ResearchSubtopic | Deep subtopic research | Pro only |
| Generating | Generate final report | Both |
Python Streaming
stream = client.research(
input="Research the latest developments in AI",
model="pro",
stream=True
)
for chunk in stream:
print(chunk.decode('utf-8'))JavaScript Streaming
const stream = await client.research("Latest AI developments", {
model: "pro",
stream: true,
});
for await (const chunk of stream) {
console.log(chunk.toString("utf-8"));
}SSE Event Structure
{
"id": "123e4567-...",
"object": "chat.completion.chunk",
"model": "mini",
"created": 1705329000,
"choices": [{
"delta": {
"role": "assistant",
"content": "# Research Report\n\n...",
"tool_calls": {...},
"sources": [...]
}
}]
}Research Flow
1. Planning tool_call → tool_response 2. WebSearch tool_call (with queries) → tool_response (with sources) 3. _(Pro)_ ResearchSubtopic cycles 4. Generating tool_call → tool_response 5. Content events (report chunks) 6. Sources event (all sources) 7. Done event
---
GET /usage
Get API key and account usage details.
Response
{
"key": {
"usage": 150,
"limit": 1000
},
"account": {
"current_plan": "Bootstrap",
"plan_usage": 500,
"plan_limit": 15000,
"paygo_usage": 25,
"paygo_limit": 100
}
}Response Fields
| Field | Description |
|---|---|
| key.usage | Credits used by this API key |
| key.limit | Credit limit for this key |
| account.current_plan | Active subscription plan |
| account.plan_usage | Total credits used this month |
| account.plan_limit | Monthly credit limit |
| account.paygo_usage | Pay-as-you-go credits used |
| account.paygo_limit | Pay-as-you-go credit limit |
---
Credit Costs Summary
| API | Basic | Advanced | Notes |
|---|---|---|---|
| Search | 1 | 2 | Per request |
| Extract | 1 / 5 URLs | 2 / 5 URLs | Only charged for successful |
| Map | 1 / 10 pages | 2 / 10 pages | With instructions = 2x |
| Crawl | Map + Extract | - | Combined cost |
| Research mini | 4-110 | - | Dynamic |
| Research pro | 15-250 | - | Dynamic |
Tavily Best Practices
Search Best Practices
Query Optimization
- Keep queries under 400 characters — concise, agent-style queries work best
- Break complex queries into sub-queries — separate focused requests for multi-topic research
# ✅ Good: focused queries
client.search("Competitors of company ABC")
client.search("Financial performance of company ABC")
client.search("Recent developments of company ABC")
# ❌ Bad: one massive multi-topic querySearch Depth Selection
| Depth | Latency | Relevance | Content Type | Best For |
|---|---|---|---|---|
| ultra-fast | Lowest | Lower | Content | Real-time apps, speed critical |
| fast | Low | Good | Chunks | Quick targeted snippets |
| basic | Medium | High | Content | General-purpose searches |
| advanced | Higher | Highest | Chunks | Specific, detailed info |
Content Types:
- Content — NLP-based page summary (general context)
- Chunks — Short snippets reranked by query relevance
Time Filtering
# Relative time range
client.search("latest ML trends", time_range="month")
# Specific date range
client.search("AI news", start_date="2025-01-01", end_date="2025-02-01")Domain Filtering
# Restrict to specific domains
client.search("CEO background at Google", include_domains=["linkedin.com/in"])
# Exclude irrelevant domains
client.search("US economy", exclude_domains=["espn.com", "vogue.com"])
# Boost country results
client.search("tech startup funding", country="united states")
# Wildcard patterns
client.search("AI news", include_domains=["*.com"], exclude_domains=["example.com"])Tip: Keep domain lists short and relevant.
auto_parameters
Tavily auto-configures based on query intent. Explicit values override automatic ones.
client.search(
query="impact of AI in education policy",
auto_parameters=True,
search_depth="basic" # Override to control cost (auto may set "advanced")
)Warning: auto_parameters may set search_depth="advanced" (2 credits). Set manually to control cost.
---
Extract Best Practices
Using Query for Targeted Extraction
# Extract only relevant portions
response = client.extract(
urls=["https://example.com/article"],
query="machine learning applications in healthcare",
chunks_per_source=3 # Max 500 chars each, 1-5 chunks
)When to use query:
- Extract only relevant portions of long documents
- Need focused content instead of full page
- Targeted information retrieval
Note: chunks_per_source only works when query is provided.
Extract Depth Selection
| Depth | Use Case |
|---|---|
| basic (default) | Simple text, faster processing |
| advanced | Complex pages, tables, JS-rendered, media |
# For complex pages
response = client.extract(
urls=["https://example.com/complex-page"],
extract_depth="advanced"
)Search vs Extract
| Approach | When to Use |
|---|---|
search(include_raw_content=True) | Quick prototyping, single API call |
extract(urls=[...]) | Specific URLs, curated extraction, query filtering |
Optimal Workflow Pipeline
async def content_pipeline(topic):
# 1. Search to discover URLs
response = await client.search(
query=topic,
search_depth="advanced",
max_results=20
)
# 2. Filter by relevance score (>0.5)
urls = [r['url'] for r in response['results'] if r.get('score', 0) > 0.5]
# 3. Deduplicate
urls = list(set(urls))[:20]
# 4. Extract with targeted query
extracted = await client.extract(
urls=urls,
query="specific focus question",
chunks_per_source=3,
extract_depth="advanced"
)
return extractedAdvanced Filtering Strategies
| Strategy | Description |
|---|---|
| Score-based | Filter search results by relevance score |
| Domain-based | Filter by trusted domains before extracting |
| Re-ranking | Use dedicated re-ranking models |
| LLM-based | Let LLM assess relevance before extraction |
---
Crawl Best Practices
Using Instructions
Guide crawl with natural language for semantic filtering:
response = client.crawl(
url="example.com",
max_depth=2,
instructions="Find all documentation pages about Python",
chunks_per_source=3 # Only with instructions
)Note: chunks_per_source only works when instructions are provided.
Depth vs Performance
| Parameter | Description | Impact |
|---|---|---|
| max_depth | Levels deep from start | Exponential latency |
| max_breadth | Links per page | Horizontal spread |
| limit | Total max pages | Hard cap |
Critical: Each depth level increases time exponentially. Start with max_depth=1.
# ✅ Conservative (recommended start)
client.crawl(url="example.com", max_depth=1, max_breadth=20, limit=20)
# ⚠️ Comprehensive (use carefully)
client.crawl(url="example.com", max_depth=3, max_breadth=100, limit=500)Path Filtering (Regex)
# Target specific sections
client.crawl(
url="example.com",
select_paths=["/blog/.*", "/docs/.*"],
exclude_paths=["/private/.*", "/admin/.*"]
)
# Stay within subdomain
client.crawl(
url="docs.example.com",
select_domains=["^docs.example.com$"]
)Map Before Crawl Workflow
1. Use Map to discover site structure 2. Analyze paths and patterns 3. Configure Crawl with discovered paths 4. Execute focused crawl
Common Pitfalls
| Pitfall | Impact | Solution |
|---|---|---|
| Excessive depth (>3) | Exponential time | Start with 1-2 |
| No instructions | Irrelevant content | Use semantic filtering |
| Missing limit | Runaway costs | Always set limit |
| Ignoring failed_results | Incomplete data | Monitor failures |
---
Research Best Practices
Prompting Tips
- Be specific — include known details (market, competitors, geography)
- Avoid contradictions — no conflicting constraints or goals
- Share what's known — include prior assumptions so research doesn't repeat
- Keep prompts clean — clear task + context + output format
Example prompts:
"Research the company ____ and its 2026 outlook. Provide overview
of products, services, and market position.""We're evaluating Notion as a partner. We know they serve SMB/mid-market,
expanded AI in 2025, compete with Confluence and ClickUp. Research their
2026 outlook, growth risks, and partnership opportunities. Include citations."Model Selection
| Model | Best For | Credits |
|---|---|---|
| mini | Narrow, well-scoped questions | 4-110 |
| pro | Complex, multi-domain, deep analysis | 15-250 |
| auto | Unsure of complexity | Variable |
# Mini for focused questions
client.research(
input="What are the top 5 competitors to X in SMB market?",
model="mini"
)
# Pro for comprehensive analysis
client.research(
input="Analyze competitive landscape for X including positioning, pricing, segments, product moves, and risks over 2-3 years.",
model="pro"
)Structured Output vs Report
| Mode | Best For |
|---|---|
| Structured Output (output_schema) | Data pipelines, enrichment, powering UIs |
| Report (default) | Reading, sharing, chat interfaces, briefs |
Schema tips:
- Write clear field descriptions (1-3 sentences)
- Use proper types (arrays, objects, enums) — not comma-separated strings
- Avoid duplicate/overlapping fields
Streaming vs Polling
| Mode | Best For |
|---|---|
Streaming (stream=True) | User interfaces, real-time updates |
| Polling (GET /research/{id}) | Background processes |
---
API Key Management
If Key Leaks
1. Revoke immediately in Tavily Dashboard 2. Generate new key 3. Update applications (env vars, secrets manager) 4. Contact support if unusual usage: support@tavily.com
Key Rotation
Rotate keys every 90 days as security best practice.
Zero-downtime rotation:
1. Generate new key (keep old active) 2. Deploy app with new key 3. Verify functionality 4. Revoke old key
Security Rules
# ✅ Use environment variables
import os
client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
# ❌ Never hardcode in source
client = TavilyClient(api_key="tvly-XXXXX") # WRONGNever:
- Hardcode keys in source code
- Commit keys to repositories
- Expose in client-side code
- Share in screenshots
Tavily MCP & Integrations
MCP Server
Model Context Protocol (MCP) enables AI assistants to integrate with Tavily for real-time web search and extraction.
Remote MCP Server (Easiest)
https://mcp.tavily.com/mcp/?tavilyApiKey=<your-api-key>Claude Desktop
Add integration in Claude Desktop (Settings > Integrations):
- Name: Tavily
- URL:
https://mcp.tavily.com/mcp/?tavilyApiKey=<your-api-key>
Cursor
{
"mcpServers": {
"tavily-remote-mcp": {
"command": "npx -y mcp-remote https://mcp.tavily.com/mcp/?tavilyApiKey=<your-api-key>",
"env": {}
}
}
}Local Installation
# Prerequisites: Node.js v20+
npx -y @tavily/mcpClaude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"tavily-mcp": {
"command": "npx",
"args": ["-y", "@tavily/mcp"],
"env": {
"TAVILY_API_KEY": "tvly-YOUR_API_KEY"
}
}
}
}OpenAI Integration
from openai import OpenAI
client = OpenAI()
resp = client.responses.create(
model="gpt-4.1",
tools=[{
"type": "mcp",
"server_label": "tavily",
"server_url": "https://mcp.tavily.com/mcp/?tavilyApiKey=<your-api-key>",
"require_approval": "never",
}],
input="Search for recent AI developments"
)Available MCP Tools
| Tool | Description |
|---|---|
| tavily-search | Web search with filters |
| tavily-extract | Extract content from URLs |
---
LangChain
Package: langchain-tavily (recommended over deprecated langchain_community.tools.tavily_search)
pip install -U langchain-tavilyTavilySearch Tool
from langchain_tavily import TavilySearch
tool = TavilySearch(
max_results=5,
topic="general", # "general", "news", "finance"
search_depth="basic", # "basic", "advanced"
include_answer=False,
include_raw_content=False,
include_images=False,
time_range="day", # "day", "week", "month", "year"
include_domains=["wikipedia.org"],
exclude_domains=["pinterest.com"]
)
# Direct invocation
result = tool.invoke({"query": "What happened at Wimbledon?"})Use with Agent
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_tavily import TavilySearch
tavily_search = TavilySearch(max_results=5)
agent = create_agent(
model=ChatOpenAI(model="gpt-4"),
tools=[tavily_search],
system_prompt="You are a research assistant. Use web search for up-to-date info."
)
response = agent.invoke({
"messages": [{"role": "user", "content": "Latest AI developments?"}]
})Tip: Inject today's date in system prompt for time-aware searches.
---
LlamaIndex
pip install llama-index-tools-tavily-research llama-index tavily-pythonTavilyToolSpec
from llama_index.tools.tavily_research.base import TavilyToolSpec
from llama_index.agent.openai import OpenAIAgent
tavily_tool = TavilyToolSpec(api_key='tvly-YOUR_API_KEY')
agent = OpenAIAgent.from_tools(tavily_tool.to_tool_list())
response = agent.chat('What happened at the latest Burning Man?')Tool: search — returns list of URLs and relevant content for agent use.
---
Google ADK
pip install google-adk mcpAgent Setup
from google.adk.agents import Agent
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPServerParams
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
import os
TAVILY_API_KEY = os.getenv("TAVILY_API_KEY")
root_agent = Agent(
model="gemini-2.5-pro",
name="tavily_agent",
instruction="Use Tavily to search the web, extract content, and explore websites.",
tools=[
MCPToolset(
connection_params=StreamableHTTPServerParams(
url="https://mcp.tavily.com/mcp/",
headers={"Authorization": f"Bearer {TAVILY_API_KEY}"},
),
)
],
)Run Agent
adk create my_agent
adk run my_agent # CLI interface
adk web --port 8000 # Web interfaceAvailable Tools: tavily-search, tavily-extract, tavily-map, tavily-crawl
---
n8n (No-Code)
No-code workflow automation with Tavily.
Setup
1. In n8n, search for Tavily node 2. Add to workflow as:
- Node — standalone Search or Extract
- Tool — for AI agent workflows
3. Connect with Tavily API key
Actions
| Action | Parameters |
|---|---|
| Search | query, topic, include_raw_content, include/exclude domains, search_depth |
| Extract | URL(s), extraction type (basic/advanced) |
Use Cases
- Job search automation
- Competitive intelligence
- Market research & news monitoring
- Lead enrichment
- Content curation
Tip: Use SplitInBatches node for multiple results, Merge node to combine searches.
---
Pydantic AI
pip install "pydantic-ai-slim[tavily]"Agent Setup
import os
from pydantic_ai.agent import Agent
from pydantic_ai.common_tools.tavily import tavily_search_tool
api_key = os.getenv('TAVILY_API_KEY')
agent = Agent(
'openai:o3-mini',
tools=[tavily_search_tool(api_key)],
system_prompt='Search Tavily for the given query and return the results.'
)
result = agent.run_sync('Top news in GenAI world, give me links.')
print(result.output)---
CrewAI
Multi-agent framework with Tavily web search.
pip install 'crewai[tools]'TavilySearchTool
from crewai import Agent, Task, Crew
from crewai_tools import TavilySearchTool
tavily_tool = TavilySearchTool(
search_depth="advanced",
max_results=10,
include_answer=True
)
researcher = Agent(
role='News Researcher',
goal='Find trending information about AI agents',
backstory='Expert researcher specializing in AI technology.',
tools=[tavily_tool],
verbose=True
)
research_task = Task(
description='Search for the top 3 Agentic AI trends in 2025.',
expected_output='A JSON report summarizing the top 3 AI trends.',
agent=researcher
)
crew = Crew(agents=[researcher], tasks=[research_task], verbose=True)
result = crew.kickoff()Search Parameters: query, search_depth, topic, time_range, max_results, include/exclude_domains, include_answer, include_raw_content, include_images, timeout
TavilyExtractorTool
from crewai_tools import TavilyExtractorTool
tavily_extract = TavilyExtractorTool(
extract_depth="advanced",
include_images=True
)
extractor = Agent(
role='Content Extractor',
goal='Extract key information from web pages',
tools=[tavily_extract]
)Extract Parameters: urls, include_images, extract_depth, timeout
Tavily JavaScript SDK Reference
Installation
npm i @tavily/core
# or
yarn add @tavily/core
# or
pnpm add @tavily/coreClient Setup
import { tavily } from "@tavily/core";
// From environment variable TAVILY_API_KEY
const client = tavily();
// Explicit key
const client = tavily({ apiKey: "tvly-YOUR_API_KEY" });
// With proxy (axios proxy config)
const client = tavily({
apiKey: "tvly-YOUR_API_KEY",
httpClient: {
proxy: {
host: "proxy.example.com",
port: 8080,
},
},
});---
search()
const response = await client.search(query, {
// Search behavior
searchDepth: "basic", // "basic"|"advanced"|"fast"|"ultra-fast"
topic: "general", // "general"|"news"|"finance"
autoParameters: false, // Auto-configure based on query
// Time filtering
timeRange: "day", // "day"|"week"|"month"|"year"
startDate: "2024-01-01", // YYYY-MM-DD format
endDate: "2024-12-31", // YYYY-MM-DD format
// Results configuration
maxResults: 5, // 0-20
chunksPerSource: 3, // 1-3, requires searchDepth="advanced"
// Content options
includeAnswer: false, // true|"basic"|"advanced"
includeRawContent: false, // true|"markdown"|"text"
includeImages: false,
includeImageDescriptions: false,
includeFavicon: false,
// Domain filtering
includeDomains: ["example.com"], // Max 300
excludeDomains: ["spam.com"], // Max 150
// Regional
country: "US", // Boost results from country
// Usage tracking
includeUsage: false,
});Response Structure
{
query: string;
answer?: string;
results: Array<{
title: string;
url: string;
content: string;
score: number;
rawContent?: string;
publishedDate?: string;
favicon?: string;
}>;
images?: Array<{url: string; description?: string}>;
responseTime: number;
usage?: {credits: number};
requestId: string;
}---
extract()
const response = await client.extract(urls, {
// urls: string or string[] (max 20)
// Extraction options
extractDepth: "basic", // "basic"|"advanced"
format: "markdown", // "markdown"|"text"
includeImages: false,
includeFavicon: false,
// Relevance filtering
query: "specific question", // Rerank chunks by relevance
chunksPerSource: 3, // 1-5, requires query
// Timing
timeout: 30, // 1-60 seconds
includeUsage: false,
});Response Structure
{
results: Array<{
url: string;
rawContent: string;
images?: string[];
favicon?: string;
}>;
failedResults: Array<{
url: string;
error: string;
}>;
responseTime: number;
usage?: {credits: number};
requestId: string;
}---
crawl()
const response = await client.crawl(url, {
// Scope control
maxDepth: 1, // 1-5 levels
maxBreadth: 20, // Links per page
limit: 50, // Total pages
// AI guidance (doubles credit cost)
instructions: "Focus on API docs",
// Path filtering (regex patterns)
selectPaths: ["/docs/.*"], // Only crawl matching paths
excludePaths: ["/private/.*"], // Skip matching paths
// Domain filtering (regex patterns)
selectDomains: ["example\\.com"],
excludeDomains: ["ads\\..*"],
allowExternal: true, // Follow external links
// Extraction options
extractDepth: "basic", // "basic"|"advanced"
format: "markdown", // "markdown"|"text"
includeImages: false,
includeFavicon: false,
chunksPerSource: 3, // 1-5, with instructions
// Timing
timeout: 150, // 10-150 seconds
includeUsage: false,
});Response Structure
{
baseUrl: string;
results: Array<{
url: string;
rawContent: string;
images?: string[];
favicon?: string;
}>;
responseTime: number;
usage?: {credits: number};
requestId: string;
}---
map()
const response = await client.map(url, {
// Scope control
maxDepth: 1, // 1-5 levels
maxBreadth: 20, // Links per page
limit: 50, // Total URLs
// AI guidance (doubles credit cost)
instructions: "Find all API endpoints",
// Path filtering (regex patterns)
selectPaths: ["/api/.*"],
excludePaths: ["/internal/.*"],
// Domain filtering (regex patterns)
selectDomains: ["api\\.example\\.com"],
excludeDomains: ["beta\\..*"],
allowExternal: true,
// Timing
timeout: 150, // 10-150 seconds
includeUsage: false,
});Response Structure
{
baseUrl: string;
results: string[]; // List of URLs (no content)
responseTime: number;
usage?: {credits: number};
requestId: string;
}---
TypeScript Types
import type { TavilySearchOptions, TavilySearchResponse, TavilyExtractOptions, TavilyExtractResponse, TavilyCrawlOptions, TavilyCrawlResponse, TavilyMapOptions, TavilyMapResponse } from "@tavily/core";---
Error Handling
import { tavily } from "@tavily/core";
const client = tavily();
try {
const response = await client.search("query");
} catch (error) {
if (error.status === 401) {
console.error("Invalid API key");
} else if (error.status === 429) {
console.error("Rate limit or credits exceeded");
} else {
console.error(`Request failed: ${error.message}`);
}
}---
Best Practices
// ✅ Parallel searches with Promise.all
const queries = ["AI", "ML", "LLM"];
const results = await Promise.all(queries.map((q) => client.search(q)));
// ✅ Domain filtering for quality
const response = await client.search("python tutorials", {
includeDomains: ["python.org", "realpython.com"],
excludeDomains: ["medium.com"],
});
// ✅ Auto-configure for unknown queries
const response = await client.search(userInput, {
autoParameters: true,
});
// ✅ Get structured answers for RAG
const response = await client.search("What is quantum computing?", {
includeAnswer: "advanced",
includeRawContent: "markdown",
});
// ✅ Crawl with path filtering
const docs = await client.crawl("https://docs.example.com", {
selectPaths: ["/api/.*", "/guides/.*"],
excludePaths: ["/blog/.*"],
maxDepth: 2,
});---
Parameter Naming Convention
JavaScript SDK uses camelCase (Python SDK uses snake_case):
| Python | JavaScript |
|---|---|
| search_depth | searchDepth |
| max_results | maxResults |
| include_answer | includeAnswer |
| include_raw_content | includeRawContent |
| include_images | includeImages |
| include_domains | includeDomains |
| exclude_domains | excludeDomains |
| time_range | timeRange |
| start_date | startDate |
| end_date | endDate |
| chunks_per_source | chunksPerSource |
| auto_parameters | autoParameters |
| include_favicon | includeFavicon |
| include_usage | includeUsage |
| extract_depth | extractDepth |
| max_depth | maxDepth |
| max_breadth | maxBreadth |
| select_paths | selectPaths |
| exclude_paths | excludePaths |
| select_domains | selectDomains |
| exclude_domains | excludeDomains |
| allow_external | allowExternal |
Tavily Python SDK Reference
Installation
pip install tavily-pythonClient Setup
Synchronous Client
from tavily import TavilyClient, TavilyKeylessLimitError
# From environment variable TAVILY_API_KEY, or keyless mode in 0.7.25+
client = TavilyClient()
# Explicit key
client = TavilyClient(api_key="tvly-YOUR_API_KEY")
# With proxy
client = TavilyClient(
api_key="tvly-YOUR_API_KEY",
proxies={
"http://": "http://proxy.example.com:8080",
"https://": "http://proxy.example.com:8080"
}
)Keyless Mode
In tavily-python 0.7.25+, TavilyClient() with no api_key can run against Tavily's public keyless API for quick trials. Treat this as an exploration mode, not production configuration.
from tavily import TavilyClient, TavilyKeylessLimitError
client = TavilyClient()
try:
response = client.search("Who is Leo Messi?")
except TavilyKeylessLimitError as exc:
print(exc.retry_after_seconds)Keyless mode supports search() and extract() only. crawl(), map(), research(), and higher limits require a real API key.
Organization header
In tavily-python 0.7.26+, pass an optional org_id to attribute requests to a specific organization. It is sent as the X-Tavily-Orgid header.
client = TavilyClient(api_key="tvly-YOUR_API_KEY", org_id="org-123")Async Client
from tavily import AsyncTavilyClient
client = AsyncTavilyClient()
response = await client.search("query")---
search()
response = client.search(
query="string", # Required
# Search behavior
search_depth="basic", # "basic"|"advanced"|"fast"|"ultra-fast"
topic="general", # "general"|"news"|"finance"
auto_parameters=False, # Auto-configure based on query
# Time filtering
time_range="day", # "day"|"week"|"month"|"year"
start_date="2024-01-01", # YYYY-MM-DD format
end_date="2024-12-31", # YYYY-MM-DD format
# Results configuration
max_results=5, # 0-20
chunks_per_source=3, # 1-3, requires search_depth="advanced"
# Content options
include_answer=False, # True|"basic"|"advanced"
include_raw_content=False, # True|"markdown"|"text"
include_images=False,
include_image_descriptions=False,
include_favicon=False,
# Domain filtering
include_domains=["example.com"], # Max 300
exclude_domains=["spam.com"], # Max 150
# Regional
country="US", # Boost results from country
# Usage tracking
include_usage=False
)Response Structure
{
"query": str,
"answer": str | None,
"results": [
{
"title": str,
"url": str,
"content": str,
"score": float,
"raw_content": str | None,
"published_date": str | None,
"favicon": str | None
}
],
"images": [{"url": str, "description": str}],
"response_time": float,
"usage": {"credits": int} | None,
"request_id": str
}---
extract()
response = client.extract(
urls="https://example.com", # String or list of URLs (max 20)
# Extraction options
extract_depth="basic", # "basic"|"advanced"
format="markdown", # "markdown"|"text"
include_images=False,
include_favicon=False,
# Relevance filtering
query="specific question", # Rerank chunks by relevance
chunks_per_source=3, # 1-5, requires query
# Timing
timeout=30, # 1-60 seconds
include_usage=False
)Response Structure
{
"results": [
{
"url": str,
"raw_content": str,
"images": [str] | None,
"favicon": str | None
}
],
"failed_results": [
{"url": str, "error": str}
],
"response_time": float,
"usage": {"credits": int} | None,
"request_id": str
}---
crawl()
response = client.crawl(
url="https://example.com", # Required: starting URL
# Scope control
max_depth=1, # 1-5 levels
max_breadth=20, # Links per page
limit=50, # Total pages
# AI guidance (doubles credit cost)
instructions="Focus on API docs",
# Path filtering (regex patterns)
select_paths=["/docs/.*"], # Only crawl matching paths
exclude_paths=["/private/.*"], # Skip matching paths
# Domain filtering (regex patterns)
select_domains=["example\\.com"],
exclude_domains=["ads\\..*"],
allow_external=True, # Follow external links
# Extraction options
extract_depth="basic", # "basic"|"advanced"
format="markdown", # "markdown"|"text"
include_images=False,
include_favicon=False,
chunks_per_source=3, # 1-5, with instructions
# Timing
timeout=150, # 10-150 seconds
include_usage=False
)Response Structure
{
"base_url": str,
"results": [
{
"url": str,
"raw_content": str,
"images": [str] | None,
"favicon": str | None
}
],
"response_time": float,
"usage": {"credits": int} | None,
"request_id": str
}---
map()
response = client.map(
url="https://example.com", # Required: starting URL
# Scope control
max_depth=1, # 1-5 levels
max_breadth=20, # Links per page
limit=50, # Total URLs
# AI guidance (doubles credit cost)
instructions="Find all API endpoints",
# Path filtering (regex patterns)
select_paths=["/api/.*"],
exclude_paths=["/internal/.*"],
# Domain filtering (regex patterns)
select_domains=["api\\.example\\.com"],
exclude_domains=["beta\\..*"],
allow_external=True,
# Timing
timeout=150, # 10-150 seconds
include_usage=False
)Response Structure
{
"base_url": str,
"results": [str], # List of URLs (no content)
"response_time": float,
"usage": {"credits": int} | None,
"request_id": str
}---
TavilyHybridClient (RAG Integration)
For MongoDB-based hybrid RAG with Tavily web search.
from tavily import TavilyHybridClient
client = TavilyHybridClient(
api_key="tvly-YOUR_API_KEY",
db_provider="mongodb",
collection=mongo_collection, # pymongo Collection object
index="vector_index", # Atlas Search index name
embeddings_field="embeddings", # Field with vectors
content_field="content" # Field with text
)
# Search combining local DB + web
results = client.search(
query="What is machine learning?",
max_results=5,
max_local=3, # From MongoDB
max_foreign=2 # From Tavily web search
)Custom Embedding Function
import cohere
co = cohere.Client("cohere-api-key")
def my_embeddings(texts: list[str]) -> list[list[float]]:
response = co.embed(
texts=texts,
model="embed-english-v3.0",
input_type="search_document"
)
return response.embeddings
client = TavilyHybridClient(
...,
embedding_function=my_embeddings
)Custom Ranking Function
def my_ranker(query: str, documents: list[str], top_n: int) -> list[dict]:
response = co.rerank(
query=query,
documents=documents,
model="rerank-english-v2.0",
top_n=top_n
)
return [{"index": r.index, "score": r.relevance_score} for r in response.results]
client = TavilyHybridClient(
...,
ranking_function=my_ranker
)Save Web Results to DB
results = client.search(
query="latest AI news",
save_foreign=True # Cache web results in MongoDB
)---
Error Handling
from tavily import TavilyClient
from tavily.errors import (
InvalidAPIKeyError,
TavilyKeylessLimitError,
UsageLimitExceededError,
MissingAPIKeyError
)
try:
response = client.search("query")
except InvalidAPIKeyError:
print("Invalid API key")
except UsageLimitExceededError:
print("Credit limit reached")
except TavilyKeylessLimitError as exc:
print(f"Keyless limit reached; retry after {exc.retry_after_seconds}s")
except MissingAPIKeyError:
print("No API key provided")
except Exception as e:
print(f"Request failed: {e}")---
Best Practices
# ✅ Use async for high throughput
async def batch_search(queries: list[str]):
client = AsyncTavilyClient()
tasks = [client.search(q) for q in queries]
return await asyncio.gather(*tasks)
# ✅ Filter domains for quality
response = client.search(
query="python tutorials",
include_domains=["python.org", "realpython.com"],
exclude_domains=["medium.com"]
)
# ✅ Use auto_parameters for unknown query types
response = client.search(
query=user_input,
auto_parameters=True
)
# ✅ Get structured answers for RAG
response = client.search(
query="What is quantum computing?",
include_answer="advanced",
include_raw_content="markdown"
)