> ## 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.

# Contents API

> Extract text, highlights, and summaries from any URL.

Exa Contents returns clean page content from URLs, handling JavaScript-rendered pages, PDFs, and complex layouts automatically.

All contents features are also available in [Exa Search](/docs/search/quickstart) for returned URLs, at no extra charge up to 10 results per search (\$1/1000 pages afterwards). We recommend using Search in this way instead of Contents for web search tool use cases.

## Make your first request

Pass one or more URLs or document IDs and request highlights for the parts relevant to your task. In HTTP requests, provide them in `ids`:

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

  exa = Exa()

  result = exa.get_contents(
      ["https://exa.ai/blog/dynamic-highlights"],
      highlights={"query": "token efficiency and quality results"},
  )

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

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

  const exa = new Exa();

  const result = await exa.getContents(
    ["https://exa.ai/blog/dynamic-highlights"],
    {
      highlights: {
        query: "token efficiency and quality results"
      }
    }
  );

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

  ```bash cURL 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://exa.ai/blog/dynamic-highlights"],
      "highlights": {
        "query": "token efficiency and quality results"
      }
    }'
  ```
</CodeGroup>

<Accordion title="Example response">
  ```json theme={null}
  {
    "requestId": "e492118ccdedcba5088bfc4357a8a125",
    "results": [
      {
        "id": "https://exa.ai/blog/dynamic-highlights",
        "title": "Dynamic Highlights",
        "url": "https://exa.ai/blog/dynamic-highlights",
        "highlights": [
          "With a 12k character budget, relative to existing highlights, Dynamic Highlights achieves a 40% average token efficiency gain with a notable quality increase..."
        ]
      }
    ],
    "statuses": [
      {
        "id": "https://exa.ai/blog/dynamic-highlights",
        "status": "success",
        "source": "cached"
      }
    ],
    "costDollars": {
      "total": 0.001
    }
  }
  ```
</Accordion>

Each item in `results` includes page metadata and the content view you requested. Check `statuses` for the success or failure of every URL.

<a id="dynamic-highlights" />

## Output shapes

