Exa Search API | SandBase
Run Exa neural web search, page contents, and cited answers through one REST API. No separate SDK — one SandBase key, built for agent and RAG workflows.

Exa is a search engine built for AI, not for humans clicking blue links — it does neural, meaning-based retrieval so an agent can find pages by intent, pull their text, and get a cited answer. That makes it a natural fit for retrieval-augmented generation (RAG), research agents, and grounding LLM output in real sources. Wiring it up yourself still means managing another API client and its auth.
The SandBase Exa Search API puts that behind one convention. It runs Exa’s search, contents, and answer capabilities through plain REST — one SandBase API key, no separate SDK. The endpoint API reference is the source of truth for each parameter and the response envelope; the field names below come from calls I ran (tested on 2026-09-27, UTC) and are observed-only — not documented guarantees — so confirm them against a live response, since payloads change over time. Ready to try it? Get a SandBase API key and browse the Exa endpoints.
Key takeaway
- Three endpoints cover the loop:
search(find pages),contents(pull text), andanswer(a cited answer).- Each call is
POST /v1/api/exa/<path>— pass only that endpoint’s params, no SDK, oneSANDBASE_API_KEY.- The envelope can vary: the reference documents
outputs[0].data, but in my calls the payload often arrived under a top-leveloutputobject — read either shape.- Built for RAG and agents: search by meaning, fetch page text, or get an answer grounded in citations.
A note on this endpoint’s response shape
SandBase endpoints normally return an outputs array whose first item carries the payload under data; the Exa references document this outputs[0].data shape. In my own calls the payload often arrived under a single top-level output object instead (carrying results for search and contents, answer plus citations for answer). Because the envelope can vary, write code that reads either shape: prefer a top-level output, and fall back to outputs[0].data. Confirm the exact envelope for the endpoint you call against its live reference before you build.
Which Exa endpoint do you need?
| Your need | Endpoint | Input |
|---|---|---|
| Find relevant pages by meaning | exa/search | query |
| Pull the text of specific pages | exa/contents | ids |
| Get a direct answer with citations | exa/answer | query |
SandBase vs. the direct Exa API
| Your situation | Use |
|---|---|
| You want one key and one convention across many providers | SandBase Exa API (this guide) |
| You need Exa-specific features or billing directly | Exa’s own API and dashboard |
SandBase provides a uniform Model API layer over Exa’s capabilities; when you need vendor-specific controls or a direct contract, use Exa’s own API.
What you can get from the Exa API
- Neural search —
exa/searchtakes aqueryand returns ranked results, each with aurl,title, andpublishedDate. - Contents —
exa/contentstakesids(page URLs, which Exa uses as identifiers) and returns each page’s text. - Answer —
exa/answertakes aqueryand returns a synthesizedanswerwithcitations.
Check each endpoint’s live API reference for the exact parameters before you build; availability differs by endpoint.
The Exa API page on SandBase — a tagged overview and the endpoint list, each with its path.
Quick start: your first call
SandBase exposes more than one API surface. The catalog may show GET paths under /apis/v1/...; this guide uses the vendor-qualified Model API path on each endpoint’s API reference. Do not swap the HTTP method or URL — follow the reference for the endpoint you choose.
Run a neural search:
import os
import requests
resp = requests.post(
"https://api.sandbase.ai/v1/api/exa/search",
headers={
"Authorization": f"Bearer {os.environ['SANDBASE_API_KEY']}",
"Content-Type": "application/json",
},
json={"query": "latest advances in AI agents"},
)
resp.raise_for_status()
body = resp.json()
if body.get("status") != "completed":
error = body.get("error", {})
raise RuntimeError(error.get("message", "Exa request did not complete"))
# The envelope can vary: prefer a top-level `output`, else fall back to
# the documented `outputs[0].data`. Read the payload defensively.
output = body.get("output")
if output is None and body.get("outputs"):
output = body["outputs"][0].get("data", {})
output = output or {}
for r in output.get("results", [])[:5]:
print(r.get("title"), "-", r.get("url"))
curl -X POST https://api.sandbase.ai/v1/api/exa/search \
-H "Authorization: Bearer $SANDBASE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "latest advances in AI agents"}'
The response carries an id, a status, and the model name. The reference documents the payload under outputs[0].data; in my calls it often arrived under a top-level output object — so read either shape. A failed or timeout run instead carries error, so branch on status first. The block below is illustrative, not a documented guarantee — one capture from my search call (search run id 67700c0e-9398-4f89-954d-42a434ed2706, tested on 2026-09-27, UTC); the reference’s business-payload example is intentionally empty, so treat these field names as observed-only and confirm them against a live response:
{
"id": "67700c0e-9398-4f89-954d-42a434ed2706",
"model": "exa/search",
"status": "completed",
"output": {
"results": [ { "id": "…", "title": "…", "url": "…", "publishedDate": "…" } ],
"searchTime": 0.0,
"resolvedSearchType": "…"
}
}
Response shapes differ by endpoint — inspect one real response and map the exact path per endpoint.
The endpoint API reference is the source of truth for each parameter name and response path.
Chaining search → contents → answer
Because every endpoint shares the same auth and a consistent output envelope, an agent can chain the classic RAG loop without special-casing each surface:
- Search by meaning. Call
exa/searchwith aquery; read theresults, each carrying aurl(also usable as itsid). - Pull the text. Call
exa/contentswithids(page URLs or Exa result IDs) to fetch page text; notetextis an optional request setting, so confirm the reference for what you request and receive. - Or get a direct answer. Call
exa/answerwith aqueryto get a synthesizedanswerpluscitations.
Each step returns the same { id, status, model, output } shape, so your agent branches on status once and reuses the same JSON-reading code across every step.
A slice of the Exa endpoint list.
Common use cases
Exa search API for RAG retrieval
Run exa/search with a query to find relevant pages by meaning, then exa/contents with their URLs to pull text for your context window. Input: a query, then page URLs. Output: ranked results, then page text. Endpoints: search, contents.
Exa answer API for grounded responses
Call exa/answer with a question to get a synthesized answer with citations you can surface to users or verify. Input: a query. Output: an answer plus citations. Endpoint: answer.
Exa contents API for text extraction
Pass a set of page URLs as ids to exa/contents to pull their text in one call. Input: ids (URLs). Output: each page’s text. Endpoint: contents.
Why run this at the API layer
Reading Exa through one uniform API means your code depends on named JSON fields and a single response envelope rather than another vendor SDK. Auth is one key, and because every endpoint returns the same { id, status, model, output } shape, retries, logging, and error handling live in one helper you write once and reuse everywhere. Swap a query, swap a set of URLs, and the code path is identical — which is exactly what makes a RAG or research agent composable. Your time goes to what the retrieved sources mean for your task, not to gluing SDKs together. When you need more than these reads, check the live listing for the endpoint that fits and confirm its parameters before wiring it in.
Limitations and boundaries
- Envelope can vary. The reference documents
outputs[0].data; in my calls the payload often arrived under a top-leveloutputobject. Read either shape (preferoutput, fall back tooutputs[0].data) and branch onstatus. contentstakesids. Theidsparameter accepts page URLs or Exa result IDs (typically from a priorsearch). Page text (text) is an optional request setting — confirm the reference for what you request and what comes back.- Fields are observed-only. The reference guarantees the envelope; treat
results,text,answer, andcitationsas observed and confirm live. - Rate and volume. Treat responses as best-effort reads; as a client-side resilience measure, retry with backoff on transient errors such as HTTP 429.
- Verify endpoints against the live reference. Availability and fields can change; confirm before building on a specific endpoint.
FAQ
Do I need a separate Exa API key or SDK?
No. You authenticate to SandBase with your SANDBASE_API_KEY and call the Exa endpoints over plain REST — no separate SDK on your side.
Why does this surface return output instead of outputs?
The reference documents the payload under outputs[0].data; in my calls it often arrived under a top-level output object with the endpoint’s payload. Because the envelope can vary, read either shape (prefer output, fall back to outputs[0].data) and confirm against the live reference for the endpoint you call.
What’s the difference between search and answer?
search returns ranked pages you fetch and process yourself; answer returns a synthesized answer with citations. Use search (+ contents) for RAG, answer when you want a grounded response directly.
Can I use this for RAG?
Yes. The search → contents pair is a natural RAG retrieval step: find pages by meaning, pull their text, and feed it to your model. answer covers the case where you want the grounded response directly.
Start with a search
Create a SandBase API key, call search with a query, and inspect the output before you chain into contents or answer. When you are ready: