Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,8 @@ const result = await exa.search("interesting articles about space", {
```

```ts
const deepResult = await exa.search("Who leads OpenAI's safety team?", {
type: "deep",
systemPrompt: "Prefer official sources and avoid duplicate results",
const structuredResult = await exa.search("Who leads OpenAI's safety team?", {
type: "auto",
outputSchema: {
type: "object",
properties: {
Expand All @@ -74,19 +73,23 @@ const deepResult = await exa.search("Who leads OpenAI's safety team?", {
}
});

console.log(deepResult.output?.content);
console.log(structuredResult.output?.content);
```

Deep `outputSchema` modes:
`/search` `outputSchema` modes:
- `type: "text"`: return plain text in `output.content` (optionally guided by `description`)
- `type: "object"`: return structured JSON in `output.content`

Deep search also supports `systemPrompt` to guide both the search process and the final returned result, for example by preferring certain sources, emphasizing novel findings, avoiding duplicates, or constraining output style.
All search types support `outputSchema`, including `auto`, `fast`, `instant`, `keyword`, `neural`, `deep`, and `deep-reasoning`.

For `type: "object"`, deep search currently enforces:
For `type: "object"`, `/search` currently enforces:
- max nesting depth: `2`
- max total properties: `10`

Deep search variants add:
- `systemPrompt` to guide both search planning and the final returned synthesis
- `additionalQueries` to seed specific search expansions

Deep search variants:
- `deep`: light mode
- `deep-reasoning`: base reasoning mode
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "exa-js",
"version": "2.8.0",
"version": "2.9.0",
"description": "Exa SDK for Node.js and the browser",
"publishConfig": {
"access": "public"
Expand Down
54 changes: 31 additions & 23 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,23 @@ type BaseRegularSearchOptions = BaseSearchOptions & {
*/
moderation?: boolean;
useAutoprompt?: boolean;
/**
* Output schema for synthesized `/search` responses.
* Supported for all search types.
* - `type: "text"` for plain text output (optionally guided by `description`)
* - `type: "object"` for structured JSON output
*
* Note: For object schemas, the API enforces max depth 2 and max 10 total properties.
*/
outputSchema?: SearchOutputSchema;
};

export type DeepSearchType = "deep" | "deep-reasoning";

/**
* Deep search output schema mode for plain text responses.
* Search output schema mode for plain text responses.
*/
export type DeepTextOutputSchema = {
export type SearchTextOutputSchema = {
type: "text";
/**
* Optional formatting guidance for text output.
Expand All @@ -107,9 +116,9 @@ export type DeepTextOutputSchema = {
};

/**
* Deep search output schema mode for structured JSON object responses.
* Search output schema mode for structured JSON object responses.
*/
export type DeepObjectOutputSchema = {
export type SearchObjectOutputSchema = {
type: "object";
/**
* JSON-schema-style properties for the result object.
Expand All @@ -122,13 +131,16 @@ export type DeepObjectOutputSchema = {
};

/**
* Deep search output schema.
* Search output schema.
* - `type: "text"` returns plain text in `output.content` (with optional description guidance).
* - `type: "object"` returns structured JSON in `output.content`.
*
* Note: For object schemas, the API enforces a maximum nesting depth of 2 and a maximum of 10 total properties.
*/
export type DeepOutputSchema = DeepTextOutputSchema | DeepObjectOutputSchema;
export type SearchOutputSchema = SearchTextOutputSchema | SearchObjectOutputSchema;
export type DeepTextOutputSchema = SearchTextOutputSchema;
export type DeepObjectOutputSchema = SearchObjectOutputSchema;
export type DeepOutputSchema = SearchOutputSchema;

/**
* Contents options for deep search.
Expand All @@ -155,14 +167,6 @@ type DeepSearchOptions = Omit<BaseRegularSearchOptions, "contents"> & {
* Use this to prefer certain sources, emphasize novelty, avoid duplicates, or constrain output style.
*/
systemPrompt?: string;
/**
* Output schema for deep search responses.
* - `type: "text"` for plain text output (optionally guided by `description`)
* - `type: "object"` for structured JSON output
*
* Note: For object schemas, the API enforces max depth 2 and max 10 total properties.
*/
outputSchema?: DeepOutputSchema;
/**
* Options for retrieving page contents.
*/
Expand Down Expand Up @@ -499,30 +503,34 @@ export type SearchResult<T extends ContentsOptions> = {
entities?: Entity[];
} & ContentsResultComponent<T>;

export type DeepSearchOutputGroundingCitation = {
export type SearchOutputGroundingCitation = {
url: string;
title: string;
};

export type DeepSearchOutputGroundingConfidence = "low" | "medium" | "high";
export type SearchOutputGroundingConfidence = "low" | "medium" | "high";

export type DeepSearchOutputGrounding = {
export type SearchOutputGrounding = {
field: string;
citations: DeepSearchOutputGroundingCitation[];
confidence: DeepSearchOutputGroundingConfidence;
citations: SearchOutputGroundingCitation[];
confidence: SearchOutputGroundingConfidence;
};

export type DeepSearchOutput = {
export type SearchOutput = {
content: string | Record<string, unknown>;
grounding: DeepSearchOutputGrounding[];
grounding: SearchOutputGrounding[];
};
export type DeepSearchOutputGroundingCitation = SearchOutputGroundingCitation;
export type DeepSearchOutputGroundingConfidence = SearchOutputGroundingConfidence;
export type DeepSearchOutputGrounding = SearchOutputGrounding;
export type DeepSearchOutput = SearchOutput;

/**
* Represents a search response object.
* @typedef {Object} SearchResponse
* @property {Result[]} results - The list of search results.
* @property {string} [context] - Deprecated. The context for the search.
* @property {DeepSearchOutput} [output] - Deep search synthesized output object with `content` and `grounding`.
* @property {SearchOutput} [output] - Synthesized `/search` output object with `content` and `grounding`.
* @property {string} [autoDate] - The autoprompt date, if applicable.
* @property {string} requestId - The request ID for the search.
* @property {CostDollars} [costDollars] - The cost breakdown for this request.
Expand All @@ -533,7 +541,7 @@ export type SearchResponse<T extends ContentsOptions> = {
results: SearchResult<T>[];
/** @deprecated Use `highlights` or `text` on individual results instead. Will be removed in a future version. */
context?: string;
output?: DeepSearchOutput;
output?: SearchOutput;
autoDate?: string;
requestId: string;
statuses?: Array<Status>;
Expand Down
71 changes: 71 additions & 0 deletions test/unit/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,77 @@ describe("Search API", () => {
});
});

it("should pass outputSchema for auto search", async () => {
const mockResponse = {
results: [
{
title: "Structured Result",
url: "https://example.com/structured-auto",
id: "structured-auto-id",
text: "Structured result text",
},
],
output: {
content: { company: "Exa", founded: 2021 },
grounding: [
{
field: "company",
citations: [
{ url: "https://example.com/structured-auto", title: "Structured Result" },
],
confidence: "high",
},
],
},
requestId: "req-structured-auto-123",
};

const requestSpy = vi.spyOn(exa, "request").mockResolvedValueOnce(mockResponse);

const result = await exa.search("exa company profile", {
type: "auto",
outputSchema: {
type: "object",
properties: {
company: { type: "string" },
founded: { type: "number" },
},
required: ["company", "founded"],
},
numResults: 5,
});

expect(requestSpy).toHaveBeenCalledWith("/search", "POST", {
query: "exa company profile",
type: "auto",
outputSchema: {
type: "object",
properties: {
company: { type: "string" },
founded: { type: "number" },
},
required: ["company", "founded"],
},
numResults: 5,
contents: {
text: {
maxCharacters: 10000,
},
},
});
expect(result).toEqual(mockResponse);
expect(result.output).toEqual({
content: { company: "Exa", founded: 2021 },
grounding: [
{
field: "company",
citations: [{ url: "https://example.com/structured-auto", title: "Structured Result" }],
confidence: "high",
},
],
});
});

it("should pass systemPrompt for deep search", async () => {
const mockResponse = {
results: [
Expand Down
Loading