Profile
Back to NewsBack
GitHub Trending 6 min
Reader Mode
exa-labs/exa-js: The Official Exa Javascript SDK

exa-labs/exa-js: The Official Exa Javascript SDK

16 hours ago

Exa JavaScript SDK

npm version</a>

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

Raw body fields

Pass requestOptions.body as the third argument for per-call search body overrides.

const result = await exa.search("AI research", undefined, { body: { futureField: true } });

Contents

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

const { results } = await exa.getContents(["https://exa.ai/docs"], {
  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 and Contents 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" }),
  ],
});

getContents() is available in the same namespaces and lets the model read pages it already has URLs for. It takes a list of URLs and accepts every exa.getContents option:

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

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 Ultra, set effort: "ultra":

const ultraRun = await exa.agent.runs.create({
  query:
    "Find all companies building browser automation tools in the United States.",
  effort: "ultra",
  budget: { maxCostDollars: 10 },
});

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 backtest of a past news window — no monitor persists. const backtest = await exa.beta.agent.monitors.backtests.createAndWait({ betas, entities: [{ name: "Acme Corp", domain: "acme.com" }], fields: [{ name: "funding", description: "New funding rounds" }], startTime: "2026-01-01T00:00:00Z", endTime: "2026-01-08T00:00:00Z", }); console.log(backtest.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.

Chat with me