---
type: analysis
slug: research-exa-s-contents-api-returns-cached-pages-unless-you-set-maxagehours-so-agents
title: "Exa's Contents API returns cached pages unless you set maxAgeHours, so agents need an explicit freshness setting"
description: "Exa has replaced the string livecrawl flag with an integer maxAgeHours, folded content extraction into Search pricing, and moved errors into per-URL statuses. If you leave the freshness field out, you can get a cached snapshot that is days old, and one open-source agent already shipped that bug."
published_at: 2026-10-23T21:00:00.000Z
topics: ["search","agents","mcp","research"]
source_urls: ["https://exa.ai/docs/reference/contents-retrieval","https://exa.ai/docs/reference/contents-api-guide-for-coding-agents","https://exa.ai/docs/reference/get-contents","https://exa.ai/docs/changelog","https://github.com/NousResearch/hermes-agent/pull/103858","https://github.com/quanticsoul4772/exa-mcp-server/pull/6"]
author: "Andrey Reshetnikov"
author_profile: https://newruntime.com/owner-profile.md
---
The story is a reference page, Exa's "Contents Retrieval" doc. It covers the /contents endpoint, which returns clean markdown, highlights or LLM summaries for URLs, including JavaScript-rendered pages and PDFs. A doc page on its own isn't news. It matters because many agent stacks use Exa as their web_extract or fetch tool, and the page codifies several API changes that affect what an agent actually reads and what it pays.

The main change is how freshness works. The old livecrawl string modes (never/fallback/always/preferred) are deprecated in favor of maxAgeHours, an explicit limit on how old cached content may be. Contents are also folded into Search pricing, and errors are reported per URL. For builders, the default behavior is now the thing to watch: leave the field out and you get Exa's cached index, which may not be the live page.

## What we checked

- **Does the Contents Retrieval cluster reveal a broader market or architecture transition that a single release note can't capture?** partly confirmed. Within Exa, several changelog entries add up to one architectural shift. Freshness is now a numeric cache-age setting instead of a crawl mode, contents come bundled with Search (free for the first 10 results), and the endpoint reports success or failure for each URL. The only evidence is Exa's own documentation, so this supports a vendor-level pattern, not an industry-wide transition. [4][1][3]
- **(Added) When maxAgeHours is omitted, can integrators silently receive stale content?** confirmed. Exa documents that omitting maxAgeHours means cached content is used when available. A Hermes-agent PR opened on 2026-09-05 reports that a Hacker News front page came back about 10 days old with nothing in the output to say so, and fixes it by passing max_age_hours=0. That PR was still open and unmerged when we checked. [2][5]
- **(Added) Is the migration from livecrawl to maxAgeHours causing breakage downstream?** partly confirmed. A third-party Exa MCP server PR, merged 2026-10-10, swapped livecrawl for maxAgeHours (range -1 to 720) as part of a larger fix. It found several tools failing against the live API, including a 410 from the retired Research API. The PR does not list maxAgeHours among its live-verified checks. [6]

## What we found

- The Contents reference defines maxAgeHours as follows. Omitted means use the cache when available. A positive integer means use the cache only if it is that recent. 0 means always fetch fresh, and -1 means cache only. The deprecated livecrawl values map as "always"→0, "never"→-1 and "fallback"→omit. "preferred" has no direct equivalent (Exa suggests a low value such as 1), and the two parameters cannot be sent together. [1][2]
- The API spec sets limits on both fields. maxAgeHours accepts integers from -1 to 720, and livecrawlTimeout accepts more than 0 up to 90000 ms with a default of 10000. The spec also says the deprecated livecrawl parameter 'does not guarantee freshly fetched content.' snapshotAsOf replaces the older crawledBeforeDate. [3][1]
- Errors are now reported per URL. Each entry in statuses has a status (success or error) and a source (cached or crawled). Error entries carry a tag and an httpStatusCode, but the spec does not list the tag values. Because every result says whether it came from cache or a fresh crawl, an agent can log or display freshness instead of guessing. [3][4]
- Exa's changelog dates the pricing change to March 3, 2026. Contents for the first 10 search results are included free, and the standalone Contents endpoint costs $1 per 1k pages per content type. The docs advise using Search with contents for web search tasks, and requesting a single content view, because highlights, text and summary are each billed separately. [4][2]
- The default is already causing problems. A Hermes-agent PR (opened 2026-09-05, still open and unmerged) reports that get_contents(urls, text=True) returned a roughly 10-day-old Hacker News snapshot without any warning. The proposed fix sets max_age_hours=0. The author notes this adds latency, and a failed live crawl now returns an error instead of a stale copy. [5]
- Third-party wrappers are lagging behind the API. An Exa MCP server PR merged 2026-10-10 found several default tools failing against the live API, including a 410 because the Research API was retired. It moved to maxAgeHours (-1 to 720) and agent_run, but maxAgeHours was not among the checks it verified against the live API. [6]

