---
title: "Interact with a page before scraping — click, type, press keys, hover, choose a select option, scroll, navigate on, run JavaScript, or wait for dynamic content. Use for SPAs, login-gated content, or multi-step flows. Set browserOptions.stealth to run the chain in a stealth browser context. A click or form submit that loads another page is a navigation too: it gets its own navigations entry (trigger names the action type), and finalUrl is where the chain ended. Screenshots are taken by {type:\"screenshot\"} actions (and on error) and stored as crawlforge://screenshot/{actionId} resources."
method: POST
path: "/tools/scrape_with_actions"
tags: ["tools"]
---

# Interact with a page before scraping — click, type, press keys, hover, choose a select option, scroll, navigate on, run JavaScript, or wait for dynamic content. Use for SPAs, login-gated content, or multi-step flows. Set browserOptions.stealth to run the chain in a stealth browser context. A click or form submit that loads another page is a navigation too: it gets its own navigations entry (trigger names the action type), and finalUrl is where the chain ended. Screenshots are taken by {type:"screenshot"} actions (and on error) and stored as crawlforge://screenshot/{actionId} resources.

`POST /tools/scrape_with_actions`

## Request

- Base URL: `https://www.crawlforge.dev/api/v1`
- URL: `https://www.crawlforge.dev/api/v1/tools/scrape_with_actions`
- Auth: API key in header `X-API-Key`

## Request body

