npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

exa-js

v2.22.2

Published

Exa SDK for Node.js and the browser

Downloads

3,264,462

Readme

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 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 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 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.