Skip to main content

Exa Python SDK

PyPI version

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

Documentation | Dashboard

Install

pip install exa-py

Requires Python 3.9+

Quick Start

from exa_py import Exa

exa = Exa(api_key="your-api-key")

# Search the web
results = exa.search(
    "blog post about artificial intelligence",
    type="auto",
    contents={"highlights": True}
)

# Ask a question
response = exa.answer("What is the capital of France?")

Search

results = exa.search(
    "machine learning startups",
    contents={"highlights": True}
)
results = exa.search(
    "climate tech news",
    num_results=20,
    start_published_date="2024-01-01",
    include_domains=["techcrunch.com", "wired.com"],
    contents={"highlights": True}
)
results = exa.search(
    "What are the latest battery breakthroughs?",
    type="auto",
    system_prompt="Prefer official sources and avoid duplicate results",
    output_schema={
        "type": "object",
        "properties": {
            "summary": {"type": "string"},
            "key_companies": {"type": "array", "items": {"type": "string"}},
        },
        "required": ["summary", "key_companies"],
    },
)
print(results.output.content if results.output else None)
for chunk in exa.stream_search(
    "What are the latest battery breakthroughs?",
    type="auto",
):
    if chunk.content:
        print(chunk.content, end="", flush=True)

Search output_schema modes:

  • {"type": "text", "description": "..."}: return plain text in output.content
  • {"type": "object", ...}: return structured JSON in output.content

system_prompt and output_schema are supported on every search type. Search streaming is available via stream_search(...), 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 additional_queries:

  • deep-lite
  • deep
  • deep-reasoning

Contents

results = exa.get_contents(
    ["https://docs.exa.ai"],
    text=True
)
results = exa.get_contents(
    ["https://arxiv.org/abs/2303.08774"],
    highlights=True
)

Answer

response = exa.answer("What caused the 2008 financial crisis?")
print(response.answer)
for chunk in exa.stream_answer("Explain quantum computing"):
    print(chunk, end="", flush=True)

Web Search tools

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

from exa_py import Exa
from openai import OpenAI

exa = Exa()
openai_client = OpenAI()

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

completion = openai_client.chat.completions.create(
    model="gpt-5.6",
    messages=messages,
    tools=[exa.openai.web_search()],
)

message = completion.choices[0].message
messages.append(message)
messages += exa.openai.handle_tool_calls(message)
import anthropic

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    messages=messages,
    tools=[exa.anthropic.web_search()],
)

Pass name (and optionally description) to rename the tool. Anthropic requires tool names to be unique, so a custom name lets the Exa tool run alongside Anthropic's built-in web_search_20250305 tool:

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

For the OpenAI Responses API, use exa.openai.responses.web_search() and the same handle_tool_calls helper. The handlers answer every tool call: calls naming a tool they can't resolve get an Error: unknown tool "<name>" output instead of being dropped, so follow-up requests stay valid. If you run other tools alongside Exa's, replace those error outputs with your own results before the next request.

Agent API

The Agent API is available without a beta header.

run = exa.agent.runs.create(
    query="Find engineering leaders at AI infrastructure companies that raised a Series A or B in the last 6 months.",
    output_schema={
        "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",
)

run = exa.agent.runs.poll_until_finished(run.id)
print(run.output.structured if run.output else None)

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

from exa_py import Exa
from exa_py.agent import AGENT_MAX_EFFORT_BETA

exa = Exa()
run = 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.

from exa_py.agent import AGENT_MONITORS_BETA_HEADER

betas = [AGENT_MONITORS_BETA_HEADER]

# Create a monitor. Creation is async: it returns with status "creating"
# and becomes "active" once the first refresh completes.
monitor = exa.beta.agent.monitors.create(
    betas=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"},
    ],
    idempotency_key="my-monitor-1",  # safe retries: same key returns the same monitor
)

# Page the monitor's current entities and their contents.
for view in exa.beta.agent.monitors.entities.list_all(monitor.id, betas=betas):
    print(view.entity.name, view.contents)

