> ## 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 clean, LLM-ready web content.

<div className="callout-box not-prose">
  <p className="callout-title">Just want working code?</p>

  <p className="callout-body">
    Stop reading. Visit [contents coding agent reference](/docs/reference/contents-api-guide-for-coding-agents)
    and copy paste to your agent.
  </p>
</div>

## What it is

`/contents` returns clean, structured content from any URL, handling JavaScript-rendered pages, PDFs, and complex layouts automatically. You pass in URLs and choose full page text, targeted highlights, or LLM-generated summaries. It can also crawl linked subpages to pull content from entire site sections in a single request.

All contents features are also available in `/search` 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.

<Info>
  Use `/contents` when you already know the URLs. If you are starting from a query and want Exa to
  find the pages first, start with [Search](/docs/reference/search-api-guide).
</Info>

## Key capabilities

### Content modes

Choose the content view that matches the task:

| Mode                   | What You Get                             | Best For                                                |
| ---------------------- | ---------------------------------------- | ------------------------------------------------------- |
| **Text**               | Full page content as clean markdown      | Deep analysis, full context research                    |
| **Highlights**         | Key excerpts relevant to your query      | Per-page evidence and factual lookups                   |
| **Dynamic Highlights** | Excerpts allocated across the result set | Shared agent or RAG context                             |
| **Summary**            | LLM-generated abstract                   | Quick overviews, structured extraction with JSON schema |

### Subpage crawling

Automatically discover and extract content from linked pages within a site. Pass `subpages: 10` and optionally `subpageTarget: ["docs", "about"]` to focus on relevant sections.

### Content freshness

Control whether results come from cache or are freshly crawled with `maxAgeHours`:

| Setting        | Behavior                                          |
| -------------- | ------------------------------------------------- |
| Omit (default) | Livecrawl only when no cache exists               |
| `24`           | Use cache if \< 24 hours old, otherwise livecrawl |
| `0`            | Always livecrawl (slowest, freshest)              |
| `-1`           | Cache only (fastest, may be stale)                |

## Dynamic highlights

<Note>
  Dynamic Highlights is available as a research preview on `/search` and `/contents`. Include the `Exa-Beta: dynamic-highlights-2026-08-28` header on every request that sets `dynamic: true`.
</Note>

Regular highlights find relevant excerpts within each page independently. Dynamic Highlights considers the pages together and allocates one shared context budget across the result set. Useful pages can receive more context, while redundant or weak pages can receive less context.

Use it when several pages will feed the same agent or RAG context. Keep regular highlights when every page needs its own excerpt or a predictable per-page limit.

<Tabs>
  <Tab title="Search">
    On `/search`, enable it inside `contents.highlights`:

    ```bash theme={null}
    curl -X POST 'https://api.exa.ai/search' \
      -H "x-api-key: $EXA_API_KEY" \
      -H 'Content-Type: application/json' \
      -H 'Exa-Beta: dynamic-highlights-2026-08-28' \
      -d '{
        "query": "How are inference providers reducing transformer latency?",
        "numResults": 5,
        "contents": {
          "highlights": {
            "dynamic": true
          }
        }
      }'
    ```

    Response:

    ```json theme={null}
    {
      "results": [
        {
          "title": "How to optimize LLM inference speed and reduce costs in production",
          "url": "https://www.baseten.co/blog/how-to-optimize-llm-inference-speed-and-reduce-costs-in-production/",
          "highlights": [
            "During one decode iteration, the GPU generates one token for every active request in the batch. The problem with traditional batching is that the server waits for every request in the batch to finish before accepting new ones. ... Speculative decoding lets you generate multiple tokens per decode step ..."
          ]
        },
        {
          "title": "Smaller, faster, safer: running Kimi and GLM at scale",
          "url": "https://blog.cloudflare.com/smaller-faster-safer-models/",
          "highlights": [
            "... separating the prefill and decode phases of inference to get more out of each GPU. This post looks at three techniques we layer on top of that to fit these models into memory and keep them fast: quantizing the KV cache, compressing the model weights ..."
          ]
        },
        {
          "title": "How Modern LLM Inference Became 10-100x Faster",
          "url": "https://nandigamharikrishna.substack.com/p/how-modern-llm-inference-became-10100x",
          "highlights": [
            "... In the vLLM paper, PagedAttention achieved near-zero KV-cache ... 2 to 4x throughput improvements over systems such as FasterTransformer and Orca at similar latency."
          ]
        }
      ]
    }
    ```

    The most useful pages above received several thousand characters of the shared budget, while thinner pages received a few hundred.
  </Tab>

  <Tab title="Contents">
    On `/contents`, `highlights` remains a top-level field:

    ```bash theme={null}
    curl -X POST 'https://api.exa.ai/contents' \
      -H "x-api-key: $EXA_API_KEY" \
      -H 'Content-Type: application/json' \
      -H 'Exa-Beta: dynamic-highlights-2026-08-28' \
      -d '{
        "urls": [
          "https://www.baseten.co/blog/how-to-optimize-llm-inference-speed-and-reduce-costs-in-production/",
          "https://blog.cloudflare.com/smaller-faster-safer-models/",
          "https://www.crusoe.ai/resources/blog/430-tokens-per-second-optimizing-kimi-k2-6-and-k2-7-for-production"
        ],
        "highlights": {
          "dynamic": true,
          "query": "How are inference providers reducing transformer latency?"
        }
      }'
    ```

    Response:

    ```json theme={null}
    {
      "results": [
        {
          "url": "https://www.baseten.co/blog/how-to-optimize-llm-inference-speed-and-reduce-costs-in-production/",
          "highlights": [
            "A batch is a group of requests processed together on the GPU at the same time. Batching matters because GPUs are built to handle multiple computations from different requests in parallel. ..."
          ]
        },
        {
          "url": "https://blog.cloudflare.com/smaller-faster-safer-models/",
          "highlights": [
            "We've written before about how we serve large models on Workers AI and about separating the prefill and decode phases of inference to get more out of each GPU. ..."
          ]
        },
        {
          "url": "https://www.crusoe.ai/resources/blog/430-tokens-per-second-optimizing-kimi-k2-6-and-k2-7-for-production",
          "highlights": [
            "Through rigorous profiling, we identified a decode kernel that was operating suboptimally for specific Kimi workload shapes. We developed a custom optimization for this path ... This change alone added approximately 40 output tokens per second. ..."
          ]
        }
      ]
    }
    ```
  </Tab>
