Expand AI logo
DocsDocs
Glow Active
API Reference
Login

Documentation

Get Started

OverviewMCP ServerQuickstartWhy ExpandWays to Use Expand

Fetch

OverviewHow Fetch WorksOutput ModelProgressive DisclosureInclude OptionsDocumentsYouTubeBrowser BehaviorHighlightsPlayground & ReplayBatched Fetch

Reference

MCP Tools & ResourcesCLI, Skill & HooksCLI CommandsAPI ReferenceTypeScript SDK

Account & Billing

Pricing & UsageTiersFAQ

Machine-Readable Docs

start.mdllms.txtllms-full.txtDocs as Markdown
Browse docs

Get Started

OverviewMCP ServerQuickstartWhy ExpandWays to Use Expand

Fetch

OverviewHow Fetch WorksOutput ModelProgressive DisclosureInclude OptionsDocumentsYouTubeBrowser BehaviorHighlightsPlayground & ReplayBatched Fetch

Reference

MCP Tools & ResourcesCLI, Skill & HooksCLI CommandsAPI ReferenceTypeScript SDK

Account & Billing

Pricing & UsageTiersFAQ

Machine-Readable Docs

start.mdllms.txtllms-full.txtDocs as Markdown

Progressive Disclosure

Fetch once, then go back to the saved snapshot for more context, or let an agent verify against the original HTML, without refetching the page.

Fetch returns the useful working context first. One capture also saves the original HTML, a HAR, a WACZ archive, evidence, network records, and assets, and none of that is in the first response. Every layer is one plain GET away, keyed by the snapshot, and public snapshot reads need no API key.

answer → catalog → collection → record → preview → content

That serves two different jobs, and they work differently:

  • Programmatic use. You fetched pages earlier and now want more context on those fetches: the HAR to debug, the network records to find the API behind a page, the source HTML to re-parse.
  • Agents. The Markdown answered most of the question, but one value is missing. The agent goes into the saved HTML and looks it up instead of refetching.

Where the trail starts

Markdown frontmatter carries one more URL. JSON Mode carries the same landing page in meta.resources. Follow the URL exactly as supplied.

---
snapshotId: 01JPUBLIC01
more: https://api.expand.ai/v1/public/snapshots/01JPUBLIC01?format=markdown
---
{
  "meta": {
    "resources": {
      "version": 1,
      "snapshotId": "01JPUBLIC01",
      "url": "https://api.expand.ai/v1/public/snapshots/01JPUBLIC01",
      "markdownUrl": "https://api.expand.ai/v1/public/snapshots/01JPUBLIC01?format=markdown"


meta.resources.snapshotId is the public snapshot-link ID used by every URL below. meta.snapshotId is the capture ID used by Highlights and the Playground; they are not interchangeable.

Programmatic use: more context on fetches you already made

Store meta.resources.snapshotId next to each fetch result. Hours or days later, everything the capture saved is still readable by that ID, with no API key and no new browser session.

You needRead
The HTTP exchange behind the page, to debug a bad capture or replay it elsewhereartifacts/har/content
The JSON API a page loads, so you can call it directly next timecollections.network for the request; the HAR for every exchange
The source HTML, to re-parse with your own tooling or diff against a later fetchartifacts/html.original/content
Files the page referenced: images, PDFs, scripts

The catalog lists what was saved and where to read it. Links are paths on the API origin.

curl 'https://api.expand.ai/v1/public/snapshots/<snapshot-link-id>'
{
  "version": 1,
  "snapshotId": "01JPUBLIC01",
  "collections": {
    "evidence": "/v1/public/snapshots/01JPUBLIC01/evidence",
    "network": "/v1/public/snapshots/01JPUBLIC01/network",
    "assets": 











  • Collections return { items, next? }. Network and asset records carry the captured request and response headers, status, and timing, plus an artifact link to the HAR and a content link that resolves the saved body by URL. Requests to the same URL share one content link and it returns the first match, so for polling or repeated POSTs read the HAR, which keeps every exchange. A record's url is provenance, never a download target.
  • Artifacts are saved bytes. Use preview for a bounded, base64-encoded look; use url for the full content. Previews answer HTTP 413 when the capture archive is over the server budget; the content link still works, so fall through to the full download in that case.

With the TypeScript SDK, one fetchJson runs the browser and the rest reads back from the snapshot:

import { ExpandClient, isFetchOriginError } from "@expandai/sdk"

const client = new ExpandClient() // reads EXPAND_API_KEY

// 1. The answer: metadata + Markdown. The only step that runs a browser.
const page = await client.fetchJson({ url: "https://example.com" })
if (isFetchOriginError(page) || !page.meta.resources) throw new Error("no snapshot to disclose")

// 2. The public catalog behind meta.resources: what else was saved. No API key needed.
const snapshotId = page.meta.resources.snapshotId
const catalog = await client.snapshots.get({ snapshotId })

















The same walkthrough ships as a runnable script in the SDK package at packages/sdk-node/examples/progressive-disclosure.ts.

Agents: verify against the page after the fact

Main Markdown is pruned on purpose. Sometimes the thing the task needs was pruned with it: an attribute value, a data- field, a number inside a script tag, a hidden element, a form default. The agent should not refetch the page. The saved HTML is exact, already paid for, and one GET away.

# 1. The Markdown response names the catalog (the value is quoted). No API key for anything below.
MORE=$(sed -n 's/^more: //p' page.md | tr -d '"')
curl -sS "$MORE"

# 2. Look at the first bytes of the original HTML before committing to all of it.
curl -sS "https://api.expand.ai/v1/public/snapshots/<snapshot-link-id>/artifacts/html.original/preview?maxBytes=4096" \
  | jq -r .body | base64 -d

# 3. Pull the full source and search it.
curl -sS "https://api.expand.ai/v1/public/snapshots/<snapshot-link-id>/artifacts/html.original/content"

If the value was loaded by an XHR rather than rendered into the HTML, list collections.network to find the request, then read its content link for the saved body. If the page hit that URL more than once, read the HAR instead; content resolves by URL and returns the first match.

This is a different tool from Highlights. expandai search <snapshotId> <query> and the fetch_search MCP tool find passages that are in the Markdown and State JSON but were too long to read. The HTML is not in that corpus. When the Markdown does not contain it at all, go to html.original.

Rules for this path. The Expand skill installed by expandai skill install already teaches the more catalog step:

  • GET the more URL exactly as written, with no auth header. Follow its links; never guess routes.
  • Preview before content. Read truncated and fall through to content when the preview is cut off or answers 413.
  • Treat record url fields as provenance. Saved bytes come from the catalog's artifact links and a record's content link, never from the original URL.
  • Never refetch the origin to fill a gap. Cite the snapshot the value came from.

Next steps

  • Output Model: the fields in the first response.
  • Highlights: search the Markdown and State JSON of a saved capture.
  • Playground & Replay: inspect the same snapshot visually.
  • TypeScript SDK: the snapshots methods.
PreviousOutput Model
NextInclude Options

On This Page

Where the trail startsProgrammatic use: more context on fetches you already madeAgents: verify against the page after the factNext steps
}
}
}
collections.assets
A self-contained archive of the whole capturethe wacz artifact
"/v1/public/snapshots/01JPUBLIC01/assets"
,
"artifacts": "/v1/public/snapshots/01JPUBLIC01/artifacts"
},
"artifacts": [
{ "id": "html.original", "kind": "html", "url": ".../artifacts/html.original/content", "preview": ".../artifacts/html.original/preview" },
{ "id": "har", "kind": "har", "url": ".../artifacts/har/content", "preview": ".../artifacts/har/preview" },
{ "id": "wacz", "kind": "wacz", "url": ".../wacz" }
],
"links": {
"markdown": "/v1/public/snapshots/01JPUBLIC01?format=markdown",
"json": "/v1/public/snapshots/01JPUBLIC01"
}
}
console.log(catalog.artifacts.map((artifact) => artifact.id)) // ["html.original", "har", "wacz"]
// 3. A collection: every network request the page made.
const network = await client.snapshots.network({ snapshotId })
// 4. A bounded peek at an artifact before committing to the full download.
// Archives over the server budget answer 413 here; the content link below still works.
const preview = await client.snapshots
.preview({ snapshotId, artifactId: "html.original", maxBytes: 512 })
.catch(() => undefined)
// 5. The full bytes. Catalog links are paths on the API origin.
const har = catalog.artifacts.find((artifact) => artifact.id === "har")
if (har) {
const bytes = await fetch(new URL(har.url, page.meta.resources.url)).then((response) => response.arrayBuffer())
}
await client.close()
\
| grep -o 'data-price="[^"]*"'