Exa Python SDK

repository·master·Indexed 42 days ago

https://github.com/exa-labs/exa-py

Python SDK for Exa API (version 2.22.0), a web search API designed for AI agents and LLMs. It provides tools for semantic search, structured data extraction, and automated entity monitoring. The SDK includes specialized integrations for OpenAI and Anthropic tool-calling, an Agent API for complex multi-step queries, and a Metaphor client for neural web search and similarity discovery.

Tokens
36.8K
Snippets
110
Records
145
Agent score
53%

What's inside Exa

  1. Understand search result response types

    master

    The SDK returns different result objects depending on which content options you requested. All results share base fields like url, id, title, score, published_date, author, image, favicon, subpages, extras, entities, and crawl_date.

    Common response types:

    • Result: The standard result containing optional text, summary, highlights, and highlight_scores.
    • ResultWithText: Returned when text is explicitly requested; text is guaranteed to be a str.
    • ResultWithSummary: Returned when summary is explicitly requested; summary is guaranteed to be a str.
    • ResultWithTextAndSummary: Returned when both are requested.

    Cost Tracking: Responses also include a costDollars object containing total, search (broken down into neural and keyword), and contents (broken down into text and summary).

  2. How to retrieve contents directly from a SearchResponse

    master

    A SearchResponse object returned by search() or find_similar() provides a convenience method get_contents() to fetch the text extracts for all results in that response. This avoids manually extracting IDs and calling the client again.

    results = client.search(query='python programming')
    # Directly fetch contents from the response object
    contents = results.get_contents()
    
    for doc in contents.contents:
        print(doc.extract)
  3. Migrate from `find_similar()` to `search()`

    master

    The find_similar() method is deprecated. To find pages similar to a specific URL, use the search() method with a formatted query string.

    Migration Pattern: Instead of exa.find_similar(url), use exa.search(f"pages similar to {url}").

    url = "https://www.miniclip.com/games/en/"
    # Recommended way:
    similar_results = exa.search(
        f"pages similar to {url}",
        num_results=2,
    )
  4. Understand Search Monitor triggers and webhooks

    master

    Search Monitors can be automated using triggers and notified via webhooks.

    Triggers (SearchMonitorTrigger)

    Monitors can be set to run on an interval.

    • type: Must be Literal['interval'].
    • period (str): The frequency of the run (e.g., a cron-like string or duration).

    Webhooks (SearchMonitorWebhook)

    To receive notifications when monitor events occur:

    • url (str): The endpoint to receive POST requests.
    • events (List[SearchMonitorWebhookEvent]): A list of specific events to trigger the webhook.
  5. Use Agent Monitors (Beta)

    master

    Agent Monitors track entities and fields over time. They keep data fresh by refreshing static fields once and tracking dynamic fields (like news) on a cadence.

    Key Concepts:

    • Static Fields: Answered once per entity.
    • Dynamic Fields: Tracked from news on every refresh.
    • Cadence: The frequency of updates (e.g., "7d").
    • Beta Requirement: Requires the AGENT_MONITORS_BETA_HEADER identifier in the betas list.

    Core Operations:

    • exa.beta.agent.monitors.create(...): Creates a monitor (returns status "creating" initially).
    • exa.beta.agent.monitors.entities.list_all(monitor_id, betas): Pages through current entities and their contents.
    • exa.beta.agent.monitors.changes.list(monitor_id, betas, since): Follows the content change feed.
    • exa.beta.agent.monitors.snapshots.create_and_wait(...): Performs a one-shot stateless snapshot of a past time window.
    • exa.beta.agent.monitors.entities.add(...): Adds new entities to an existing monitor.
    • exa.beta.agent.monitors.get(...): Inspects refresh progress, status, and usage.
    from exa_py.agent import AGENT_MONITORS_BETA_HEADER
    
    betas = [AGENT_MONITORS_BETA_HEADER]
    
    # Create a monitor
    monitor = exa.beta.agent.monitors.create(
        betas=betas,
        cadence="7d",
        entities=[
            {"name": "Acme Corp", "domain": "acme.com"},
        ],
        fields=[
            {"name": "funding", "description": "New funding rounds"},
            {"name": "ceo", "description": "The company's current CEO", "mode": "static"},
        ],
        idempotency_key="my-monitor-1",
    )
    
    # List entities
    for view in exa.beta.agent.monitors.entities.list_all(monitor.id, betas=betas):
        print(view.entity.name, view.contents)
  6. Use Exa as a Tool for OpenAI and Anthropic Agents

    master

    Exa provides helper methods to integrate web search and content retrieval directly into LLM tool-calling loops.

    OpenAI Integration

    • Use exa.openai.web_search() to provide a search tool.
    • Use exa.openai.get_contents() to allow the model to read specific URLs.
    • Use exa.openai.handle_tool_calls(message) to process tool calls returned by the model.
    • For the OpenAI Responses API, use exa.openai.responses.web_search().

    Anthropic Integration

    • Use exa.anthropic.web_search() to provide a search tool.
    • You can pass a name to web_search() to avoid collisions with Anthropic's built-in tools (e.g., name="exa_web_search").
    # OpenAI Example
    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)
    
    # Anthropic Example
    import anthropic
    
    client = anthropic.Anthropic()
    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        messages=messages,
        tools=[exa.anthropic.web_search(name="exa_web_search")],
    )
  7. Build a News Summarizer with Metaphor and LLMs

    master

    A complete pattern for building a real-time news summarizer:

    1. Query Generation: Use an LLM to turn a user question into a optimized search query.
    2. Neural Search: Use metaphor.search() with start_published_date to find recent, relevant links.
    3. Content Extraction: Use search_response.get_contents() to get cleaned text from those links.
    4. Summarization: Pass the content_item.extract field back to an LLM to generate a concise summary.
    import openai
    import textwrap
    from metaphor_python import Metaphor
    
    # 1. Generate Search Query
    completion = openai.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": "You are a helpful assistant that generates search queries. Only generate one search query."},
            {"role": "user", "content": "What's the recent news in physics this week?"},
        ],
    )
    search_query = completion.choices[0].message.content
    
    # 2. Search with Metaphor
    metaphor = Metaphor(EXA_API_KEY)
    search_response = metaphor.search(search_query, use_autoprompt=True)
    
    # 3. Get Contents
    contents_result = search_response.get_contents()
    content_item = contents_result.contents[0]
    
    # 4. Summarize
    summary_completion = openai.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": "You are a helpful assistant that briefly summarizes the content of a webpage."},
            {"role": "user", "content": content_item.extract},
        ],
    )
    
    print(f"Summary for {content_item.url}:\n{content_item.title}\n{textwrap.fill(summary_completion.choices[0].message.content, 80)}")
  8. Quick Start with Exa

    master

    To get started, initialize the Exa client with your API key. You can perform basic web searches with content highlights or ask direct questions using the answer method.

    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?")
  9. Initialize the Exa client

    master

    To use the SDK, instantiate the Exa class. You can either provide your API key explicitly or let the client read the EXA_API_KEY environment variable automatically.

    Get your API key from the Exa Dashboard.

    from exa_py import Exa
    
    # Automatically reads EXA_API_KEY from environment
    exa = Exa()
    
    # Or explicitly provide the key
    exa = Exa(api_key="your-api-key")
  10. Use Exa tools in agentic workflows

    master

    Exa provides provider-specific tool integrations (OpenAI, Anthropic) designed to be passed directly to LLM function-calling APIs. These tools wrap Exa's web_search and get_contents operations, providing the necessary JSON schemas and execution logic to handle model tool calls and return formatted, model-readable text results.

    Key Tool Types

    • OpenAI Tools: Compatible with OpenAI Chat Completions and Responses APIs.
    • Anthropic Tools: Compatible with Anthropic Messages API tool-use blocks.
    • Provider-neutral Tools: Raw ToolSpec objects for custom implementations.

    Workflow Pattern

    1. Initialize: Create the tool using the appropriate namespace (e.g., exa.openai.web_search()).
    2. Pass to Model: Include the tool in your model's tools parameter.
    3. Handle Calls: When the model emits a tool call, use the handle_tool_calls (or handle_tool_use for Anthropic) method to execute the tool and generate the response message to send back to the model.
    # Example: Using OpenAI web search tool
    from exa_py import Exa
    
    exa = Exa(api_key="YOUR_API_KEY")
    
    # Create the tool
    web_search_tool = exa.openai.web_search()
    
    # The tool can be passed directly to OpenAI's tools list
    tools = [web_search_tool]
    
    # After the model returns a tool call, handle it:
    # (Assuming 'assistant_message' is the message from the LLM)
    tool_messages = exa.openai.handle_tool_calls(assistant_message, tools=tools)