> ## Documentation Index
> Fetch the complete documentation index at: https://exa.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> The Exa API is served at https://api.exa.ai. Authenticate with `Authorization: Bearer $EXA_API_KEY` (or `x-api-key: $EXA_API_KEY`); create keys at https://dashboard.exa.ai/api-keys.
> Prefer the official SDKs, `exa-py` (`pip install exa-py`) and `exa-js` (`npm install exa-js`); both read `EXA_API_KEY` from the environment.
> Tool-using agents can call Exa without writing code through the hosted MCP server at https://mcp.exa.ai/mcp, or install the Exa agent skill with `npx skills add exa-labs/agent-skills` (skill file: https://exa.ai/docs/skill.md).
> The OpenAPI specs at https://exa.ai/docs/exa-spec.yaml and https://exa.ai/docs/team-management-spec.yaml are the source of truth for request and response schemas.

# Start building with Exa

> A powerful web search tool designed for agents. Everything optimized to get you token-efficient, accurate results.

Get an API key at [dashboard.exa.ai/api-keys](https://dashboard.exa.ai/api-keys) and export it as `EXA_API_KEY`; the SDKs and the examples below read it from the environment. Start with the [quickstart](/docs/get-started/quickstart), or connect an agent directly to the hosted [Exa MCP server](/docs/get-started/exa-mcp).

## Examples

### Search the web

<CodeGroup>
  ```python theme={null}
  from exa_py import Exa

  exa = Exa()

  result = exa.search(
      "companies selling AI voice agents to dental practices",
      contents={"highlights": True},
  )

  for hit in result.results:
      print(hit.title, hit.url)
      print(hit.highlights)
  ```

  ```javascript theme={null}
  import Exa from "exa-js";

  const exa = new Exa();

  const result = await exa.search(
    "companies selling AI voice agents to dental practices",
    { contents: { highlights: true } },
  );

  for (const hit of result.results) {
    console.log(hit.title, hit.url);
    console.log(hit.highlights);
  }
  ```

  ```bash theme={null}
  curl -s -X POST "https://api.exa.ai/search" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $EXA_API_KEY" \
    -d '{
      "query": "companies selling AI voice agents to dental practices",
      "contents": { "highlights": true }
    }'
  ```
</CodeGroup>

### Get page contents

<CodeGroup>
  ```python theme={null}
  from exa_py import Exa

  exa = Exa()

  result = exa.get_contents(
      ["https://www.anthropic.com/pricing"],
      highlights={"query": "enterprise plan features and pricing"},
  )

  print(result.results[0].highlights)
  ```

  ```javascript theme={null}
  import Exa from "exa-js";

  const exa = new Exa();

  const result = await exa.getContents(
    ["https://www.anthropic.com/pricing"],
    {
      highlights: {
        query: "enterprise plan features and pricing",
      },
    },
  );

  console.log(result.results[0].highlights);
  ```

  ```bash theme={null}
  curl -s -X POST "https://api.exa.ai/contents" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $EXA_API_KEY" \
    -d '{
      "ids": ["https://www.anthropic.com/pricing"],
      "highlights": {
        "query": "enterprise plan features and pricing"
      }
    }'
  ```
</CodeGroup>

### Get structured output

<CodeGroup>
  ```python theme={null}
  from exa_py import Exa

  exa = Exa()

  result = exa.search(
      "Who is Anthropic's CEO, when was the company founded, and where is it headquartered?",
      type="deep",
      output_schema={
          "type": "object",
          "required": ["company", "ceo", "founded_year", "headquarters"],
          "properties": {
              "company": {"type": "string"},
              "ceo": {"type": "string"},
              "founded_year": {"type": "number"},
              "headquarters": {"type": "string"},
          },
      },
  )

  print(result.output.content)
  ```

  ```javascript theme={null}
  import Exa from "exa-js";

  const exa = new Exa();

  const result = await exa.search(
    "Who is Anthropic's CEO, when was the company founded, and where is it headquartered?",
    {
      type: "deep",
      outputSchema: {
        type: "object",
        required: ["company", "ceo", "founded_year", "headquarters"],
        properties: {
          company: { type: "string" },
          ceo: { type: "string" },
          founded_year: { type: "number" },
          headquarters: { type: "string" },
        },
      },
    },
  );

  console.log(result.output?.content);
  ```

  ```bash theme={null}
  curl -s -X POST "https://api.exa.ai/search" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $EXA_API_KEY" \
    -d '{
      "query": "Who is Anthropic'\''s CEO, when was the company founded, and where is it headquartered?",
      "type": "deep",
      "outputSchema": {
        "type": "object",
        "required": ["company", "ceo", "founded_year", "headquarters"],
        "properties": {
          "company": { "type": "string" },
          "ceo": { "type": "string" },
          "founded_year": { "type": "number" },
          "headquarters": { "type": "string" }
        }
      }
    }'
  ```
</CodeGroup>

### Give an LLM web search

<CodeGroup>
  ```python theme={null}
  from exa_py import Exa
  from openai import OpenAI

  exa = Exa()
  openai = OpenAI()

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

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

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

  completion = openai.chat.completions.create(model="gpt-5.6", messages=messages)

  print(completion.choices[0].message.content)
  ```

  ```javascript theme={null}
  import Exa from "exa-js";
  import { OpenAI } from "openai";

  const exa = new Exa();
  const openai = new OpenAI();

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

  let completion = await openai.chat.completions.create({
    model: "gpt-5.6",
    messages,
    tools: [exa.openai.webSearch(), exa.openai.getContents()],
  });

  const message = completion.choices[0].message;
  messages.push(message, ...(await exa.openai.handleToolCalls(message)));

  completion = await openai.chat.completions.create({ model: "gpt-5.6", messages });

  console.log(completion.choices[0].message.content);
  ```

  ```bash theme={null}
  # 1. Offer the model an Exa search tool and let it pick the query.
  USER_MSG='{ "role": "user", "content": "What'\''s the latest on AI chips?" }'
  TOOLS='[{ "type": "function", "function": {
    "name": "web_search",
    "description": "Search the web and return the most relevant pages.",
    "parameters": { "type": "object", "properties": { "query": { "type": "string" } }, "required": ["query"] }
  }}]'
  ASSISTANT="$(curl -s "https://api.openai.com/v1/chat/completions" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $OPENAI_API_KEY" \
    -d "{ \"model\": \"gpt-5.6\", \"messages\": [$USER_MSG], \"tools\": $TOOLS }" \
    | python3 -c 'import json,sys; print(json.dumps(json.load(sys.stdin)["choices"][0]["message"]))')"

  # 2. Run the tool call against Exa.
  SEARCH_BODY="$(echo "$ASSISTANT" | python3 -c '
  import json, sys
  args = json.loads(json.load(sys.stdin)["tool_calls"][0]["function"]["arguments"])
  print(json.dumps({"query": args["query"], "contents": {"highlights": True}}))')"
  RESULTS="$(curl -s -X POST "https://api.exa.ai/search" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $EXA_API_KEY" \
    -d "$SEARCH_BODY")"

  # 3. Hand the results back to the model for the final answer.
  TOOL_MSG="$(echo "$ASSISTANT" | python3 -c '
  import json, sys
  assistant = json.load(sys.stdin)
  print(json.dumps({"role": "tool", "tool_call_id": assistant["tool_calls"][0]["id"], "content": sys.argv[1]}))' "$RESULTS")"
  curl -s "https://api.openai.com/v1/chat/completions" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $OPENAI_API_KEY" \
    -d "{ \"model\": \"gpt-5.6\", \"messages\": [$USER_MSG, $ASSISTANT, $TOOL_MSG] }" \
    | python3 -c 'import json,sys; print(json.load(sys.stdin)["choices"][0]["message"]["content"])'
  ```
</CodeGroup>

### Build a researched list

<CodeGroup>
  ```python theme={null}
  import json
  from exa_py import Exa

  exa = Exa()

  run = exa.agent.runs.create(
      query="Find 10 seed-stage companies building infrastructure for AI coding agents.",
      output_schema={
          "type": "object",
          "required": ["companies"],
          "properties": {
              "companies": {
                  "type": "array",
                  "maxItems": 10,
                  "items": {
                      "type": "object",
                      "required": ["name", "website"],
                      "properties": {
                          "name": {"type": "string"},
                          "website": {"type": "string", "format": "uri"},
                      },
                  },
              }
          },
      },
      effort="auto",
  )

  run = exa.agent.runs.poll_until_finished(run.id)

  print(json.dumps(run.output.structured if run.output else None, indent=2))
  ```

  ```javascript theme={null}
  import Exa from "exa-js";

  const exa = new Exa();

  const run = await exa.agent.runs.create({
    query:
      "Find 10 seed-stage companies building infrastructure for AI coding agents.",
    outputSchema: {
      type: "object",
      required: ["companies"],
      properties: {
        companies: {
          type: "array",
          maxItems: 10,
          items: {
            type: "object",
            required: ["name", "website"],
            properties: {
              name: { type: "string" },
              website: { type: "string", format: "uri" },
            },
          },
        },
      },
    },
    effort: "auto",
  });

  const completedRun = await exa.agent.runs.pollUntilFinished(run.id);

  console.log(JSON.stringify(completedRun.output?.structured, null, 2));
  ```

  ```bash theme={null}
  RUN_ID="$(curl -s -X POST "https://api.exa.ai/agent/runs" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $EXA_API_KEY" \
    -d '{
      "query": "Find 10 seed-stage companies building infrastructure for AI coding agents.",
      "effort": "auto",
      "outputSchema": {
        "type": "object",
        "required": ["companies"],
        "properties": {
          "companies": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "required": ["name", "website"],
              "properties": {
                "name": { "type": "string" },
                "website": { "type": "string", "format": "uri" }
              }
            }
          }
        }
      }
    }' | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')"

  while true; do
    RUN_JSON="$(curl -s "https://api.exa.ai/agent/runs/$RUN_ID" \
      -H "Authorization: Bearer $EXA_API_KEY")"
    STATUS="$(printf '%s' "$RUN_JSON" | python3 -c 'import json,sys; print(json.load(sys.stdin)["status"])')"

    if [ "$STATUS" = "completed" ]; then
      printf '%s' "$RUN_JSON" | python3 -c 'import json,sys; print(json.dumps(json.load(sys.stdin)["output"]["structured"], indent=2))'
      break
    elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "cancelled" ]; then
      printf '%s' "$RUN_JSON"
      break
    fi

    sleep 4
  done
  ```
</CodeGroup>

### Analyze a complex topic

<CodeGroup>
  ```python theme={null}
  from exa_py import Exa

  exa = Exa()

  run = exa.agent.runs.create(
      query=(
          "Analyze how the EU AI Act affects startups selling AI products in Europe. "
          "Cover key dates, obligations, and practical risks, citing official sources."
      ),
      effort="medium",
  )

  run = exa.agent.runs.poll_until_finished(run.id)

  print(run.output.text if run.output else None)
  ```

  ```javascript theme={null}
  import Exa from "exa-js";

  const exa = new Exa();

  const run = await exa.agent.runs.create({
    query:
      "Analyze how the EU AI Act affects startups selling AI products in Europe. Cover key dates, obligations, and practical risks, citing official sources.",
    effort: "medium",
  });

  const completedRun = await exa.agent.runs.pollUntilFinished(run.id);

  console.log(completedRun.output?.text);
  ```

  ```bash theme={null}
  RUN_ID="$(curl -s -X POST "https://api.exa.ai/agent/runs" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $EXA_API_KEY" \
    -d '{
      "query": "Analyze how the EU AI Act affects startups selling AI products in Europe. Cover key dates, obligations, and practical risks, citing official sources.",
      "effort": "medium"
    }' | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')"

  while true; do
    RUN_JSON="$(curl -s "https://api.exa.ai/agent/runs/$RUN_ID" \
      -H "Authorization: Bearer $EXA_API_KEY")"
    STATUS="$(printf '%s' "$RUN_JSON" | python3 -c 'import json,sys; print(json.load(sys.stdin)["status"])')"

    if [ "$STATUS" = "completed" ]; then
      printf '%s' "$RUN_JSON" | python3 -c 'import json,sys; print(json.load(sys.stdin)["output"]["text"])'
      break
    elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "cancelled" ]; then
      printf '%s' "$RUN_JSON"
      break
    fi

    sleep 4
  done
  ```
</CodeGroup>

## Available APIs

* [Search](/docs/search/quickstart): real-time, grounded data with token-efficient page contents
* [Agent](/docs/agent/quickstart): async agents for deep research, list building, and enrichment
* [Deep Search](/docs/search/deep-search): search that reasons across sources and returns structured output
* [Contents](/docs/contents/quickstart): full text, highlights, and summaries from any URL