<Tabs>
  <Tab title="Highlights">
    Highlights return relevant passages copied from the page. Start here for agents, RAG, and
    factual lookups because highlights keep context smaller than full text.

    Set `highlights: true` to enable highlights. An additional `query` parameter is recommended when using Contents to focus content extraction from the page:

    <CodeGroup>
      ```python Python theme={null}
      result = exa.get_contents(
          ["https://example.com/research-paper"],
          highlights={"query": "methodology and results"},
      )
      ```

      ```javascript JavaScript theme={null}
      const result = await exa.getContents(
        ["https://example.com/research-paper"],
        {
          highlights: {
            query: "methodology and results"
          }
        }
      );
      ```

      ```bash cURL 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://example.com/research-paper"],
          "highlights": {
            "query": "methodology and results"
          }
        }'
      ```
    </CodeGroup>

    See [Highlights](/docs/search/highlights) for Dynamic Highlights and guidance on allocating context
    across several pages.
  </Tab>

  <Tab title="Full text">
    Full text returns the clean page body as markdown. Use it when the task depends on broad context,
    document structure, or details that highlights may leave out.

    Full pages can be large, so use `maxCharacters` when you need a limit:

    <CodeGroup>
      ```python Python theme={null}
      result = exa.get_contents(
          ["https://example.com/technical-report"],
          text={"max_characters": 10000},
      )
      ```

      ```javascript JavaScript theme={null}
      const result = await exa.getContents(
        ["https://example.com/technical-report"],
        {
          text: {
            maxCharacters: 10000
          }
        }
      );
      ```

      ```bash cURL 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://example.com/technical-report"],
          "text": {
            "maxCharacters": 10000
          }
        }'
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Summary">
    Summary makes a language model call for each page. Use it when you need a generated overview or
    fields extracted into a JSON schema.

    <CodeGroup>
      ```python Python theme={null}
      result = exa.get_contents(
          ["https://example.com/company"],
          summary={"query": "Summarize the product, customers, and pricing"},
      )
      ```

      ```javascript JavaScript theme={null}
      const result = await exa.getContents(
        ["https://example.com/company"],
        {
          summary: {
            query: "Summarize the product, customers, and pricing"
          }
        }
      );
      ```

      ```bash cURL 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://example.com/company"],
          "summary": {
            "query": "Summarize the product, customers, and pricing"
          }
        }'
      ```
    </CodeGroup>

    To extract fields instead of prose, pass a JSON schema in `summary.schema`. The summary comes
    back as a JSON string matching the schema; parse it to read the fields:

    ```json theme={null}
    {
      "ids": ["https://example.com/company"],
      "summary": {
        "schema": {
          "$schema": "https://json-schema.org/draft/2020-12/schema",
          "title": "Company Information",
          "type": "object",
          "properties": {
            "name": { "type": "string", "description": "The company name" },
            "industry": { "type": "string", "description": "Primary industry" },
            "foundedYear": { "type": "number", "description": "Year the company was founded" }
          },
          "required": ["name"]
        }
      }
    }
    ```
  </Tab>
</Tabs>

Pick one content view per request. Requesting highlights, text, and summary together returns and bills each view separately.

## Content freshness

`maxAgeHours` controls how fresh the extracted page content must be.

| Value            | Behavior                                                                         |
| ---------------- | -------------------------------------------------------------------------------- |
| Omit             | Use cached content when available and fetch the page when needed                 |
| Positive integer | Use cached content if it is newer than this many hours, otherwise fetch the page |
| `0`              | Always fetch fresh content                                                       |
| `-1`             | Only use cached content                                                          |

Most requests should omit this field. Set it when stale page content would be unusable, such as for prices, availability, or frequently updated pages. Pair a low `maxAgeHours` with `livecrawlTimeout` (milliseconds) to cap how long a fresh fetch may take.

<Accordion title="Migrate from the deprecated livecrawl parameter">
  The `livecrawl` string parameter (`"always"`, `"preferred"`, `"fallback"`, `"never"`) is
  deprecated in favor of `maxAgeHours`:

  | Old `livecrawl` value | Equivalent                                                     |
  | --------------------- | -------------------------------------------------------------- |
  | `"always"`            | `maxAgeHours: 0`                                               |
  | `"never"`             | `maxAgeHours: -1`                                              |
  | `"fallback"`          | Omit `maxAgeHours`                                             |
  | `"preferred"`         | No direct equivalent; use a low value such as `maxAgeHours: 1` |
</Accordion>

## Crawl subpages

Set `subpages` to follow links from each starting URL. Add `subpageTarget` when you want Exa to prioritize particular site sections:

<CodeGroup>
  ```python Python theme={null}
  result = exa.get_contents(
      ["https://docs.example.com"],
      subpages=10,
      subpage_target=["api", "reference", "guides"],
      highlights=True,
  )
  ```

  ```javascript JavaScript theme={null}
  const result = await exa.getContents(
    ["https://docs.example.com"],
    {
      subpages: 10,
      subpageTarget: ["api", "reference", "guides"],
      highlights: true
    }
  );
  ```

  ```bash cURL 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://docs.example.com"],
      "subpages": 10,
      "subpageTarget": ["api", "reference", "guides"],
      "highlights": true
    }'
  ```
</CodeGroup>

## Images and favicons

Set `extras.imageLinks` to the number of image URLs you want from each page. Results also include
the site's `favicon` and a representative `image` URL when available. On `/search`, this option
sits at `contents.extras.imageLinks`.

## Next steps

<Columns cols={2}>
  <Card title="API reference" icon="square-terminal" href="/docs/reference/get-contents" cta="Open reference" arrow="true">
    See every request parameter and response field.
  </Card>

  <Card title="Highlights" icon="highlighter" href="/docs/search/highlights" cta="Read guide" arrow="true">
    Compare regular and Dynamic Highlights for agent and RAG context.
  </Card>

  <Card title="Search API" icon="search" href="/docs/search/quickstart" cta="Open guide" arrow="true">
    Find relevant pages before extracting their content.
  </Card>

  <Card title="SDKs" icon="code" href="/docs/sdks/quickstart" cta="View SDKs" arrow="true">
    Use Exa from Python or JavaScript.
  </Card>
</Columns>
