Skip to content

Instantly share code, notes, and snippets.

@usametov
Created March 12, 2026 07:08
Show Gist options
  • Select an option

  • Save usametov/c873bcffa19b85d489f4acc9b81770cd to your computer and use it in GitHub Desktop.

Select an option

Save usametov/c873bcffa19b85d489f4acc9b81770cd to your computer and use it in GitHub Desktop.
exa integration prompt

Exa API Setup Guide

Your Configuration

Setting Value
Coding Tool Claude
Framework cURL
Use Case Web search tool
Search Type Auto - Balanced relevance and speed (~1 second)
Content Full text
Project Description: job search, rentals, real estate, etc.

API Key Setup

Environment Variable

export EXA_API_KEY="YOUR_API_KEY"

.env File

EXA_API_KEY=YOUR_API_KEY

🔌 Exa MCP Server for Claude Code

Give Claude Code real-time web search, code context, and company research with Exa MCP.

Run in terminal:

claude mcp add --transport http exa https://mcp.exa.ai/mcp?exaApiKey=1d5eb8ee-ade7-4d6b-8c09-725747467194

Tool enablement (optional): Add a tools= query param to the MCP URL.

Enable specific tools:

https://mcp.exa.ai/mcp?exaApiKey=1d5eb8ee-ade7-4d6b-8c09-725747467194&tools=web_search_exa,get_code_context_exa,people_search_exa

Enable all tools:

https://mcp.exa.ai/mcp?exaApiKey=1d5eb8ee-ade7-4d6b-8c09-725747467194&tools=web_search_exa,web_search_advanced_exa,get_code_context_exa,crawling_exa,company_research_exa,people_search_exa,deep_researcher_start,deep_researcher_check

Your API key: 1d5eb8ee-ade7-4d6b-8c09-725747467194 Manage keys at dashboard.exa.ai/api-keys.

Troubleshooting: if tools don't appear, restart your MCP client after updating the config.

📖 Full docs: docs.exa.ai/reference/exa-mcp


Quick Start (cURL)

cURL

curl -X POST 'https://api.exa.ai/search' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "query": "latest developments in AI safety research",
  "type": "auto",
  "num_results": 10,
  "contents": {
    "text": {
      "max_characters": 20000
    }
  }
}'

Function Calling / Tool Use

Function calling (also known as tool use) allows your AI agent to dynamically decide when to search the web based on the conversation context. Instead of searching on every request, the LLM intelligently determines when real-time information would improve its response—making your agent more efficient and accurate.

Why use function calling with Exa?

  • Your agent can ground responses in current, factual information
  • Reduces hallucinations by fetching real sources when needed
  • Enables multi-step reasoning where the agent searches, analyzes, and responds

📚 Full documentation: https://docs.exa.ai/reference/openai-tool-calling

OpenAI Function Calling

import json
from openai import OpenAI
from exa_py import Exa

openai = OpenAI()
exa = Exa()

tools = [{
    "type": "function",
    "function": {
        "name": "exa_search",
        "description": "Search the web for current information.",
        "parameters": {
            "type": "object",
            "properties": {"query": {"type": "string", "description": "Search query"}},
            "required": ["query"]
        }
    }
}]

def exa_search(query: str) -> str:
    results = exa.search_and_contents(query, type="auto", num_results=10, text={"max_characters": 20000})
    return "\n".join([f"{r.title}: {r.url}" for r in results.results])

messages = [{"role": "user", "content": "What's the latest in AI safety?"}]
response = openai.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)

if response.choices[0].message.tool_calls:
    tool_call = response.choices[0].message.tool_calls[0]
    search_results = exa_search(json.loads(tool_call.function.arguments)["query"])
    messages.append(response.choices[0].message)
    messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": search_results})
    final = openai.chat.completions.create(model="gpt-4o", messages=messages)
    print(final.choices[0].message.content)

Anthropic Tool Use

import anthropic
from exa_py import Exa

client = anthropic.Anthropic()
exa = Exa()

tools = [{
    "name": "exa_search",
    "description": "Search the web for current information.",
    "input_schema": {
        "type": "object",
        "properties": {"query": {"type": "string", "description": "Search query"}},
        "required": ["query"]
    }
}]

def exa_search(query: str) -> str:
    results = exa.search_and_contents(query, type="auto", num_results=10, text={"max_characters": 20000})
    return "\n".join([f"{r.title}: {r.url}" for r in results.results])

messages = [{"role": "user", "content": "Latest quantum computing developments?"}]
response = client.messages.create(model="claude-sonnet-4-20250514", max_tokens=4096, tools=tools, messages=messages)

if response.stop_reason == "tool_use":
    tool_use = next(b for b in response.content if b.type == "tool_use")
    tool_result = exa_search(tool_use.input["query"])
    messages.append({"role": "assistant", "content": response.content})
    messages.append({"role": "user", "content": [{"type": "tool_result", "tool_use_id": tool_use.id, "content": tool_result}]})
    final = client.messages.create(model="claude-sonnet-4-20250514", max_tokens=4096, tools=tools, messages=messages)
    print(final.content[0].text)

Search Type Reference

Type Best For Speed Depth
fast Real-time apps, autocomplete, quick lookups Fastest Basic
auto Most queries - balanced relevance & speed Medium Smart
deep Research, enrichment, thorough results Slow Deep
deep-reasoning Complex research, multi-step reasoning Slowest Deepest

