How to Use Exa, Tavily, and Firecrawl in OpenClaw
Problem
When I built research agents in OpenClaw, I had to manually install and configure web search plugins. Each plugin had different APIs, configuration formats, and dependency issues. The setup was painful.
OpenClaw 2026.3.22-beta.1 solved this by bundling Exa, Tavily, and Firecrawl as first-class plugins. But now I faced a different question: which of these three should I use, and when?
This post shows how to use OpenClaw’s bundled web search plugins. The key point is matching each plugin to its strength.
What Changed in OpenClaw 2026.3.22-beta.1
The release notes said:
“Exa added with native date filters, search-mode selection, and optional content extraction.” “Tavily added with dedicated tavily_search and tavily_extract tools.” “Firecrawl added with firecrawl_search and firecrawl_scrape tools.”
This means three serious web-search options are now bundled and first-class. No manual plugin install required - they work out of the box. Each has its own config namespace so I can run them alongside each other.
Research agents just got more capable out of the box.
When to Use Each Plugin
After testing all three, I found each excels at different tasks:
| Plugin | Best For | Key Feature |
|---|---|---|
| Exa | Semantic search with recency | Native date filtering |
| Tavily | Structured data extraction | Clean JSON output |
| Firecrawl | Full page scraping | JavaScript rendering |
Let me show you how I use each one.
Exa: Semantic Search with Date Filters
I use Exa when I need AI-native search that understands meaning, not just keywords. Its date filtering is unique among the three.
When I Use Exa
- Finding recent articles (last 7 days, last month)
- Semantic research where keyword search fails
- Finding related content through meaning
- Agentic research loops where search quality matters
Configuration
plugins: exa: apiKey: "your-exa-api-key" defaultSearchMode: "auto" # auto | keyword | neural defaultNumResults: 10Basic Semantic Search
// Exa search with semantic understandingconst results = await exa.search({ query: "latest developments in AI agent frameworks", numResults: 10, useAutoprompt: true})
// Results are ranked by semantic relevance, not just keyword matchresults.results.forEach(result => { console.log(result.title) console.log(result.url) console.log(result.score) // Relevance score})Date-Filtered Search
I use this feature constantly for news and recent developments:
// Find articles from last 7 daysconst lastWeek = new Date()lastWeek.setDate(lastWeek.getDate() - 7)
const recentResults = await exa.search({ query: "OpenClaw release updates", numResults: 5, startPublishedDate: lastWeek.toISOString(), endPublishedDate: new Date().toISOString()})Search with Content Extraction
// Search and extract full content in one callconst searchWithContent = await exa.search({ query: "LangGraph tutorial", numResults: 5, contents: { text: { maxCharacters: 1000 }, highlights: { numSentences: 3 } }})
// Each result now includes extracted textsearchWithContent.results.forEach(result => { console.log(result.text) // Extracted content console.log(result.highlights) // Key highlights})Tavily: Structured Data Extraction
I use Tavily when I need clean, structured data from web searches. Its output is optimized for agent consumption.
When I Use Tavily
- Building knowledge bases from web content
- Extracting specific data points from multiple pages
- Research agents that need structured input
- Follow-up extraction after initial discovery
Configuration
plugins: tavily: apiKey: "your-tavily-api-key" includeRawContent: false maxResults: 5Basic Search with AI Answer
// Tavily search with structured outputconst results = await tavily.search({ query: "best practices for MCP server development", maxResults: 5, includeAnswer: true // Get AI-generated answer})
console.log(results.answer) // AI-generated summaryconsole.log(results.results) // Structured resultsExtract Structured Data from URLs
// Extract from specific URLsconst extracted = await tavily.extract({ urls: [ "https://docs.openclaw.ai/plugins/overview", "https://docs.openclaw.ai/mcp/integration" ], extractDepth: "advanced" // basic | advanced})
// Get structured data from each URLextracted.forEach(page => { console.log(page.url) console.log(page.rawContent) // Cleaned content console.log(page.metadata) // Page metadata})Search Specific Domains
// I use this for documentation-only searchesconst domainResults = await tavily.search({ query: "agent orchestration patterns", maxResults: 10, includeDomains: ["docs.anthropic.com", "python.langchain.com"]})Firecrawl: Full Page Scraping
I use Firecrawl when I need complete page content, especially for JavaScript-rendered pages or documentation ingestion.
When I Use Firecrawl
- Documentation ingestion
- Full article/blog content extraction
- Pages requiring JavaScript rendering
- Converting web content to LLM-friendly format
Configuration
plugins: firecrawl: apiKey: "your-firecrawl-api-key" formats: ["markdown"] waitForEvent: "load"Full Page Scrape
// Scrape a single page with markdown outputconst page = await firecrawl.scrape({ url: "https://docs.openclaw.ai/getting-started", formats: ["markdown", "html"]})
console.log(page.markdown) // LLM-friendly markdownconsole.log(page.html) // Original HTMLconsole.log(page.metadata) // Page metadataSearch and Scrape Combined
// Search and get full content in one callconst results = await firecrawl.search({ query: "OpenClaw browser automation guide", limit: 5})
// Each result includes full scraped contentresults.forEach(result => { console.log(result.markdown) // Full content})Handle JavaScript-Rendered Pages
This is Firecrawl’s superpower - it handles dynamic content:
// Scrape JavaScript-rendered pagesconst dynamicPage = await firecrawl.scrape({ url: "https://example.com/spa-page", formats: ["markdown"], waitFor: 2000, // Wait 2 seconds for JS to render actions: [ { type: "scroll", direction: "down" } ]})
// Get content that would be invisible to simple scrapersconsole.log(dynamicPage.markdown)Combining Plugins in a Research Agent
The real power comes from chaining these plugins together. Here’s how I built a research agent:
async function researchTopic(topic: string) { // 1. Use Exa for semantic discovery with recency const discoveries = await exa.search({ query: topic, numResults: 10, startPublishedDate: getLastWeekISOString(), useAutoprompt: true })
// 2. Use Tavily for structured extraction from top results const topUrls = discoveries.results.slice(0, 3).map(r => r.url) const structured = await tavily.extract({ urls: topUrls, extractDepth: "advanced" })
// 3. Use Firecrawl for full content if needed if (needsFullContent(structured)) { const fullContent = await firecrawl.scrape({ url: topUrls[0], formats: ["markdown"] }) return { discoveries, structured, fullContent } }
return { discoveries, structured }}
function getLastWeekISOString(): string { const date = new Date() date.setDate(date.getDate() - 7) return date.toISOString()}
function needsFullContent(structured: any): boolean { // Check if we need more detail return structured.some(page => page.rawContent.length < 500)}Common Mistakes
Mistake 1: Using One Plugin for Everything
Each plugin is optimized for different tasks. Don’t force Firecrawl to do semantic search, or Exa to do full-page scraping. Match the tool to the job.
// WRONG: Using Firecrawl for semantic searchconst results = await firecrawl.search({ query: "AI agent frameworks", limit: 10})// Firecrawl's search is basic, not semantic
// CORRECT: Use Exa for semantic searchconst results = await exa.search({ query: "AI agent frameworks", numResults: 10, useAutoprompt: true})Mistake 2: Ignoring API Keys
While bundled, each plugin still requires its own API key:
# WRONG: Missing API keysplugins: exa: defaultSearchMode: "auto" # No apiKey - will fail
# CORRECT: Configure keysplugins: exa: apiKey: "your-exa-api-key" defaultSearchMode: "auto"Mistake 3: Not Chaining Tools
The power comes from combining plugins:
// WRONG: Using only one toolconst results = await exa.search({ query: topic })
// CORRECT: Chain tools for better resultsconst discoveries = await exa.search({ query: topic })const structured = await tavily.extract({ urls: discoveries.results.map(r => r.url)})Mistake 4: Overlooking Date Filters in Exa
Exa’s date filtering is unique among the three:
// WRONG: Ignoring date filtersconst results = await exa.search({ query: "OpenClaw updates"})// Gets old, potentially outdated results
// CORRECT: Use date filters for recent contentconst lastMonth = new Date()lastMonth.setMonth(lastMonth.getMonth() - 1)
const results = await exa.search({ query: "OpenClaw updates", startPublishedDate: lastMonth.toISOString()})Mistake 5: Forgetting Rate Limits
Each service has its own rate limits:
// WRONG: No rate limitingfor (const url of urls) { await firecrawl.scrape({ url })}
// CORRECT: Add delaysfor (const url of urls) { await firecrawl.scrape({ url }) await sleep(1000) // Respect rate limits}Plugin Comparison
Here’s my quick reference for choosing:
| Feature | Exa | Tavily | Firecrawl |
|---|---|---|---|
| Primary Use | Semantic search | Structured extraction | Full scraping |
| Search Quality | AI-native, semantic | Keyword-based | Basic search |
| Date Filtering | Yes (unique) | No | No |
| Content Depth | Optional extraction | Structured data | Full markdown |
| JS Rendering | No | Limited | Yes |
| Rate Limits | Per API plan | Per API plan | Per API plan |
| Best For | Research discovery | Data extraction | Documentation |
Summary
In this post, I showed how to use OpenClaw’s bundled web search plugins. The key point is matching each plugin to its strength:
- Use Exa when you need semantic search with date filtering - ideal for finding recent, relevant content
- Use Tavily when you need structured, clean data extraction - ideal for building knowledge bases
- Use Firecrawl when you need complete page content - ideal for documentation ingestion and JS-rendered pages
All three can be configured independently and used together in research workflows. Configure your API keys, understand each plugin’s strengths, and chain them together for powerful research agents.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Exa API Documentation
- 👨💻 Tavily Documentation
- 👨💻 Firecrawl Documentation
- 👨💻 Reddit Discussion: OpenClaw Release Notes
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments