Skip to content

Latest commit

 

History

549 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Exa JavaScript SDK

npm version

The official JavaScript SDK for Exa, the web search API built for AI.

Documentation  |  Dashboard

Install

npm install exa-js

Quick Start

import Exa from "exa-js";

const exa = new Exa(process.env.EXA_API_KEY);

// Search the web
const result = await exa.search("blog post about artificial intelligence", {
  type: "auto",
  contents: {
    highlights: true,
  },
});

// Get answers with citations
const { answer } = await exa.answer("What is the capital of France?");

Search

Find webpages using natural language queries.

const result = await exa.search("interesting articles about space", {
  numResults: 10,
  includeDomains: ["nasa.gov", "space.com"],
  startPublishedDate: "2024-01-01",
  contents: {
    highlights: true,
  },
});
const resultWithOutput = await exa.search("Who leads OpenAI's safety team?", {
  type: "auto",
  systemPrompt: "Prefer official sources and avoid duplicate results",
  outputSchema: {
    type: "object",
    properties: {
      leader: { type: "string" },
      title: { type: "string" },
      sourceCount: { type: "number" },
    },
    required: ["leader", "title"],
  },
});

console.log(resultWithOutput.output?.content);
for await (const chunk of exa.streamSearch("Who leads OpenAI's safety team?", {
  type: "auto",
})) {
  if (chunk.content) {
    process.stdout.write(chunk.content);
  }
}

Search outputSchema modes:

  • type: "text": return plain text in output.content (optionally guided by description)
  • type: "object": return structured JSON in output.content

systemPrompt and outputSchema are supported on every search type. Search streaming is available via streamSearch(...), which yields OpenAI-style chat completion chunks.

For type: "object", search currently enforces:

  • max nesting depth: 2
  • max total properties: 10

Deep search variants that also support additionalQueries:

  • deep-lite
  • deep
  • deep-reasoning

Contents

Get clean text, highlights, or summaries from any URL.

const { results } = await exa.getContents(["https://docs.exa.ai"], {
  text: true,
  highlights: true,
  summary: true,
});

Answer

const response = await exa.answer("What caused the 2008 financial crisis?");
console.log(response.answer);
for await (const chunk of exa.streamAnswer("Explain quantum computing")) {
  if (chunk.content) {
    process.stdout.write(chunk.content);
  }
}

Web Search tools

Use Exa as a web_search tool in an OpenAI or Anthropic loop. Call webSearch() with no arguments to get Exa's recommended settings for agentic search (type: "auto" and contents: { highlights: true }).

import Exa from "exa-js";
import { OpenAI } from "openai";

const exa = new Exa(process.env.EXA_API_KEY);
const openai = new OpenAI();

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

const completion = await openai.chat.completions.create({
  model: "gpt-5.6",
  messages,
  tools: [exa.openai.webSearch()],
});

const message = completion.choices[0].message;
messages.push(message, ...(await exa.openai.handleToolCalls(message)));
import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic();
const response = await anthropic.messages.create({
  model: "claude-sonnet-4-5",
  max_tokens: 1024,
  messages,
  tools: [exa.anthropic.webSearch()],
});

For the OpenAI Responses API, use exa.openai.responses.webSearch() and the same handleToolCalls helper.

Pass name (and optionally description) to rename the tool — for example to run Exa alongside Anthropic's built-in web_search_20250305 server tool, which reserves the web_search name:

const response = await anthropic.messages.create({
  model: "claude-sonnet-4-5",
  max_tokens: 1024,
  messages,
  tools: [
    { type: "web_search_20250305", name: "web_search", max_uses: 5 },
    exa.anthropic.webSearch({ name: "exa_web_search" }),
  ],
});

Agent API

