Skip to content

Exa Integration

Official Exa documentation

KeyPool supports the current official exa-js and exa-py clients through one team-authenticated service URL.

Setup

npm install exa-js
pip install exa-py

=== "TypeScript"

```typescript
import Exa from "exa-js";

const exa = new Exa(
  process.env.KEYPOOL_TOKEN,
  `${process.env.KEYPOOL_BASE_URL}/v1/exa`,
);
```

=== "Python"

```python
import os
from exa_py import Exa

exa = Exa(
    api_key=os.environ["KEYPOOL_TOKEN"],
    base_url=f'{os.environ["KEYPOOL_BASE_URL"]}/v1/exa',
)
```

Use the KeyPool Team Token as the SDK API key. Do not send a personal Exa key through this endpoint.

Supported SDK surface

Capability Current SDK entry point Notes
Search search, streamSearch / stream_search Regular and deep search modes, inline contents, structured deep output
Contents getContents / get_contents Text, highlights, summaries, context, and subpages
Answer answer, streamAnswer / stream_answer Answer text, citations, and streaming
Agent agent.runs Create, stream, get, list, cancel, delete, events, polling, and continuation
Exa Connect Agent dataSources / data_sources Premium data providers attached to an Agent run
Search Monitors monitors, monitors.runs Monitor lifecycle, manual trigger, and run history
Research research Legacy compatibility API

Search types currently include keyword, neural, hybrid, auto, fast, instant, deep-lite, deep, and deep-reasoning. Use the option names exposed by your installed SDK version.

Search with contents

Prefer search with contents; the older combined convenience method is deprecated.

=== "TypeScript"

```typescript
const result = await exa.search("latest agent evaluation research", {
  type: "auto",
  numResults: 3,
  contents: {
    text: { maxCharacters: 2_000 },
    highlights: { numSentences: 2 },
  },
});

for (const item of result.results) {
  console.log(item.title, item.url, item.text);
}
```

=== "Python"

```python
result = exa.search(
    "latest agent evaluation research",
    type="auto",
    num_results=3,
    contents={
        "text": {"max_characters": 2_000},
        "highlights": {"num_sentences": 2},
    },
)

for item in result.results:
    print(item.title, item.url, item.text)
```

For streamed search, iterate await exa.streamSearch(...) in TypeScript or exa.stream_search(...) in Python.

Contents and Answer

=== "TypeScript"

```typescript
const contents = await exa.getContents(["https://example.com"], {
  text: { maxCharacters: 2_000 },
});

const answer = await exa.answer("What changed in the latest Exa Agent API?", {
  text: true,
});
console.log(answer.answer, answer.citations);
```

=== "Python"

```python
contents = exa.get_contents(
    ["https://example.com"],
    text={"max_characters": 2_000},
)

answer = exa.answer(
    "What changed in the latest Exa Agent API?",
    text=True,
)
print(answer.answer, answer.citations)
```

Use streamAnswer or stream_answer when the answer should arrive incrementally.

Exa Agent

Use the stable agent namespace. beta.agent remains a deprecated alias.

=== "TypeScript"

```typescript
const run = await exa.agent.runs.create({
  query: "Find three companies building AI observability tools.",
  effort: "low",
  outputSchema: {
    type: "object",
    properties: {
      companies: {
        type: "array",
        maxItems: 3,
        items: {
          type: "object",
          properties: {
            name: { type: "string" },
            website: { type: "string", format: "uri" },
          },
          required: ["name", "website"],
        },
      },
    },
    required: ["companies"],
  },
  stream: false,
});

const completed = await exa.agent.runs.pollUntilFinished(run.id);
console.log(completed.output?.structured);
```

=== "Python"

```python
run = exa.agent.runs.create(
    query="Find three companies building AI observability tools.",
    effort="low",
    output_schema={
        "type": "object",
        "properties": {
            "companies": {
                "type": "array",
                "maxItems": 3,
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "website": {"type": "string", "format": "uri"},
                    },
                    "required": ["name", "website"],
                },
            }
        },
        "required": ["companies"],
    },
)

completed = exa.agent.runs.poll_until_finished(run.id)
print(completed.output.structured)
```