Tip: type="auto" works well for most queries. Use type="deep" when you need thorough research results or structured outputs with field-level grounding.


Content Configuration

Choose ONE content type per request (not both):

Type Config Best For
Text "text": {"max_characters": 20000} Full content extraction, RAG
Highlights "highlights": {"max_characters": 4000} Snippets, summaries, lower cost

⚠️ Token usage warning: Using text: true (full page text) can significantly increase token count, leading to slower and more expensive LLM calls. To mitigate:

  • Add max_characters limit: "text": {"max_characters": 10000}
  • Use highlights instead if you don't need contiguous text

When to use text vs highlights:

  • Text - When you need untruncated, contiguous content (e.g., code snippets, full articles, documentation)
  • Highlights - When you need key excerpts and don't need the full context (e.g., summaries, Q&A, general research)

Domain Filtering (Optional)

Usually not needed - Exa's neural search finds relevant results without domain restrictions.

When to use:

  • Targeting specific authoritative sources
  • Excluding low-quality domains from results

Example:

{
  "includeDomains": ["arxiv.org", "github.com"],
  "excludeDomains": ["pinterest.com"]
}

Note: includeDomains and excludeDomains can be used together to include a broad domain while excluding specific subdomains (e.g., "includeDomains": ["vercel.com"], "excludeDomains": ["community.vercel.com"]).


Web Search Tool

{
  "query": "latest developments in AI safety research",
  "num_results": 10,
  "contents": {
    "text": {
      "max_characters": 20000
    }
  }
}

Tips:

  • Use type: "auto" for most queries
  • Great for building search-powered chatbots or agents
  • Combine with contents for RAG workflows

Category Examples

Use category filters to search dedicated indexes. Each category returns only that content type.

Note: Categories can be restrictive. If you're not getting enough results, try searching without a category first, then add one if needed.

People Search (category: "people")

Find people by role, expertise, or what they work on

curl -X POST 'https://api.exa.ai/search' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "query": "software engineer distributed systems",
  "category": "people",
  "type": "auto",
  "num_results": 10
}'

Tips:

  • Use SINGULAR form
  • Describe what they work on
  • No date/text filters supported

Company Search (category: "company")

Find companies by industry, criteria, or attributes

curl -X POST 'https://api.exa.ai/search' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "query": "AI startup healthcare",
  "category": "company",
  "type": "auto",
  "num_results": 10
}'

Tips:

  • Use SINGULAR form
  • Simple entity queries
  • Returns company objects, not articles

News Search (category: "news")

News articles

curl -X POST 'https://api.exa.ai/search' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "query": "OpenAI announcements",
  "category": "news",
  "type": "auto",
  "num_results": 10,
  "contents": {
    "text": {
      "max_characters": 20000
    }
  }
}'

Tips:

  • Use livecrawl: "preferred" for breaking news
  • Avoid date filters unless required

Research Papers (category: "research paper")

Academic papers

curl -X POST 'https://api.exa.ai/search' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "query": "transformer architecture improvements",
  "category": "research paper",
  "type": "auto",
  "num_results": 10,
  "contents": {
    "text": {
      "max_characters": 20000
    }
  }
}'

Tips:

  • Use type: "auto" for most queries
  • Includes arxiv.org, paperswithcode.com, and other academic sources

Tweet Search (category: "tweet")

Twitter/X posts

curl -X POST 'https://api.exa.ai/search' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "query": "AI safety discussion",
  "category": "tweet",
  "type": "auto",
  "num_results": 10,
  "contents": {
    "text": {
      "max_characters": 20000
    }
  }
}'

Tips:

  • Good for real-time discussions
  • Captures public sentiment

Content Freshness (maxAgeHours)

maxAgeHours sets the maximum acceptable age (in hours) for cached content. If the cached version is older than this threshold, Exa will livecrawl the page to get fresh content.

Value Behavior Best For
24 Use cache if less than 24 hours old, otherwise livecrawl Daily-fresh content
1 Use cache if less than 1 hour old, otherwise livecrawl Near real-time data
0 Always livecrawl (ignore cache entirely) Real-time data where cached content is unusable
-1 Never livecrawl (cache only) Maximum speed, historical/static content
(omit) Default behavior (livecrawl as fallback if no cache exists) Recommended — balanced speed and freshness

When LiveCrawl Isn't Necessary: Cached data is sufficient for many queries, especially for historical topics or educational content. These subjects rarely change, so reliable cached results can provide accurate information quickly.

See maxAgeHours docs for more details.


Other Endpoints

Beyond /search, Exa offers these endpoints:

Endpoint Description Docs
/contents Get contents for known URLs Docs
/answer Q&A with citations from web search Docs

Example - Get contents for URLs:

POST /contents
{
  "urls": ["https://example.com/article"],
  "text": { "max_characters": 20000 }
}

Troubleshooting

Results not relevant?

  1. Try type: "auto" - most balanced option
  2. Try type: "deep" - runs multiple query variations and ranks the combined results
  3. Refine query - use singular form, be specific
  4. Check category matches your use case

Need structured data from search?

  1. Use type: "deep" or type: "deep-reasoning" with outputSchema
  2. Define the fields you need in the schema — Exa returns grounded JSON with citations

Results too slow?

  1. Use type: "fast"
  2. Reduce num_results
  3. Skip contents if you only need URLs

No results?

  1. Remove filters (date, domain restrictions)
  2. Simplify query
  3. Try type: "auto" - has fallback mechanisms

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment