Expand AI logo
DocsDocs
Glow Active
API Reference
Login

Documentation

Get started

IntroductionWhy ExpandQuickstartAuthenticationConnect your agent (MCP)

Products

OverviewFetching a URLBatched Fetch
Browser-rendered pagesYouTube videosPDFs and other files
API reference

SDKs & tools

TypeScript SDK
MCP tools & resourcesDocs for agents

Reference

API referenceErrors & retriesPricing & UsageLimitsConcurrency tiersFAQ
Browse docs

Get started

IntroductionWhy ExpandQuickstartAuthenticationConnect your agent (MCP)

Products

OverviewFetching a URLBatched Fetch
Browser-rendered pagesYouTube videosPDFs and other files
API reference

SDKs & tools

TypeScript SDK
MCP tools & resourcesDocs for agents

Reference

API referenceErrors & retriesPricing & UsageLimitsConcurrency tiersFAQ

Browser-rendered pages

Fetch opens each page in a real browser and runs its JavaScript before writing the Markdown, State JSON, and a Snapshot you can replay.

Fetch opens every page in a real browser and runs its JavaScript before reading it. That makes the difference on pages where the raw HTML misses what you need:

  • docs that load their content with JavaScript
  • dashboards and apps whose data lives in JSON
  • pricing pages, tables, and grids where the layout carries meaning
  • pages where the icons are the signal
Note
Fetch reads pages. It doesn't fill in forms, click through steps, or drive the page. For that, use browser automation.

Readiness and timing

Fetch decides when the page is ready. There's no option to set a timeout, a wait selector, network idle, or other readiness rules.

What to expect:

  • Pages are rendered before extraction, not captured as raw source.
  • A page that never settles may be captured partially.
  • Captures that fail or are refused surface as errors or blocked outcomes.
  • When output looks off, replay shows what Fetch actually saw.
Queue, wait for a browser
Load and render the page
Wait for the page to settle
Full-page scroll (optional)
Snapshot with Iris & X-Ray (System 1)
Record network data
0.0s0s3s

Fetchingโ€ฆ

Waiting for a browserโ€ฆ

Queue, wait for a browser
Load and render the page
Wait for the page to settle
Full-page scroll (optional)
Snapshot with Iris & X-Ray (System 1)
Record network data
0.0s0s180s

Fetchingโ€ฆ

Waiting for a browserโ€ฆ

Queue, wait for a browser
Load and render the page
Wait for the page to settle
Full-page scroll (optional)
Snapshot with Iris & X-Ray (System 1)
Record network data
0.0s0s180s

Fetchingโ€ฆ

Waiting for a browserโ€ฆ

Every Fetch that waits for its result has one 150-second budget. It starts when the request is accepted and covers everything after that: queueing, loading, settling, scrolling, the Snapshot, and delivery. Retries inside Expand don't reset it.

  • If the capture had started and the budget runs out, you get 504 FetchCaptureTimeout. The TypeScript SDK doesn't retry it, so an outage on our side isn't amplified. A manual retry can still help. The timeout doesn't prove the page was at fault.
  • If the request was still waiting for a browser, you get 529 FetchCapacityTimeout with Retry-After: 5. It's a capacity problem, not a page problem. The TypeScript SDK retries it.
  • Expand takes up to 5 seconds to decide which, so the error arrives by 155 seconds. A result that lands in that window is still returned.
  • Set your client timeout to 180 seconds. That leaves 25 seconds for the error to reach you. The TypeScript SDK's timeoutMs defaults to 60 seconds and cuts off long captures, so raise it. See TypeScript SDK.
  • In a Batched Fetch, each item gets its own 150 seconds once it's scheduled, with the same two errors. Preparing the run doesn't count against it, since you poll for results instead of holding one request open.
  • Fetches from the Playground follow the same budget and rules. Streamed requests report the outcome in the event stream.
Note
150 seconds is the ceiling, not a target. Most captures finish in a few seconds. On a 504, timeoutMs reports the ceiling, 150000 by default. The timings inside the budget, such as readiness waits, are internal and can change.

Scrolling

scrollFullPage is the one public browser behavior control. It defaults to false. When true, Fetch attempts to scroll the page before capture to trigger lazy-loaded content, then returns to the top before capturing.

Use scrollFullPage when important content appears only after scrolling.

import { ExpandClient } from '@expandai/sdk'

const expand = new ExpandClient()

const markdown = await expand.fetch({
  url: 'https://stripe.com/pricing',
  browserConfig: { scrollFullPage: true },
})
curl https://api.expand.ai/v1/fetch \
  -H "x-expand-api-key: $EXPAND_API_KEY" \
  -H



Note
The scroll pass is best-effort. It does not guarantee that every infinite-scroll page is fully exhausted. A scrolling failure is logged and does not fail the request.

Screenshots

Fetch can take a full-page or viewport screenshot of any page it opens in a browser. To get one, set include.screenshot in the request body, not browserConfig. It comes back in the JSON object as data.screenshot, with its url and a fullPage flag. Markdown Mode can't include the screenshot, but it's still saved with the Snapshot. JSON Mode returns a link to it. See Two modes.

import { ExpandClient } from '@expandai/sdk'

const expand = new ExpandClient()

const page = await expand.fetchJson({
  url: 'https://example.com',
  include: { screenshot: true },
})
curl https://api.expand.ai/v1/fetch/json \
  -H "x-expand-api-key: $EXPAND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "include": { "screenshot": true }
  }'
Note
Asking for a screenshot always uses the browser, even for a PDF or other file that would otherwise skip it.

The full schema is in the API reference. Everything else you can include, and how each mode returns it, is on Fetching a URL.

Redirects

Fetch follows redirects and captures the page it lands on. Each mode tells you where it landed in its own way:

{
  "meta": {
    "url": "https://www.example.com/" // where Fetch landed
  }
}
---
url: http://example.com          # what you asked for
finalUrl: https://www.example.com/ # where Fetch landed, only after a redirect
---
Note
A single Fetch returns only the final URL. The rest of the HTTP exchange, such as the origin's status code, the redirect's status code, and the response headers, is returned by Batched Fetch, per item, under data.response.

Blocked pages

Fetch uses a real browser, so it gets through many sites that block plain HTTP requests. Fetch does not guarantee to get past every CAPTCHA, login wall, or bot check.

  • A blocked page returns 403 FetchBlocked. Its blockedType says why: captcha, botProtection, ipBlock, or authWall. You are not charged for blocked requests.

    {
      "_tag": "FetchBlocked",
      "url": "https://example.com",
      "blockedType"
    

A capture that was cut short can still return content. In the JSON object, meta.blocked or meta.loadingStatus marks it as partial. In a Batched Fetch, each item reports its own blocked state.

What you can and can't configure

You can set three browser options. Fetch manages everything else.

OptionFieldDefinition
ScrollingscrollFullPageBest-effort scroll before capture. See Scrolling.
Screenshotinclude.screenshotCaptures a screenshot. See Screenshots.
Screenshot size

These are managed by Fetch and aren't options today:

ControlDefinition
TimeoutReadiness is managed internally. Synchronous Fetch enforces one 150 s budget per request. Batched items get their own when scheduled. Internal retries never reset it.
Proxy / locationProxy retry behavior is internal.
Viewport / deviceNot a public Fetch option.
User agentNot a public Fetch option.

If any of these become public, this page and the API reference change together.

Debug with replay

When output looks incomplete or surprising, open the replay. It shows what Fetch saw at capture time.

  • Check for missing or lazy-loaded content.
  • Inspect citations and evidence.
  • Tell page behavior (what the site rendered) apart from extraction behavior (what Fetch kept).

Look for content that loaded late, a capture that was cut short, or an unexpected redirect. Playground & replay covers the full workflow.

Next steps

  • Fetching a URL: control which artifacts Fetch returns.
  • Playground & replay: inspect evidence visually.
  • How Fetch works: the capture mental model.
PreviousBatched Fetch
NextYouTube videos

On This Page

Readiness and timingScrollingScreenshotsRedirectsBlocked pagesWhat you can and can't configureDebug with replayNext steps
"Content-Type: application/json"
\
-d '{
"url": "https://stripe.com/pricing",
"browserConfig": { "scrollFullPage": true }
}'
:
"botProtection"
}
  • Fetch retries some blocks on its own before giving up.

  • How Fetch routes traffic is internal. There's no proxy option.

  • When a result looks wrong, open the replay before assuming the page is unreachable. See Debug with replay.

  • include.screenshot.fullPage
    Defaults to full page if not set. Set to false for the viewport only.
    Cookies / session auth
    Not a public Fetch option. You can't send cookies or sign in.
    GeolocationNot a public Fetch option.
    Wait selector / action stepsNot a public Fetch option. Fetch reads pages and doesn't drive them.
    Bot detector bypassNot a public Fetch option. Fetch retries some blocks on its own.