</Tabs>

The response shape does not change: each result still has a `highlights` array.

<Warning>
  Do not combine `dynamic: true` with `maxCharacters`. Dynamic Highlights sizes and distributes the shared output budget automatically.
</Warning>

## Common use cases

<Accordion title="Token-efficient info from an article">
  Get the most relevant excerpts without needing the full page.

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

<Accordion title="Structured outputs using summaries">
  Extract specific fields from any page using a JSON schema.

  ```python theme={null}
  result = exa.get_contents(
    ["https://example.com/company-page"],
    summary={
      "query": "Extract company information",
      "schema": {
        "type": "object",
        "properties": {
          "name": {"type": "string"},
          "industry": {"type": "string"},
          "founded": {"type": "number"}
        },
        "required": ["name", "industry"]
      }
    }
  )
  ```
</Accordion>

<Accordion title="Crawl many pages from a website">
  Pull content from a docs site, targeting specific sections.

  ```python theme={null}
  result = exa.get_contents(
    ["https://docs.example.com"],
    subpages=15,
    subpage_target=["api", "models", "embeddings"],
    max_age_hours=24,
    text={"max_characters": 5000}
  )
  ```
</Accordion>

## Human Quickstart

Get your API key from the [Exa Dashboard](https://dashboard.exa.ai/api-keys), then set it as an environment variable:

<Tabs>
  <Tab title="macOS/Linux">
    ```bash theme={null}
    export EXA_API_KEY="your-api-key"
    ```
  </Tab>

  <Tab title="Windows">
    ```powershell theme={null}
    setx EXA_API_KEY "your-api-key"
    ```
  </Tab>
</Tabs>

Install the SDK:

<CodeGroup>
  ```bash Python theme={null}
  pip install exa-py
  ```

  ```bash JavaScript theme={null}
  npm install exa-js
  ```
</CodeGroup>

Then make your first request:

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

  exa = Exa()

  result = exa.get_contents(
    ["https://example.com/article"],
    highlights=True
  )
  ```

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

  const exa = new Exa();

  const result = await exa.getContents(
    ["https://example.com/article"],
    {
      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 '{
      "urls": ["https://example.com/article"],
      "highlights": true
    }' | jq
  ```
</CodeGroup>

## Next

* [**Search API**](/docs/reference/search-api-guide) - Find content on the web with natural language
* [**Contents API Reference**](/docs/reference/get-contents) - Full API reference with all parameters
* [**MCP Setup**](/docs/reference/exa-mcp) - Connect your AI assistant to Exa
* [**SDKs**](/docs/sdks/python-sdk) - Python and JavaScript SDK docs
