Create your setup prompt
You're all set!
Setup prompt
> **Canonical reference:** https://docs.exa.ai/reference/search-api-guide-for-coding-agents
>
> If anything below looks outdated or contradicts real API behavior, fetch that URL — it is the source of truth for search types, parameters, and response shape. Report staleness back to the user.
---
# Exa API Setup Guide
## Your Configuration
| Setting | Value |
|---------|-------|
| Coding Tool | Not specified |
| Use Case | General |
| Search Type | Auto - Balanced relevance and speed (default) |
| Content | Highlights |
**Project Description:** (Not provided)
---
## API Key Setup
### Environment Variable
```bash
export EXA_API_KEY="YOUR_API_KEY"
```
### .env File
```env
EXA_API_KEY=YOUR_API_KEY
```
---
## Quick Start
### cURL
```bash
curl -X POST 'https://api.exa.ai/search' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"query": "your search query here",
"type": "auto",
"num_results": 10,
"contents": {
"highlights": true
}
}'
```
---
## Search Type Reference
| Type | Best For | Approx Latency | Depth |
|------|----------|----------------|-------|
| `auto` | Most queries — balanced relevance and speed | ~1 second | Smart | ← your selection
| `fast` | Latency-sensitive queries that still need good relevance | ~450 ms | Basic |
| `instant` | Chat, voice, autocomplete, quick lookups | ~250 ms | Basic |
| `deep-lite` | Cheaper synthesis when full deep search is overkill | 4 seconds | Deep |
| `deep` | Research, enrichment, thorough results | 4-15 seconds | Deep |
| `deep-reasoning` | Complex research, multi-step reasoning, hard synthesis tasks | 12-40 seconds | Deepest |
Latency numbers are ballpark — synthesis (`outputSchema`) and forced livecrawls (`contents.maxAgeHours: 0`) stack on top of the base `type`. See the Latency Characteristics section for details.
**Tip:** `type="auto"` works well for most queries. `outputSchema` works on every search type, so you can request structured, grounded output regardless of which type you pick.
---
## Structured Outputs (outputSchema)
`outputSchema` works on **every** search type. Pass a JSON schema and Exa returns the synthesized answer as structured JSON in `output.content`, with field-level citations in `output.grounding`. Deep variants (`deep-lite`, `deep`, `deep-reasoning`) give higher-quality synthesis for complex queries, but the response shape is the same.
**Schema controls:** `type`, `description`, `required`, `properties`, `items`. Max nesting depth 2, max total properties 10. Do NOT add citation or confidence fields to the schema — `/search` returns grounding data automatically.
```bash
curl -X POST 'https://api.exa.ai/search' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"query": "articles about GPUs",
"type": "auto",
"outputSchema": {
"type": "object",
"description": "Companies mentioned in articles",
"required": ["companies"],
"properties": {
"companies": {
"type": "array",
"description": "List of companies mentioned",
"items": {
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": "string", "description": "Name of the company" },
"description": { "type": "string", "description": "Short description of what the company does" }
}
}
}
}
},
"contents": {
"highlights": true
}
}'
```
### Response Shape
Responses with `outputSchema` include:
- `output.content` — structured JSON matching your schema (or a string for `{"type": "text"}` schemas)
- `output.grounding` — array of `{field, citations, confidence}` entries with source URLs
```json
{
"output": {
"content": {
"companies": [
{"name": "Nvidia", "description": "GPU and AI chip manufacturer"},
{"name": "AMD", "description": "Semiconductor company producing GPUs and CPUs"}
]
},
"grounding": [
{
"field": "companies[0].name",
"citations": [{"url": "https://...", "title": "Source"}],
"confidence": "high"
}
]
}
}
```
### When to Use Structured Outputs
- **Enrichment workflows** — extract specific fields (company info, people data, product details)
- **Data pipelines** — get structured data directly instead of parsing free text
- **Grounded answers** — prefer `outputSchema` on `/search` over the legacy `/answer` endpoint
- Prefer a deep variant (`deep-lite`/`deep`/`deep-reasoning`) when you need multi-step reasoning or synthesis across many sources
---
## Content Configuration
The generated examples request highlights by default:
```json
"contents": {
"highlights": true
}
```
Highlights return query-relevant excerpts, which are usually the right content mode for LLM workflows because they keep token usage predictable.
Content is controlled via the `contents` object on `/search` (or top-level fields on `/contents`). You can combine `text`, `highlights`, and `summary` in the same call — they are not mutually exclusive.
| Mode | Config | Best For |
|------|--------|----------|
| Highlights | `"highlights": true` | Token-efficient excerpts |
| Text | `"text": {"maxCharacters": 20000}` | Full content extraction, RAG |
| Summary | `"summary": {"query": "your question"}` or `"summary": true` | LLM-written summary per result |
### Tuning knobs
- **`highlights`** — pass `true` to return query-relevant highlights for each result.
- **`summary`** — pass `true` for a generic summary, or `{"query": "..."}` to bias the summary toward a specific question. Supports a `schema` field for per-result structured output. Summary has no `verbosity` setting — verbosity lives on `text` (below).
- **`text.verbosity`** — `"compact" | "full"` (default `"compact"`). Compact returns only the main content of the page, excluding navbars, banners, footers etc.
- **`text.includeHtmlTags`** — boolean (default `false`). When `true`, preserves HTML structure (useful for code blocks, tables).
- **`text.maxCharacters`** — hard cap on extracted text length. Always set this to control token cost when requesting text.
**Case conventions:** JavaScript SDK and raw JSON use camelCase (`maxCharacters`). Python SDK uses snake_case (`max_characters`) — this applies inside nested dicts too.
**Token usage:** `text: true` with no cap can blow up context. Prefer `highlights: true`, or `text` with `maxCharacters`, for agent workflows.
---
## 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:**
```json
{
"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"]`).
---
## 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](https://exa.ai/docs/reference/livecrawling-contents#maxAgeHours) for more details.
---
## Other Endpoints
Beyond `/search`, the other endpoint you'll commonly use is `/contents`:
| Endpoint | Description | Docs |
|----------|-------------|------|
| `/contents` | Get clean, parsed content for URLs you already have | [Docs](https://exa.ai/docs/reference/get-contents) |
> For grounded answers, use `outputSchema` on `/search` instead of the legacy `/answer` endpoint. `/search` + `outputSchema` returns the same answer-with-citations shape in `output.content` / `output.grounding`.
### /contents — Get Contents for Known URLs
Use `/contents` when you already have URLs and need their content. Unlike `/search` (which finds and optionally retrieves content), `/contents` is purely for content extraction from known URLs.
**When to use `/contents` vs `/search`:**
- URLs from another source (database, user input, RSS feeds) → `/contents`
- Need to refresh stale content for URLs you already have → `/contents` with `maxAgeHours`
- Need to find AND get content in one call → `/search` with `contents`
```bash
curl -X POST 'https://api.exa.ai/contents' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"urls": ["https://example.com/article", "https://example.com/blog-post"],
"highlights": true
}'
```
**Content retrieval options** (choose one per request):
| Option | Config | Best For |
|--------|--------|----------|
| Highlights | `"highlights": true` | Key excerpts, lower token usage |
| Text | `"text": {"max_characters": 20000}` | Full content extraction, RAG |
**Highlights example:**
```json
{
"urls": ["https://example.com/article"],
"highlights": true
}
```
**Freshness control:** Add `maxAgeHours` to ensure content is fresh:
- `24` — livecrawl if cached content is older than 24 hours
- `0` — always livecrawl (ignore cache)
- Omit — use cache when available, livecrawl as fallback
---
## Troubleshooting
**⚠️ COMMON PARAMETER MISTAKES — avoid these:**
- `useAutoprompt` → **deprecated**, remove it entirely
- `includeUrls` / `excludeUrls` → **do not exist**. Use `includeDomains` / `excludeDomains`
- `text`, `summary`, `highlights` at the top level of `/search` → **must be nested** inside `contents` (e.g. `"contents": {"highlights": true}`). On `/contents` they ARE top-level — don't confuse the two.
- `numSentences`, `highlightsPerUrl` → **deprecated** highlights params. Use `highlights: true` instead
- `tokensNum` → **does not exist**. Use `contents.text.maxCharacters` to limit text length
- `livecrawl: "always"` → **deprecated**. Use `contents.maxAgeHours: 0` instead
- `excludeDomains` + `category: "company" | "people"` → **400 error**. Those categories don't support `excludeDomains` or any date filters.
> **`stream: true`** switches `/search` to SSE mode (OpenAI-compatible chat-completion chunks). It's supported — just expect streaming chunks instead of one JSON response.
**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. Pass `outputSchema` on any search type — `auto` works, `deep`/`deep-reasoning` gives higher-quality synthesis
2. Define the fields you need in the schema — Exa returns grounded JSON in `output.content` with citations in `output.grounding`
**Results too slow?**
1. Use `type: "fast"` or `type: "instant"`
2. Reduce `numResults`
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
- Docs: https://exa.ai/docs
- Dashboard: https://dashboard.exa.ai
- API Status: https://status.exa.ai