← all tools
Enrichment

Exa

Search the web by meaning instead of by keyword.

Cost per call0.1–40 cr / call
Your own keynot needed
CategoryEnrichment

Exa is a search engine built for machines. You describe what you are looking for and it finds pages that match the idea, then hands over the actual text. It is how the agent researches a company, finds lookalikes, or answers something no database holds.

Exa is discovery and research. It finds and reads; enrichment turns what it found into people you can contact.

things you can ask for

  • Read this candidate profile url, then use Exa and Apollo to find recently funded SaaS companies they would fit
  • Find agencies that look like this one and check who they are hiring on TheirStack
  • Research these five firms and summarise what they actually do
  • Find engineering blog posts from companies using Rust, then source the authors on GitHub

works well with Exa

  • Apollo

    turn the companies Exa found into named contacts

  • GitHub

    go from a technical article to the engineer who wrote it

  • TheirStack

    check whether the companies Exa surfaced are hiring

  • Firecrawl

    pull the full page when a summary is not enough

Ask for the outcome and your agent composes the run itself. Or start from a skill, a whole pipeline it already knows end to end.

browse all skills →

What your agent reads before it touches Exa

Every tool ships with a written playbook, and the agent loads it before the first call. Auth, rate limits, what each call costs, which actions need your approval, and the mistakes worth avoiding. It is the difference between an agent that knows the tool and one that guesses at it.

Read the Exa docs →
Show the raw playbookwritten for the agent

Routing layer for Exa API work. WHAT/guardrails here; HOW in reference/endpoints.md and lib/exa.py. Full docs cached at reference/docs/exa/raw/llms-full.txt.

Auth & config

  • Base URL: https://api.exa.ai
  • Auth: header x-api-key (Exa → Dashboard → API Keys). Env EXA_API_KEY; never hardcode.
  • No cursor pagination: search/find_similar return up to num_results (provider-capped); raise it or refine the query for more.

Operations

  • search(query, type=, category=, num_results=, contents=) — neural/auto search. type: auto|neural|keyword|fast|deep. category: company | research paper | news | linkedin profile | etc. Pass contents={"text": True} to get page text in one call.
  • get_contents(urls, text=, ...) — cleaned page text/highlights/summary for URLs.
  • find_similar(url, num_results=) — expand from a seed URL (lookalike pages/companies).
  • answer(query, text=) — cited LLM answer grounded in Exa results.
  • Everything else (websets people/company search, monitors, chat/completions) → generic request(method, path, json=) + cached docs.

Cost note

Every search / get_contents / find_similar / answer call is charged, including one that matches nothing — Exa prices the request it accepted. The card price covers the base request; results beyond the first 10, a per-result summary, extra content types, more URLs, and a deep/deep-reasoning type each raise it. Keep num_results tight, ask only for the content types you will read, and prefer one search with contents over separate calls. Agent runs are charged when they complete (a failed/cancelled run is not charged; auto effort has no cap — set an explicit effort and bound outputSchema arrays with maxItems when spend must be predictable). No outreach side-effects — read-only research tool, so no send guardrails (just spend awareness).

Handoff

Use Exa for discovery + research in the recruiting funnel: find candidate/company pages and signals → pull text → hand structured findings to enrichment (Apollo/Lusha/etc.) or to an ATS write. Pairs with the rest of the sourcing pipeline.

Field notes (production experience)

Field-shape note: these are vendor-native operational notes. The client returns the raw vendor JSON and uses the method names in this file — read field shapes accordingly (no normalized-wrapper / result.data. prefix).

  • type vs category are independent: type = search strategy (auto/fast/neural/deep); category = result-type filter (company/people/news/financial report). type:"news" is a 422 error - use category:"news".
  • Order: run search/contents first and inspect citations; treat answer as the synthesis layer, not the first retrieval step, when precision matters.
  • Use focused queries + small numResults on pilots, widen only if recall is low.

Callable surface — lib/exa.py

Import: from lib.exa import Exa → instantiate Exa() (reads key from env). Base: https://api.exa.ai. Generic passthrough: request(method, path, *, params, json).

  • answer(query: str, *, text: bool = False, **opts) -> dict — POST /answer — LLM answer grounded in Exa search results, with citations.
  • find_similar(url: str, *, num_results: int = 10, **opts) -> dict — POST /findSimilar — find pages semantically similar to a seed URL.
  • get_contents(urls: list[str], *, text: bool = True, **opts) -> dict — POST /contents — fetch cleaned page content for URLs (text/highlights/summary).
  • search(query: str, *, num_results: int = 10, type: str | None = None, category: str | None = None, contents: dict | None = None, **opts) -> dict — POST /search — neural/auto web search. type: auto|neural|keyword|fast|deep.