Understand how Fetch renders pages before producing Markdown and evidence.
Fetch renders live pages before extraction. It runs JavaScript, observes the page, captures browser state, and returns agent-ready Markdown, State JSON, screenshots when requested, and replayable evidence.
Public Fetch keeps browser controls intentionally small: today, browserConfig.scrollFullPage is the only public browser behavior option.
curl https://api.expand.ai/v1/fetch \
-H "x-expand-api-key: $EXPAND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://stripe.com/pricing",
"browserConfig": { "scrollFullPage": true }
Fetch is not just downloading HTML. It opens the page in a browser, lets client-side JavaScript run, observes the rendered page, and then extracts the content and state that matter for agents.
That difference matters on pages where raw HTML misses the useful content:
Fetch reads and preserves page context. It is not a public browser-automation engine: this page does not promise that Fetch can fill forms, click through multi-step flows, or drive arbitrary page interactions.
Fetch decides when the page is ready internally. Public Fetch does not currently expose timeout, wait selector, network-idle, or custom readiness controls.
What that means in practice:
Synchronous Fetch has one 150-second capture-work budget. The API creates its absolute epoch before URL preparation and Hatchet dispatch, so dispatch and browser queueing, worker replacements, lease waiting, local retries, navigation, rendering, settling, optional scroll, snapshot production, and result publication all consume the same budget. Every worker invocation reads the epoch from the immutable workflow payload; retries and reassignments never receive a fresh window.
504 FetchCaptureTimeout. This includes known-started captures and the conservative fallback when classification is missing, fails, or takes longer than five seconds. It does not prove the target page caused the timeout. timeoutMs reports the configured request ceiling (150000 by default), and the SDKs do not retry this tag automatically to avoid amplifying control-plane incidents. A deliberate manual retry may still be appropriate.529 FetchCapacityTimeout with Retry-After: 5. This is a retryable Expand capacity condition, not a page verdict; both SDKs retry it.timeoutMs defaults to 60 seconds and therefore aborts long captures client-side; see TypeScript SDK.150 seconds bounds the capture-work budget, but it is not a strict success cutoff or a target: most captures finish in a few seconds. Reaching the epoch starts timeout classification; a result that arrives during that window can still win and be returned successfully. Otherwise, bounded classification produces a typed 504/529 by 155 seconds at the latest. Every capture timing constant beneath the 150-second budget — readiness waits, settle budgets, and per-phase deadlines — is internal, is not a stable public contract, and can change.
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.
{
"url": "https://example.com",
"browserConfig": {
"scrollFullPage": true
}
}The scroll pass is best-effort. It does not guarantee that every infinite-scroll page is fully exhausted, and a scroll that fails is logged and does not fail the request.
Use scrollFullPage when important content appears only after scrolling. Do not use it as a promise that every infinite-scroll page will be fully exhausted.
scrollFullPage: false -> capture the initial rendered page
scrollFullPage: true -> scroll to trigger lazy content -> return to top -> captureScreenshots are requested through include.screenshot, not browserConfig.
{
"url": "https://example.com",
"include": {
"screenshot": true
}
}Screenshots are opt-in. A requested screenshot defaults to full-page. Set include.screenshot.fullPage: false for a viewport screenshot instead. Fetch returns one screenshot per request, not both a full-page and a viewport image.
fullPage: true -> one full-page screenshot
fullPage: false -> one viewport screenshotThe response carries a screenshot URL and a fullPage boolean. The exact schema belongs to Output Model and the API Reference; see Include Options for the full request surface.
If your application needs to know what happened at the HTTP layer, request response metadata.
{
"url": "https://example.com",
"include": {
"response": {
"includeHeaders": true
}
}
}Response metadata can report:
include.response.includeHeaders is true.The full response schema lives in Output Model and the API Reference.
Fetch can handle many pages that raw HTTP cannot, but it does not promise to bypass every block, CAPTCHA, auth wall, or bot-protection system.
Fetch renders through a real browser and can handle many sites where raw HTTP fails. Some sites still block automated access; those may return FetchBlocked.
FetchBlocked.POST /v1/fetch can surface this as HTTP 403.This is the full set of public browser behavior today. The rest of the table exists so you do not hunt for options that Fetch does not expose.
| Control | Public today? | Notes |
|---|---|---|
scrollFullPage | Yes | Best-effort scroll before capture. |
| Timeout | No | Readiness is managed internally. Synchronous Fetch enforces one 150 s deadline beginning before Hatchet dispatch; batched items begin theirs before durable item-supervisor dispatch. Worker re-entry never resets either epoch. |
| Proxy / location | No |
If any of these become public, this page and the API Reference change together.
When output looks incomplete or surprising, replay shows what Fetch saw at capture time.
The common failure modes to look for: timeout, blocked, auth wall, CAPTCHA, missing lazy content, and unexpected redirects. Playground & Replay covers the inspection workflow in full.
| Proxy retry behavior is internal. |
| Viewport / device | No | Not a public Fetch option. |
| User agent | No | Not a public Fetch option. |
| Cookies / session auth | No | Authenticated browsing is not owned by this page. |
| Geolocation | No | Not a public Fetch option. |
| Wait selector / action steps | No | Fetch is not a public browser-actions API today. |
| Bot detector bypass | No | Internal-only; not a public option. |