const run = await exa.agent.runs.create({
  query:
    "Find engineering leaders at AI infrastructure companies that raised a Series A or B in the last 6 months.",
  outputSchema: {
    type: "object",
    properties: {
      people: {
        type: "array",
        maxItems: 10,
        items: {
          type: "object",
          properties: {
            name: { type: "string" },
            contact_email: { type: "string", format: "email" },
            linkedin_url: { type: "string", format: "uri" },
          },
          required: ["name", "linkedin_url"],
        },
      },
    },
    required: ["people"],
  },
  effort: "auto",
  // Optionally enable Exa Connect data providers for the run.
  dataSources: [{ provider: "financial_datasets" }],
});

const completedRun = await exa.agent.runs.pollUntilFinished(run.id);
console.log(completedRun.output?.structured);
// Per-provider tool-call counts and cost for any Exa Connect data sources used.
console.log(
  completedRun.usage?.dataSources,
  completedRun.costDollars?.dataSources
);

For Agent Max, use the beta namespace and pass the beta token explicitly:

import { AGENT_MAX_EFFORT_BETA } from "exa-js";

const maxRun = await exa.beta.agent.runs.create({
  query:
    "Find all companies building browser automation tools in the United States.",
  effort: "max",
  budget: { maxCostDollars: 10 },
  betas: [AGENT_MAX_EFFORT_BETA],
});

Agent Monitors (Beta)

Agent Monitors use the beta namespace and require the AGENT_MONITORS_BETA_HEADER beta identifier (agent-monitors-2026-08-04).

An Agent Monitor keeps a table of entities × fields fresh on a cadence: static fields are answered once per entity over the live web, dynamic fields are tracked from news on every refresh.

import { AGENT_MONITORS_BETA_HEADER } from "exa-js";

const betas = [AGENT_MONITORS_BETA_HEADER];

// Create a monitor. Creation is async: it returns with status "creating"
// and becomes "active" once the first refresh completes.
const monitor = await exa.beta.agent.monitors.create(
  {
    betas,
    cadence: "7d",
    entities: [
      { name: "Acme Corp", domain: "acme.com" },
      { name: "Globex", domain: "globex.com" },
    ],
    fields: [
      { name: "funding", description: "New funding rounds" }, // dynamic by default
      { name: "ceo", description: "The company's current CEO", mode: "static" },
    ],
  },
  { idempotencyKey: "my-monitor-1" } // safe retries: same key returns the same monitor
);

// Page the monitor's current entities and their contents.
for await (const {
  entity,
  contents,
} of exa.beta.agent.monitors.entities.listAll(monitor.id, { betas })) {
  console.log(entity.name, contents);
}

// Follow the content change feed (resume later from the page's nextCursor).
const changes = await exa.beta.agent.monitors.changes.list(monitor.id, {
  betas,
  since: "2026-01-01T00:00:00Z",
});

// One-shot stateless snapshot of a past news window — no monitor created.
const snapshot = await exa.beta.agent.monitors.snapshots.createAndWait({
  betas,
  entities: [{ name: "Acme Corp", domain: "acme.com" }],
  fields: [
    { name: "funding", description: "New funding rounds" }, // dynamic by default
  ],
  startDate: "2026-01-01",
  endDate: "2026-01-08",
});
console.log(snapshot.data);

// Add entities, inspect refresh progress, clean up.
await exa.beta.agent.monitors.entities.add(monitor.id, {
  betas,
  entities: [{ name: "Initech", domain: "initech.com" }],
});
const current = await exa.beta.agent.monitors.get(monitor.id, { betas });
console.log(current.status, current.refresh, current.usage);
await exa.beta.agent.monitors.delete(monitor.id, { betas });

TypeScript

Full TypeScript support with types for all methods.

import Exa from "exa-js";
import type { SearchResponse, RegularSearchOptions } from "exa-js";

Links

Contributing

Pull requests welcome! For major changes, open an issue first.

License

MIT.

About

The Official Exa Javascript SDK

Resources

Stars

129 stars

Watchers

9 watching

Forks

Releases

Packages

Used by

Contributors

Languages