Skip to main content
This guide assumes you already have a working Search API request. It covers how to improve that request following Exa’s recommended best practices.

Start with the smallest useful request

The best baseline is a natural-language query with highlights: true. Exa sizes each result’s excerpts to its relevance, so there is no character budget to tune:
This gives you ranked pages and token-efficient context per page, relevant to the query. Add additional parameters only as needed: Standard search retrieves and ranks pages for a query. Deep Search runs a research process that can search iteratively, inspect what it found, refine the search, and synthesize a grounded result. Deep modes are recommended by default when using outputSchema. Read the Deep Search guide for full instructions and examples.

Improve retrieval quality

When results need improvement, change one part of the request at a time.
1

Clarify the query

Describe the pages you want, not a bag of keywords. Include the subject and any source type, time period, or other detail that changes what a relevant result looks like.
2

Read the response in layers

Look at the titles, URLs, publication dates, and highlights before changing the request.
The title and URL show the kind of source Exa retrieved, publishedDate shows its recency, and the highlight shows the evidence that matched the query. Refine the query to retrieve different pages, add date filters to narrow the time period, or fetch full text when you need more context from a useful result.
3

Add only hard constraints

Use includeDomains, excludeDomains, and publication-date filters only when a result that violates the constraint cannot be used. Put retrieval preferences in the query and, when synthesizing, put response instructions in systemPrompt.
4

Change the search mode last

Use a faster mode for a latency requirement or a deep mode when the retrieval process itself needs iteration and reasoning. A different mode cannot repair an underspecified query.

Pass the objective from agents

objective is the broader goal behind a search: the task the search is one step of. The query says what to retrieve; the objective says what that task needs from the results, such as which documents should rank first, which to exclude, and which facts or figures to pull. It is most useful when a model picks the query as part of a larger task.
When you expose Search to a model as a tool, add objective as a required string parameter with a description that tells the model what the search turn is trying to achieve. We recommend the wording below verbatim, which is what the Exa MCP server uses:
The exa-py and exa-js web_search tool-calling helpers do this automatically.If you define the tool manually, use the schema above.
Keep a small set of representative queries while tuning. Compare result relevance and downstream task success across the set instead of optimizing for one example. Record requestId, searchTime, and costDollars so regressions are reproducible.

Budget latency and context

Each control spends a different resource: For a real-time path where cached content is acceptable, combine the lowest-latency mode with highlights and cache-only content:
Do not use this recipe when page freshness is part of correctness. Start from auto and default freshness unless the product has a measured latency target. To let Exa allocate one context budget across the whole result set — more from strong sources, less from redundant ones — see the Dynamic Highlights research preview.

Tips for common use cases

Attribute requests to your end users

When your application calls Exa on behalf of its own users, set userIdentifier to an identifier for the user each request is for. Exa records it with the request so usage, and any abuse, can be attributed to one end user instead of your whole team.
  • Send an opaque value your platform generates, such as a UUID or a hash of your internal user ID. Never send an email address, phone number, username, or name.
  • Use 1 to 64 characters: letters, digits, _, and -. Any other string returns a 400 error; null is the same as omitting the field.
  • It is telemetry only. It does not change results or caching, is not returned in the response, and does not authenticate the user.
  • Exa keeps it even when your team has Zero Data Retention enabled, which is why it must not contain personal information.
/contents, /answer, and /agent/runs take the same top-level field. In the SDKs it is user_identifier (Python) or userIdentifier (JavaScript) on search, get_contents / getContents, answer, stream_answer / streamAnswer, and agent.runs.create. An Agent run attributes the searches it makes to the same user, and a follow-up run (previousRunId) that sends no userIdentifier keeps the one from the run it continues.

Common mistakes

Requests copied from older examples often include parameters that are removed, deprecated, or belong somewhere else in the request. The API reference is the source of truth for the current schema. A few behaviors that are easy to miss:
  • The Python SDK uses snake_case for every key, including nested ones: contents={"highlights": True, "max_age_hours": 24}.
  • stream: true returns server-sent events only when outputSchema is set. Without a schema, the response is the normal JSON body.
  • highlights, text, and summary can be combined in one request, but each view is billed separately. Request one view unless the task needs both.

Warnings and notices

Pass the notices in a /search response to your model with the results, and log the warnings. A notice describes how the results were produced and is written for the model that reads them. A warning describes the request, such as a field that has no effect, and is written for the developer who maintains the code that builds the request. Never pass a warning to a model. Neither array changes the status code, and each array is omitted when it is empty. With stream: true, warnings and notices arrive on the event with type: "results". Each warning and notice has a machine-readable code, a path that points to the request field as a JSON Pointer, and a message that is complete on its own. Codes are an open set. Handle a code you do not recognize by its message instead of failing. To keep a notice from your model, drop it by code, or forward only the codes your application handles. The API reference describes every field, including the optional details object on a notice.

Warning codes

Notice codes

When to use another endpoint

Use a different Exa endpoint when the task changes shape:

Next steps

Search API reference

Every request parameter and response field.

Search quickstart

Core request shapes, filters, output, and freshness.

Contents API

Extract highlights or full text from pages you already know.

Exa Agent

Long-running research, list building, and enrichment.