- ScrapeWithActionsRequest
  - `url` string, required: The URL to scrape
  - `actions` object[], required: Browser actions to perform before scraping (1-20). A snapshot action first lists the interactive elements on the page, including those inside open shadow roots and iframes, as @e refs (@e1, @e2, … in document order), which later actions can target in place of CSS selectors.
    - `type` 'wait' | 'click' | 'type' | 'press' | 'scroll' | 'screenshot' | 'executeJavaScript' | 'select' | 'hover' | 'navigate' | 'snapshot': Action to perform. executeJavaScript is disabled unless the server runs with ALLOW_JAVASCRIPT_EXECUTION=true; it is refused on the hosted API.
    - `selector` string: What the action targets: either a CSS selector or an @e ref (for example @e1) taken from the most recent snapshot action in the same chain. Refs are invalidated by navigation — snapshot again after the page changes.
    - `text` string: type: text to enter
    - `key` string: press: key to press
    - `value` string: select: one option to choose in the <select> named by selector. Matches an option by its value or by its visible label.
    - `values` unknown[]: select: several options to choose, same matching rule as value. Supply value or values, not neither.
      - unknown
    - `url` string: navigate: URL to load mid-chain, in the same browser session. Passes the same robots.txt and SSRF checks as the top-level url. Every navigation is checked for a bot wall, HTTP error page or challenge interstitial (an HTTP 202 interstitial included): this action's result carries httpStatus and, when a wall was named, blocked: { vendor, evidence } — reported, not failed. The result lists every navigation in order in navigations[] (index 0 the initial load), each { url, finalUrl, httpStatus, blocked? }, httpStatus null when unknown. A click or form submit that loads another page is a navigation too: it gets its own navigations entry with trigger naming the action type, and the top-level finalUrl is where the chain ended (url stays the one requested). A chain that ends on a wall is never reported as success: it fails (over REST, a 502 TOOL_ERROR with nothing charged).
    - `waitUntil` 'load' | 'domcontentloaded' | 'networkidle' | 'commit': navigate: how long to wait for the new page before continuing
    - `script` string: executeJavaScript: script to run
    - `timeout` number: Per-action timeout in ms, distinct from browserOptions.timeout which budgets the whole chain
    - `description` string: Human-readable label for this action
    - `continueOnError` boolean: Keep going if this action fails
    - `retries` number: Retry attempts for this action (0-5)
    - `captureAfter` boolean: Capture page content after this action
    - `duration` number: wait: milliseconds to wait (0-30000)
    - `condition` 'visible' | 'hidden' | 'enabled' | 'disabled' | 'stable': wait: condition on selector
    - `button` 'left' | 'right' | 'middle': click: mouse button
    - `clickCount` number: click: number of clicks (1-3)
    - `delay` number: click/type: delay in ms (0-1000)
    - `force` boolean: click/hover: bypass actionability checks
    - `position` object: click/hover: relative position { x, y }
      - `x` number
      - `y` number
    - `clear` boolean: type: clear field before typing
    - `modifiers` unknown[]: press: modifier keys (Alt, Control, Meta, Shift)
      - unknown
    - `direction` 'up' | 'down' | 'left' | 'right': scroll: direction
    - `distance` number: scroll: pixels to scroll
    - `smooth` boolean: scroll: smooth scrolling
    - `toElement` string: scroll: selector to scroll to
    - `x` number: scroll: absolute X coordinate (window.scrollTo; with y, takes precedence over direction/distance)
    - `y` number: scroll: absolute Y coordinate (window.scrollTo; with x, takes precedence over direction/distance)
    - `fullPage` boolean: screenshot: capture full page
    - `quality` number: screenshot: jpeg quality (0-100)
    - `format` 'png' | 'jpeg': screenshot: image format
    - `args` unknown[]: executeJavaScript: arguments passed to the script
      - unknown
    - `returnResult` boolean: executeJavaScript: return the script result
    - `interactiveOnly` boolean: snapshot: list only interactive elements. Set false to include headings and landmarks too; only interactive elements are ever given a ref.
    - `maxNodes` number: snapshot: cap on how many nodes the tree lists (1-1000); under the default interactiveOnly every one of them carries a ref. The result reports truncated: true when the cap stopped the walk.
  - `formats` unknown[]: Output formats for scraped content: markdown, html, json, text. For an image, add a {type:"screenshot"} action where you want it taken.
    - unknown
  - `captureIntermediateStates` boolean: Capture page state after each action: intermediateStates[] gets one entry per action (capturePoint is the action's 1-based position) with the url, title and the requested formats at that point
  - `formAutoFill` object: Form auto-fill configuration
    - `fields` unknown[]: Fields to fill: { selector, value, type: text|select|checkbox|radio|file, waitAfter }. type picks how the field is filled: text (default) types the value, select picks the option by value or label, checkbox/radio check the input (a selector matching a group checks the member whose value attribute equals value; a checkbox with value "false" is unchecked). file is not supported and fails that action
      - unknown
    - `submitSelector` string: Selector of the submit control
    - `waitAfterSubmit` number: Wait after submit in ms (0-30000)
  - `browserOptions` object: Browser configuration options
    - `headless` boolean
    - `userAgent` string
    - `viewportWidth` number: 800-1920
    - `viewportHeight` number: 600-1080
    - `timeout` number: Browser timeout in ms (10000-120000)
    - `stealth` boolean: Run the action chain in the stealth Chromium engine at its medium profile instead of the standard browser pool. The boolean is the only knob — level, fingerprint randomization and engine are not settable from here. Slower to start; it renders JavaScript, it does not solve challenges.
    - `consent` 'off' | 'reject' | 'accept': Cookie/consent banner handling. "reject" runs an opt-out on a recognised consent banner (DuckDuckGo autoconsent rules) once after the initial navigation and after each navigate action, capped at 2 s; "accept" opts in instead. When not "off" the result carries consent: { cmp, action, ms } for the initial navigation, and each navigate action's result carries its own result.consent. cmp is the consent platform matched, or null when none was; action is optOut, optIn, hide (a cosmetic rule hid the banner), timeout (a banner was detected but not answered within 2 s), error, or none (nothing matched). No match never fails the chain. Default "off", so a chain that clicks the banner itself keeps working.
    - `proxyRotation` object: Route the stealth browser through proxies you supply. Requires stealth: true (refused otherwise). Chromium engine only: refused on Camoufox, which applies its proxy at browser launch and shares that browser across calls. CrawlForge supplies no proxies; a residential proxy is what gets past an IP-reputation block that no fingerprint fixes. Proxies on loopback, private, link-local or metadata addresses are refused. Credentials are never echoed back: metadata.browserOptions shows them redacted.
      - `enabled` boolean: Route the chain through proxies
      - `proxies` string[]: Proxy URLs: "http://user:pass@host:port" (percent-encode a password containing @ : or /), or bare "host:port" for an unauthenticated HTTP proxy. http, https, socks4 and socks5 are accepted.
      - `rotationInterval` number: Minimum ms on one proxy before the list advances
  - `respect_robots` boolean: Respect the target site's robots.txt. Omitted, the compliant default (true) applies: a URL disallowed for CrawlForge is refused before the browser opens and no credits are charged, and every navigate action is checked the same way. Setting this to false is honoured, returns a warning in the response, and is recorded against your API key.
  - `extractionOptions` object: Content extraction options
    - `selectors` object: Key-value pairs of data to extract using CSS selectors
    - `includeMetadata` boolean
    - `includeLinks` boolean
    - `includeImages` boolean
  - `continueOnActionError` boolean: Continue executing actions if one fails
  - `maxRetries` number: Whole-chain retries on failure (0-3). A retry re-navigates to the starting URL and replays every action; each attempt is reported under attempts[]
  - `screenshotOnError` boolean: Capture screenshot when an error occurs
  - `max_inline_chars` number: Largest result returned inline, in characters of its JSON (1000-10000000; env CRAWLFORGE_MAX_INLINE_CHARS sets the default). Over it, the result is stored for 1 hour and the response carries a preview, a result_handle, total_chars and truncated: true; read the rest with read_result (1 credit).
  - `redact_pii` union: Remove personal data from the text this call returns, before it is stored or sent back. true is shorthand for { mode: "fast" }: every entity, tagged. As an object: entities (any of EMAIL, PHONE, FINANCIAL, SECRET; omitted or empty means all four, and any other name is a 400 rather than a silent no-op), replace_style ("tag" → <EMAIL>, "mask" → [REDACTED], "remove" → nothing; default "tag") and mode ("fast", the default, is regex-only and costs no extra credits; "model" covers PERSON and LOCATION, needs an LLM and is rejected here — use the CrawlForge MCP server). The response carries redaction: { entities, count, mode } inside data, saying what was removed. Detection is deliberately conservative: a card number must pass Luhn and an IBAN mod-97, so a false positive cannot silently destroy real page content. URLs, queries and identifiers the response uses to name what was fetched are left intact, and counters derived from the text (content_length, word_count, character_count) describe the text as it was extracted, before redaction.
    - boolean
    - object
      - `entities` string[]
      - `replace_style` 'tag' | 'mask' | 'remove'
      - `mode` 'fast' | 'model'

## Response `200`

The tool's result; 5 credits charged (see x-credits-note and x-extra for add-ons).

- ToolSuccess: The envelope every tool returns on success. Credits are deducted only after the tool succeeded, so credits_used is the price of this call and an error response never charges.
  - `success` true, required
  - `data` object, required: The tool's result. Its shape is tool-specific and documented on the tool's docs page (x-docs-url on the operation).
  - `credits_used` integer, required: Credits charged for this call: the operation's x-credits, plus any add-on x-credits-note or x-extra describes.
  - `credits_remaining` integer, required: The account's credit balance after this call.
  - `processing_time` integer, required: Server-side processing time in milliseconds.
  - `warnings` string[]: Non-fatal notes: a robots.txt override that was recorded, a format that could not be produced, a redaction that changed the payload.

## Other responses

- `400`: VALIDATION_ERROR: the body does not match the request schema.
- `401`: MISSING_API_KEY or INVALID_API_KEY.
- `402`: INSUFFICIENT_CREDITS: the balance does not cover the tool's base price. X-Auto-Recharge-Triggered says whether an automatic recharge was started; retry once it lands.
- `429`: RATE_LIMIT_EXCEEDED: your plan's rate limit. Retry-After: 60.
- `500`: TOOL_ERROR, INTERNAL_ERROR or CREDIT_DEDUCTION_FAILED.
- `default`: Any other error. Which of 403, 404, 413, 422, 501, 502, 503 and 504 a tool can return depends on what it does (fetching a page, calling the execution backend, reading stored results); the code vocabulary is listed on the ErrorResponse schema.

---

[API](https://skmtc.dev/crawlforge/apis/crawlforge-rest-api.md) · [All operations](https://skmtc.dev/crawlforge/apis/crawlforge-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/crawlforge/apis/crawlforge-rest-api/revisions/a5532fa8d5d5?raw)
