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)

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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

exa_py-2.18.0.tar.gz (72.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

exa_py-2.18.0-py3-none-any.whl (102.5 kB view details)

Uploaded Python 3

File details

Details for the file exa_py-2.18.0.tar.gz.

File metadata

  • Download URL: exa_py-2.18.0.tar.gz
  • Upload date:
  • Size: 72.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for exa_py-2.18.0.tar.gz
Algorithm Hash digest
SHA256 ea6b444cdb696477a771bc77a22b790ab31d5bc8579f49045f554ed347cbd272
MD5 7477ab5e7e28f103a5549f6740f3623f
BLAKE2b-256 6db84e20b078cd54847f18c93a9a5c14f3baef81655a17d9ae7ade71644a7025

See more details on using hashes here.

File details

Details for the file exa_py-2.18.0-py3-none-any.whl.

File metadata

  • Download URL: exa_py-2.18.0-py3-none-any.whl
  • Upload date:
  • Size: 102.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for exa_py-2.18.0-py3-none-any.whl
Algorithm Hash digest
SHA256 eecc5e9affff2b5d054e3d52e68ec74dfb06a8ce1375638af5cf111b2baeb617
MD5 67959c1d4d3ae4756bbb35101390fe1d
BLAKE2b-256 583a6587695cb3e91e9d90b78eae62287d010f6130e99677bd1e7bd9a45721ed

See more details on using hashes here.

Release history Release notifications | RSS feed

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page