String API
Screenshots & Actions

Browser actions

Drive a page with a sequence of clicks, typing, scrolling, and waits.

The actions array runs a sequence of browser actions against the page before returning. The API navigates to your url first, then executes each action in order. An array can contain 1 to 50 actions.

{
  "url": "https://example.com/search",
  "actions": [
    { "type": "write", "text": "wireless headphones" },
    { "type": "press", "key": "Enter" },
    { "type": "wait", "selector": ".results" },
    { "type": "scroll", "direction": "down", "amount": 2000 },
    { "type": "screenshot", "full_page": true }
  ]
}

Action types

Each action is an object with a type and its own fields.

typeFieldsDescription
navigateurlNavigate to a URL.
clickselector, all?, frame?Click an element. Set all: true to click every match.
writetextType text into the focused element.
presskeyPress a key (e.g. Enter, Tab).
hoverselector, frame?Hover over an element.
scrolldirection? (up/down/left/right), amount?, selector?, frame?Scroll the page or an element.
selectOptionselector, value (string or string array), frame?Select option(s) in a <select>.
waitmilliseconds? (≤ 30000), selector?, timeout? (≤ 30000), frame?Wait for a duration, or for a selector to appear.
screenshotfull_page?, quality? (1–100)Capture a screenshot. Max one per request.
waitForResponseurl, match?, methods?, statusCodes?, timeout? (≤ 30000), onTimeout? (fail/continue), asResult?Wait for an XHR or fetch() response that matches. With asResult: true, return its body as data. See Wait for a response.
waitForNetworkIdleidleTime? (≤ 10000), concurrency? (≤ 10), timeout? (1–30000), onTimeout? (continue/fail)Wait for the page's network to go idle. See The waitForNetworkIdle action.

frame runs the action inside an iframe. See Act inside an iframe.

Timing

The API loads your url before the first action and, like every navigate, waits for the page to load. After that page load, and after every navigate, click, press, scroll and selectOption, it lets the page settle for about 3 seconds at most, or less once the network goes quiet. write, hover, wait, screenshot, waitForResponse and waitForNetworkIdle run without a settle. The whole sequence must finish within 180 seconds; past that it fails at the next action.

Set waitUntil on the request to choose what loading url waits for instead: the HTML parsed, the load event, or the network going idle. It applies only to loading url.

The settle is best effort, so don't rely on it for content that loads slowly. Wait for what you need: a wait with a selector for an element, a waitForResponse for a network response, or a waitForNetworkIdle for the network to go idle.

Timeouts

ActionWaits forLimitWhen it runs out
click, hover, selectOption, and scroll with a selectorThe element, and with frame the iframe30000 ms, fixedThe sequence fails at the action.
wait with a selectorThe element to appeartimeout, up to 30000 ms (the default)The sequence fails at the action.
waitForResponseA matching responsetimeout, up to 30000 ms (the default)Set by onTimeout: fail by default.
waitForNetworkIdleThe network to go idletimeout, up to 30000 ms (the default)Set by onTimeout: continue by default.

A waitForNetworkIdle that runs out under onTimeout: "continue" is listed in timedOutActions.

Capture the page's network requests

Add captureXHR with executeJS: true to get every XHR and fetch() request the session made, including the ones your actions triggered, as xhr. Use a waitForResponse with asResult to return one API response as data instead of the page.

{
  "url": "https://shop.example.com/category/headphones",
  "actions": [
    { "type": "click", "selector": "button.load-more" },
    { "type": "waitForResponse", "url": "/api/products", "statusCodes": [200], "asResult": true }
  ]
}

Act inside an iframe

Give wait, click, scroll, hover or selectOption a frame, a selector for the <iframe> element, and the action runs inside it, cross-origin iframes included. For an iframe inside another, frame is a list of selectors from the outermost iframe in, at most 5. write and press type into whatever has focus, so click the field first. To get the iframes' content back with the page, add includeIframes: true or captureIframes: true. See Iframes.

Response

{
  "statusCode": 200,
  "headers": { "content-type": ["text/html"] },
  "finalUrl": "https://example.com/search?q=wireless+headphones",
  "data": "<!doctype html>…",
  "screenshot": { "format": "png", "base64": "iVBORw0KGgo…" }
}
FieldDescription
finalUrlThe URL after all actions ran (navigations and redirects applied).
dataThe final page — always present. HTML by default, Markdown with format: "markdown". When the sequence succeeded and its asResult action matched, the matched response's body instead.
screenshotPresent only if a screenshot action ran: { format, base64 }.
matchedResponsePresent only when the sequence succeeded and its asResult action matched: the response's url, method, statusCode, headers and bodyEncoding.
xhrPresent only with captureXHR: the captured requests from the whole session.
xhrTruncatedtrue when a capture limit left requests or bodies out of xhr.
iframesPresent only with captureIframes: the final page's iframes.
iframesTruncatedtrue when includeIframes or captureIframes left an iframe or its content out.
waitUntilTimedOuttrue when loading url gave up on the request's waitUntil state, so the actions ran on the page as it was.
timedOutActions0-based indexes into your actions array of the waitForNetworkIdle actions that ran out and let the sequence carry on.
errorPresent if the sequence failed.
failedActionIndex0-based index into your actions array of the action that failed.

A failed action still returns 200, and data holds the page as it stood when it failed — usually the quickest way to see why a selector missed. Fields that describe a wait or a limit, such as timedOutActions and iframesTruncated, are absent unless they apply.

Markdown output

Set format: "markdown" to get the final page converted in data, through the same converter a plain /fetch uses, so a page reads the same whichever way you fetched it. Worth setting when the response feeds a model rather than a parser. markdownMode: "full" and mainContentOnly: true work here too.

{
  "url": "https://example.com/search",
  "format": "markdown",
  "markdownMode": "full",
  "actions": [{ "type": "wait", "selector": ".results" }]
}

The response envelope stays JSON either way — finalUrl, screenshot and failedActionIndex have nowhere to ride in a bare Markdown body. format: "raw" is rejected for the same reason. A page too large to convert comes back as HTML; see Large pages.

Access control applies to every step

Each navigation an action triggers — including clicks that follow links and script-driven redirects — is re-checked against your organization's access rules. See Access control & KYC.