# Follow the content change feed (resume later from the page's next_cursor).
changes = exa.beta.agent.monitors.changes.list(
    monitor.id,
    betas=betas,
    since="2026-01-01T00:00:00Z",
)

# One-shot stateless snapshot of a past news window — no monitor created.
snapshot = exa.beta.agent.monitors.snapshots.create_and_wait(
    betas=betas,
    entities=[{"name": "Acme Corp", "domain": "acme.com"}],
    fields=[{"name": "funding", "description": "New funding rounds"}],  # dynamic by default
    start_date="2026-01-01",
    end_date="2026-01-08",
)
print(snapshot.data)

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

Async

from exa_py import AsyncExa

exa = AsyncExa(api_key="your-api-key")

results = await exa.search("async search example", contents={"highlights": True})

More

See the full documentation for all features including websets, filters, and advanced options.

Release files for exa-py 2.19.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for exa-py 2.19.0
File Size Uploaded
exa_py-2.19.0.tar.gz 79.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for exa-py 2.19.0
File Interpreter ABI Platform
exa_py-2.19.0-py3-none-any.whl Python 3 none any Details

Total release size: 187.9 kB

Release files / exa_py-2.19.0.tar.gz

Download URL exa_py-2.19.0.tar.gz
Size 79.1 kB
Tags Source
SHA-256 checksum
How to use checksums
d8f684d04d086a343a1f067b59715819030201ce2508470868c21186ee6541d7
BLAKE2b-256 checksum
How to use checksums
f02694897ec74c7edeb9424a85c592263fa951b0ba313ea84bf657ced9c07e14
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release files / exa_py-2.19.0-py3-none-any.whl

Download URL exa_py-2.19.0-py3-none-any.whl
Size 108.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ee061bad0fe65597d23a4964d2263702f3faec3934f8631b0dc030063cc00dd5
BLAKE2b-256 checksum
How to use checksums
68c056e337880bf2430b4092cb3c2d86094776b04bd311b60b121d6ae0fe9156
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release history Release notifications | RSS feed

2.22.0

2 release files

2.21.0

2 release files

This release

2.19.0 This release

2 release files

2.18.1

2 release files

2.18.0

2 release files

2.16.2

2 release files

2.15.0

2 release files

2.14.0

2 release files

2.13.0

2 release files

2.12.1

2 release files

2.12.0

2 release files

2.10.2

2 release files

2.10.1

2 release files

2.10.0

2 release files

2.9.0

2 release files

2.8.1

2 release files

2.8.0

2 release files

2.7.1

2 release files

2.7.0

2 release files

2.6.1

2 release files

2.6.0

2 release files

2.5.0

2 release files

2.4.0

2 release files

2.3.0

2 release files

2.2.0

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.16.2

2 release files

1.15.6

2 release files

1.15.4

2 release files

1.15.3

2 release files

1.15.2

2 release files

1.15.1

2 release files

1.15.0

2 release files

1.14.11

1 release file

1.14.9

2 release files

1.14.8

2 release files

1.14.7

2 release files

1.13.2

2 release files

1.13.1

2 release files

1.13.0

2 release files

1.12.5

2 release files

1.12.4

2 release files

1.12.3

2 release files

1.12.1

2 release files

1.12.0

2 release files

1.10.0

2 release files

1.9.1

2 release files

1.9.0

2 release files

1.8.9

2 release files

1.8.8

2 release files

1.8.7

2 release files

1.8.6

2 release files

1.8.5

2 release files

1.8.4

2 release files

1.8.3

2 release files

1.7.3

2 release files

1.7.2

2 release files

1.7.1

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.2

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.8

2 release files

1.1.7

2 release files

1.1.6

2 release files

1.1.5

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.18

2 release files

1.0.17

2 release files

1.0.16

2 release files

1.0.15

2 release files

1.0.14

2 release files

1.0.12

2 release files

1.0.11

2 release files

1.0.10

1 release file

1.0.9

2 release files

1.0.8

2 release files

1.0.7

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page