Agent creation can also stream server-sent events. Set stream: true or stream=True and iterate the returned events. Retain the ID from agent_run.created if you need later get, cancel, delete, or event-history calls.

max is Exa's highest-effort public beta tier and prioritizes completeness over latency and cost. The auto and max efforts are metered: set budget: { maxCostDollars: 1 } in TypeScript or budget={"maxCostDollars": 1} in Python when you need an explicit per-run ceiling. Exa accepts limits from $1 to $100; without one, the upstream default cap is $5 for auto and $20 for max.

Continue a run

Each continuation creates a new run ID:

const followUp = await exa.agent.runs.create({
  query: "Limit the list to companies hiring in Toronto.",
  previousRunId: completed.id,
  effort: "low",
  stream: false,
});

Always keep both the previous and newly returned IDs.

Exa Connect

Connect is part of Agent creation, not a separate SDK namespace. Provider identifiers use the API form shown in Exa's Connect documentation.

=== "TypeScript"

```typescript
const run = await exa.agent.runs.create({
  query: "Return one public web-traffic fact about exa.ai.",
  dataSources: [{ provider: "similarweb" }],
  effort: "low",
  stream: false,
});
```

=== "Python"

```python
run = exa.agent.runs.create(
    query="Return one public web-traffic fact about exa.ai.",
    data_sources=[{"provider": "similarweb"}],
    effort="low",
)
```

Connect providers may require upstream entitlement and add provider-specific usage charges. Bound list sizes and structured schemas before running large jobs.

Search Monitors

Search Monitors require an HTTPS webhook URL when created:

const monitor = await exa.monitors.create({
  name: "Exa release monitor",
  search: { query: "Exa API release notes", numResults: 5 },
  trigger: { type: "interval", period: "1d" },
  webhook: { url: process.env.MONITOR_WEBHOOK_URL! },
});

await exa.monitors.trigger(monitor.id);
const runs = await exa.monitors.runs.list(monitor.id, { limit: 10 });

Use get, update, and delete with the returned monitor ID.

Unavailable: Websets

KeyPool does not expose the Websets API or its items, searches, enrichments, events, imports, monitors, and webhooks. Exa requires a paid plan for this product, and Websets are not accessible with free Exa credits.

The official SDKs may still display a websets namespace, but calls through the KeyPool Exa service are unsupported. Use Search, Agent, or Agent with Exa Connect instead.

Resource IDs and list calls

For reliable multi-step workflows:

  1. Create the resource through KeyPool.
  2. Persist the returned ID.
  3. Perform get, update, cancel, event, and delete calls through the same KeyPool base URL and Team Token.

List endpoints are useful for discovery but should not be treated as a complete KeyPool-wide inventory. Resource IDs returned by the same workflow are the dependable way to address stateful objects.

Legacy and deprecated APIs

  • Prefer search(..., contents=...) over searchAndContents / search_and_contents.
  • findSimilar, findSimilarAndContents, and Python equivalents remain available for compatibility but are deprecated.
  • Prefer agent over beta.agent.
  • research remains supported for existing integrations; start new long-running structured research with Agent.

Errors

  • 401: missing or invalid KeyPool Team Token.
  • 403: the token cannot access Exa, or an upstream capability requires additional entitlement.
  • 404 CAPABILITY_NOT_SUPPORTED: the requested path belongs to the paid-plan Websets family and is not exposed by KeyPool.
  • 429: KeyPool quota or upstream rate limit reached.
  • 503 NO_AVAILABLE_KEYS: no eligible Exa credential is available for a new request.
  • 503 AFFINITY_KEY_UNAVAILABLE: the upstream credential associated with a previously returned Exa resource is unavailable. Retry later rather than recreating the request against a different resource ID.

See API Reference → Exa for the SDK-backed HTTP surface.