## What it means

Treat freshness as part of the contract you set explicitly for each tool. Don't leave it to the default. For prices, availability, news front pages, dashboards or anything else that changes faster than daily, set maxAgeHours to 0 or a small number. Budget for the extra latency, using livecrawlTimeout, which defaults to 10 s and can go up to 90 s. Handle per-URL errors, because a fresh-only request will fail where a cached one would have quietly returned an old copy. For reference material, omitting the field or setting a large value is cheaper and faster. Either way, read statuses[].source ("cached" or "crawled") and pass it to the model or log it, so a cached snapshot isn't presented as live.

Check your wrappers as well. MCP servers and agent frameworks may still send livecrawl, which Exa says doesn't guarantee fresh content. Some may still call retired endpoints. On cost, use Search with contents for the first 10 results, request one content view (highlights are recommended for RAG and agents), and use costDollars only as an estimate, since the spec calls it that and not an invoice. This is one vendor's API semantics, but the general lesson for any retrieval tool is that a cache-first default needs an explicit freshness setting and visible provenance on each result.

## Still open

- What specific error tags can statuses[].error.tag take? The API spec does not list them.
- How old can cached content get when maxAgeHours is omitted? Is there a fixed upper bound, or does it vary by domain?
- The changelog entries we retrieved date the livecrawl string deprecation to June 7, 2025 but describe maxAgeHours as 'replacing boolean livecrawl' on February 2, 2026. The exact sequence of the migration is still unclear.
- Does Exa plan to stop accepting livecrawl entirely, and if so, when?

## Sources

1. [Contents Retrieval - Exa](https://exa.ai/docs/reference/contents-retrieval) (primary)
2. [Contents API Reference (guide for coding agents) - Exa](https://exa.ai/docs/reference/contents-api-guide-for-coding-agents) (primary)
3. [Contents (API reference) - Exa](https://exa.ai/docs/reference/get-contents) (primary)
4. [Changelog - Exa](https://exa.ai/docs/changelog) (primary)
5. [fix(web/exa): web_extract requests fresh content instead of Exa's index snapshot - hermes-agent PR #103858](https://github.com/NousResearch/hermes-agent/pull/103858)
6. [Update to current Exa API (0.5.0) - exa-mcp-server PR #6](https://github.com/quanticsoul4772/exa-mcp-server/pull/6)

Author: [Andrey Reshetnikov](https://newruntime.com/owner-profile.md), AI product and AI engineering; profile for recruiters and agents: https://newruntime.com/owner-profile.json

## Continue in the graph

Next records for an agent, linked to their Markdown twins. Replace `.md` with `.json` for structured fields, including `next_reads`.

- topic hub: [Search](https://newruntime.com/topics/search.md): Explore the search topic hub.
- topic hub: [Agents](https://newruntime.com/topics/agents.md): Explore the agents topic hub.
- topic hub: [Mcp](https://newruntime.com/topics/mcp.md): Explore the mcp topic hub.
- collection: [Analysis](https://newruntime.com/analysis.json): Open the latest authorial analysis.
- collection: [Source Ledger](https://newruntime.com/source-ledger.json): Inspect the public source evidence graph.
- whole graph: [discovery graph](https://newruntime.com/discovery-graph.json) (typed nodes and edges) and [site index](https://newruntime.com/site-index.md)
