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.
turn the companies Exa found into named contacts
go from a technical article to the engineer who wrote it
check whether the companies Exa surfaced are hiring
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 →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 →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.
https://api.exa.aix-api-key (Exa → Dashboard → API Keys). Env EXA_API_KEY; never hardcode.search/find_similar return up to num_results (provider-capped); raise it or refine the query for more.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.request(method, path, json=) + cached docs.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).
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-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".search/contents first and inspect citations; treat answer as the synthesis layer, not the first retrieval step, when precision matters.numResults on pilots, widen only if recall is low.lib/exa.pyImport: 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.