---
name: exa
description: Use when building web search capabilities into agents, LLMs, or applications. Exa provides real-time web search, page content extraction, deep research, and structured data enrichment. Use for finding relevant pages, extracting clean content, building researched lists, enriching data with web evidence, or giving LLMs access to current web information.
metadata:
    mintlify-proj: exa
    version: "1.0"
---

# Exa Skill

## Product summary

Exa is a web search API optimized for agents and LLMs. It provides real-time semantic search, page content extraction, and async research workflows. Agents use Exa to find relevant web pages, extract clean content, build structured lists, and enrich data with verified sources.

**Key endpoints:**
- `POST /search` — semantic search with highlights, full text, or structured output
- `POST /agent/runs` — async research, list building, enrichment with grounded JSON
- `POST /get-contents` — extract content from known URLs
- `POST /search` with `type: deep` — iterative research with synthesis

**SDKs:** `exa-py` (Python), `exa-js` (JavaScript). Both read `EXA_API_KEY` from environment.

**Primary docs:** https://exa.ai/docs

---

## When to use

Reach for Exa when:

- **An agent needs web search.** Give Claude, ChatGPT, or a custom agent access to current web information via tool calling or MCP.
- **Building a researched list.** Find entities matching criteria (companies, people, products) and enrich each with verified details.
- **Extracting page content.** You have URLs and need clean text, highlights, or summaries without parsing HTML.
- **Verifying claims.** Research a fact or entity against authoritative sources and return grounded citations.
- **Enriching structured data.** Add web-sourced fields to existing records (company funding, person's current role, product pricing).
- **Real-time synthesis.** Return structured JSON shaped to a schema, synthesized from multiple sources.

Do **not** use Exa for:
- Searching private or internal data (Exa searches the public web only)
- Guaranteed freshness on pages that change hourly (use `maxAgeHours: 0` but expect latency)
- Pagination (search returns up to 100 results; no cursor-based pagination)

---

## Quick reference

### Search types and latency

| Type | Latency | Use when |
| --- | --- | --- |
| `instant` | ~250 ms | Real-time paths (autocomplete, voice) |
| `fast` | ~450 ms | User-facing, latency-sensitive |
| `auto` | ~1 s | Default; balanced quality and speed |
| `deep-lite` | ~4 s | Lightweight research with synthesis |
| `deep` | 4–15 s | Multi-step research, harder queries |
| `deep-reasoning` | 12–40 s | Complex analysis; use Agent instead |

### Search request shape (minimal)

```python
exa.search(
    "your query in natural language",
    contents={"highlights": True}
)
```

### Agent request shape (minimal)

```python
exa.agent.runs.create(
    query="Find 10 companies matching criteria. Return name, website, funding date.",
    output_schema={
        "type": "object",
        "properties": {
            "companies": {
                "type": "array",
                "maxItems": 10,
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "website": {"type": "string"},
                        "funding_date": {"type": "string"}
                    },
                    "required": ["name"]
                }
            }
        },
        "required": ["companies"]
    },
    effort="auto"
)
```

### Content options

| Option | Effect | Cost |
| --- | --- | --- |
| `highlights: true` | Query-relevant excerpts (recommended) | Included |
| `text: {maxCharacters: N}` | Full page text, capped at N chars | Included |
| `summary: {query: "..."}` | LLM-generated summary | +1 LLM call per result |
| `maxAgeHours: 0` | Fetch fresh content | +latency |
| `maxAgeHours: -1` | Cache-only (fastest) | Included |

### Agent effort levels

| Effort | Price | Use for |
| --- | --- | --- |
| `minimal` | $0.012/req | Cheapest lookups |
| `low` | $0.025/req | Simple factual tasks |
| `medium` | $0.10/req | Standard research (default) |
| `high` | $0.50/req | Harder research, more citations |
| `xhigh` | $1.00/req | Complex schemas, deep verification |
| `auto` | Metered, $5 cap | Variable-scope work, list building |
| `ultra` | Metered, $20 cap | Exhaustive research, large lists |

### Common filters

```python
exa.search(
    query,
    include_domains=["anthropic.com", "openai.com/blog"],  # Hard constraint
    exclude_domains=["medium.com", "dev.to"],              # Hard constraint
    start_published_date="2025-01-01",                     # ISO 8601
    end_published_date="2025-12-31",
    num_results=20,                                        # Up to 100
    contents={"highlights": True}
)
```

---

## Decision guidance

### Search vs. Agent vs. Contents

| Task | Use | Why |
| --- | --- | --- |
| Find pages for a query, return ranked results | Search with `type: auto` | Fast, simple, you control downstream reasoning |
| Build a list of 10+ entities matching criteria | Agent with `effort: auto` | Handles multi-step research, verification, enrichment |
| Extract content from 1–5 known URLs | Contents API | Simpler than search, no ranking needed |
| Synthesize results into JSON | Search with `outputSchema` | Faster than Agent for single-pass synthesis |
| Research a complex topic with multiple angles | Agent with `effort: ultra` | Highest reasoning depth, handles hard verification |

### Highlights vs. full text

| Need | Use | Avoid |
| --- | --- | --- |
| Token-efficient context for an LLM | `highlights: true` | Full text for every result |
| Broader page context, document structure | `text: {maxCharacters: 10000}` | Highlights when you need full paragraphs |
| Fixed excerpt limit per page | `highlights: {maxCharacters: 2000}` | Unbounded highlights |

### When to add outputSchema

| Scenario | Add schema | Reason |
| --- | --- | --- |
| Downstream code needs structured fields | Yes | Validates shape, enables parsing |
| You want prose + citations | No | Use `output.text` and `output.grounding` |
| Synthesizing 3+ fields from search | Yes | Helps model organize output |
| Single-field synthesis (e.g., summary) | No | Use `contents.summary` instead |

---

## Workflow

### 1. Search for pages

**Understand the task.** What pages do you need? What kind of source (blog, research, news)? What time period?

**Write a natural-language query.** Include the subject and source type if it matters:
```text
Recent technical articles on retrieval-augmented generation for legal documents
```

**Make the request:**
```python
result = exa.search(
    "Recent technical articles on retrieval-augmented generation for legal documents",
    type="auto",
    contents={"highlights": True},
    num_results=10
)
```

**Inspect the response.** Check titles, URLs, publication dates, and highlights. If results are off-topic, refine the query (not the filters).

### 2. Extract content from pages

**If you have URLs,** use Contents API:
```python
result = exa.get_contents(
    urls=["https://example.com/page1", "https://example.com/page2"],
    contents={"highlights": True}
)
```

**If you got URLs from search,** use the `id` field from each result:
```python
result = exa.get_contents(
    ids=[result.results[0].id],
    contents={"text": {"max_characters": 5000}}
)
```

### 3. Build a researched list

**Define the entity and criteria.** What are you looking for? How many? What counts as a match?

**Write a task-specific query:**
```text
Find 10 AI infrastructure companies that raised Series A or B in the last 6 months.
For each, return the company name, website, funding round, and announcement date.
Verify funding from official announcements or reputable business publications.
```

**Create an Agent run:**
```python
run = exa.agent.runs.create(
    query="Find 10 AI infrastructure companies...",
    output_schema={
        "type": "object",
        "properties": {
            "companies": {
                "type": "array",
                "maxItems": 10,
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "website": {"type": "string"},
                        "round": {"type": "string"},
                        "announcement_date": {"type": "string"}
                    },
                    "required": ["name", "round"]
                }
            }
        },
        "required": ["companies"]
    },
    effort="auto"
)
```

**Poll for completion:**
```python
run = exa.agent.runs.poll_until_finished(run.id, poll_interval=4000)
print(run.output.structured)  # Your list
print(run.output.grounding)   # Citations
```

### 4. Enrich existing records

**Prepare your data.** Put existing records in `input.data`:
```python
run = exa.agent.runs.create(
    query="For each company, find the current CEO and their LinkedIn URL.",
    input={
        "data": [
            {"company": "Anthropic"},
            {"company": "OpenAI"},
        ]
    },
    output_schema={
        "type": "object",
        "properties": {
            "companies": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "company": {"type": "string"},
                        "ceo": {"type": "string"},
                        "linkedin_url": {"type": "string", "format": "uri"}
                    },
                    "required": ["company", "ceo"]
                }
            }
        },
        "required": ["companies"]
    },
    effort="low"
)
```

### 5. Give an LLM web search

**Use tool calling.** Register Exa's search and contents tools with your LLM:

```python
from exa_py import Exa
from openai import OpenAI

exa = Exa()
client = OpenAI()

messages = [{"role": "user", "content": "What's the latest on AI chips?"}]

completion = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=[exa.openai.web_search(), exa.openai.get_contents()]
)

message = completion.choices[0].message
messages.append(message)
messages += exa.openai.handle_tool_calls(message)

# Continue the conversation
completion = client.chat.completions.create(
    model="gpt-4o",
    messages=messages
)
print(completion.choices[0].message.content)
```

---

## Common gotchas

**Pagination doesn't exist.** Search returns up to 100 results; there is no cursor or offset. If you need more, run a new search with a refined query.

**Highlights are sized per result.** You cannot set a global character budget across all results. Use `highlights.maxCharacters` only if your application requires a fixed excerpt limit per page.

**outputSchema validates shape, not facts.** Agent may return `null` for fields when evidence doesn't support them, even if the schema marks them required. Always validate important claims against `output.grounding`.

**Agent runs are async.** Do not hold an HTTP request open waiting for completion. Create the run, save its ID, poll in the background, and store the result when done.

**maxAgeHours controls content freshness, not result recency.** It does not filter results by publication date. Use `startPublishedDate` and `endPublishedDate` for date filtering.

**Deprecated parameters still appear in old examples.** Use the API reference, not blog posts. Common mistakes:
- `useAutoprompt` → remove it (Exa interprets queries directly)
- `includeUrls` → use `includeDomains`
- `numSentences` → use `highlights: true` (Exa sizes excerpts automatically)
- `livecrawl: "always"` → use `maxAgeHours: 0`

**Tool calling requires both SDKs.** You need both `exa-py`/`exa-js` and `openai`/`anthropic` installed. The Exa SDK provides the tool schema and handler; the LLM SDK handles the conversation loop.

**Effort modes have different pricing models.** Fixed efforts (`minimal`, `low`, `medium`, `high`, `xhigh`) cost the same per request. `auto` and `ultra` are metered by usage and have default caps ($5 and $20). Set `budget.maxCostDollars` to enforce a hard ceiling.

**ZDR (Zero Data Retention) teams cannot use previousRunId.** If your team has ZDR enabled, consume the live stream or poll within 10 minutes of completion. Run data is deleted after that window.

---

## Verification checklist

Before submitting work with Exa:

- [ ] **API key is set.** Check `EXA_API_KEY` environment variable or pass it explicitly.
- [ ] **Query is natural language.** Avoid keyword bags; describe the pages or entities you want.
- [ ] **Content option is appropriate.** Use `highlights: true` for most tasks; only use `text` or `summary` when needed.
- [ ] **Filters are hard constraints.** Only use `includeDomains`, `excludeDomains`, and date filters when a result violating them is unusable.
- [ ] **Agent schema is bounded.** Arrays have `maxItems`; required fields are actually required by the task, not just the schema.
- [ ] **Grounding is persisted.** For Agent runs, store `output.grounding` alongside `output.structured` for citations.
- [ ] **Error handling is in place.** Check for 402 (out of credits), 429 (rate limit), and 5xx (retry with backoff).
- [ ] **Async runs are handled correctly.** Create, save ID, poll in background, store result—do not block on completion.
- [ ] **Tool calling loop is complete.** Keep tools on every request and repeat until the model stops calling them.

---

## Resources

**Comprehensive page listing:** https://exa.ai/docs/llms.txt

**Critical docs:**
1. [Search Quickstart](https://exa.ai/docs/search/quickstart) — core request shapes, search types, output options
2. [Agent Quickstart](https://exa.ai/docs/agent/quickstart) — async research, list building, structured output
3. [Search Best Practices](https://exa.ai/docs/search/best-practices) — query quality, latency budgeting, common mistakes

---

> For additional documentation and navigation, see: https://exa.ai/docs/llms.txt