scrape_ with_ actions
Ejecute cadenas de acciones de navegador, incluidas clic, desplazamiento, escritura y autocompletado de formularios, con captura de capturas de pantalla. Ideal para flujos de inicio de sesión, desplazamiento infinito, diálogos modales y sitios complejos con uso intensivo de JavaScript.
Casos de uso
Flujos de inicio de sesión
Automatice formularios de inicio de sesión y acceda a contenido autenticado tras los muros de inicio de sesión
Desplazamiento infinito
Haga scraping de contenido de páginas con desplazamiento infinito, como feeds de redes sociales y listados de productos
Diálogos modales
Interactúe con ventanas emergentes, modales y superposiciones dinámicas
Sitios con uso intensivo de JavaScript
Maneje SPAs y sitios con carga de contenido dinámico mediante AJAX
Formularios de múltiples pasos
Navegue por asistentes de múltiples pasos y envíos de formularios complejos
Pruebas visuales
Capture capturas de pantalla en cada paso para depuración y pruebas de regresión visual
Endpoint
/api/v1/tools/scrape_with_actionsParameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Required | - | La URL a cargar antes de ejecutar la cadena de acciones Example: https://app.example.com/dashboard |
actions | array | Required | - | Lista ordenada de 1-20 acciones de navegador a ejecutar antes del scraping. Cada elemento es un objeto con un `type` de `wait`, `click`, `type`, `press`, `scroll`, `screenshot`, `executeJavaScript`, `select`, `hover`, `navigate` o `snapshot`, además de campos específicos según el tipo: `selector` (destino CSS, o una referencia `@e1` de un `snapshot` anterior), `text` (para `type`), `key` (para `press`), `script` (para `executeJavaScript`), `duration`/`condition` (para `wait`), `button`/`clickCount`/`delay` (para `click`), `direction`/`distance`/`toElement` (para `scroll`) `fullPage`/`quality`/`format` (para `screenshot`), `value` o `values` (para `select`), `url`/`waitUntil` (para `navigate`) e `interactiveOnly`/`maxNodes` (para `snapshot`). `select` admite un único `value` o un arreglo `values`, y una cadena simple coincide con una opción por su valor o por su etiqueta visible; `hover` necesita `selector` y, opcionalmente, `force` y `position`. Un `snapshot` enumera los elementos interactivos de la página, incluidos los que están dentro de shadow roots abiertos y de iframes, con una referencia estable en cada uno —`@e1`, `@e2`, … en orden del documento— y cualquier acción posterior de la misma cadena puede apuntar a una de esas referencias en lugar de un selector CSS; la navegación las invalida, así que vuelve a tomar un `snapshot` cuando la página haya cambiado. Opcionales en cualquier acción: `timeout` (por acción, predeterminado 10000ms; distinto de `browserOptions.timeout`, que presupuesta toda la cadena), `description`, `continueOnError` (predeterminado false), `retries` (0-5, predeterminado 1) y `captureAfter` (predeterminado false). Example: [{"type": "click", "selector": "#login"}, {"type": "type", "selector": "#email", "text": "user@example.com"}, {"type": "wait", "duration": 1000}, {"type": "screenshot"}] |
formats | array | Optional | ["json"] | Formatos de salida a devolver: `markdown`, `html`, `json` o `text`. Para obtener una imagen, añade una acción `{"type": "screenshot"}` en el punto donde quieras tomarla. Example: ["markdown"] |
captureIntermediateStates | boolean | Optional | false | Captura el estado de la página después de cada acción: `intermediateStates[]` recibe una entrada por acción (`capturePoint` es la posición de la acción, contando desde 1) con la `url`, el `title` y los formatos solicitados en ese punto Example: true |
formAutoFill | object | Optional | - | Rellena y envía un formulario en un solo paso. Estructura: `{ fields: [{ selector, value, type: text|select|checkbox|radio|file, waitAfter }], submitSelector, waitAfterSubmit }` — `waitAfterSubmit` es 2000ms de forma predeterminada. El `type` de cada campo decide cómo se rellena: `text` (el predeterminado) escribe el valor, `select` elige la opción por valor o etiqueta, y `checkbox` y `radio` marcan el control: un selector que coincide con todo un grupo, como `input[name=size]`, marca el miembro cuyo atributo `value` es igual a `value`, y un checkbox con valor `"false"` se desmarca. `file` no se admite y hace fallar esa acción. Example: {"fields": [{"selector": "#email", "value": "user@example.com", "type": "text"}], "submitSelector": "#login"} |
browserOptions | object | Optional | - | Configuración del navegador: `headless` (predeterminado true), `userAgent`, `viewportWidth` (predeterminado 1280, rango 800-1920), `viewportHeight` (predeterminado 720, rango 600-1080), `timeout` (predeterminado 30000ms, rango 10000-120000) y `stealth` (predeterminado false). Mantenga `timeout` dentro de la ventana REST de ~25s. Ponga `stealth` en true para ejecutar la cadena de acciones en el motor sigiloso de Chromium con su perfil `medium` en lugar del grupo de navegadores estándar: el booleano es el único control, así que el nivel, la aleatorización de huellas y la elección de motor no se pueden ajustar desde aquí. Tarda más en arrancar y renderiza JavaScript, no resuelve desafíos. `consent` (predeterminado `"off"`) gestiona los banners de cookies y consentimiento: `"reject"` ejecuta el rechazo (opt-out) de DuckDuckGo autoconsent sobre el banner una vez tras la navegación inicial y tras cada acción `navigate`, con un límite de 2 s, y `"accept"` acepta en su lugar. Cuando no es `"off"`, el resultado incluye `consent: { cmp, action, ms }` para la navegación inicial, y el resultado de cada acción `navigate` incluye su propio `result.consent`. `cmp` es la plataforma de gestión de consentimiento detectada, o `null` si no se detectó ninguna; `action` es `optOut`, `optIn`, `hide` (una regla cosmética ocultó el banner), `timeout` (se detectó un banner pero no se respondió dentro del límite de 2 s), `error` o `none` (no se detectó nada), y `ms`, cuánto tardó. Una cadena nunca falla porque no se detectara ninguna plataforma. El valor predeterminado es off para que una cadena que pulsa el banner por su cuenta siga funcionando. `proxyRotation` enruta el navegador sigiloso a través de proxies que tú aportas: `{ enabled, proxies, rotationInterval }`, donde `enabled` es false por defecto y `rotationInterval` (predeterminado 300000) es el tiempo mínimo en ms en un mismo proxy antes de que la lista avance al siguiente. Requiere `stealth: true` y se rechaza en caso contrario. Los proxies se aplican por contexto solo en el motor Chromium; un `proxyRotation` por llamada se rechaza en Camoufox, que aplica su proxy al lanzar el navegador y comparte ese navegador entre llamadas. Cada entrada de `proxies` es una URL de proxy: `"http://user:pass@host:port"` (codifica con porcentaje una contraseña que contenga `@`, `:` o `/`) o un `"host:port"` a secas para un proxy HTTP sin autenticación; se aceptan `http`, `https`, `socks4` y `socks5`. Se rechazan los proxies en direcciones de loopback, privadas, link-local o de metadatos de la nube, y las credenciales nunca se devuelven: `metadata.browserOptions` en el resultado las muestra ocultas. CrawlForge no proporciona proxies: el navegador sigiloso alojado sale desde una dirección de centro de datos, y un proxy residencial es lo que supera un bloqueo por reputación de IP que ninguna huella digital resuelve. Example: {"viewportWidth": 1440, "viewportHeight": 900, "timeout": 20000} |
extractionOptions | object | Optional | - | Opciones de extracción de contenido: `selectors` (un mapa CSS clave→valor de los datos a extraer), `includeMetadata` (predeterminado true), `includeLinks` (predeterminado true) e `includeImages` (predeterminado true). Example: {"selectors": {"title": "h1", "price": ".price"}} |
continueOnActionError | boolean | Optional | false | Continúa ejecutando las acciones restantes cuando una falla, en lugar de abortar la cadena Example: false |
maxRetries | number | Optional | 0 | Reintentos de la cadena completa en caso de fallo (0-3). Un reintento vuelve a navegar a la URL inicial y repite todas las acciones, por lo que cada uno cuesta lo mismo que la ejecución original; cada intento se registra en `attempts[]` Example: 0 |
screenshotOnError | boolean | Optional | true | Toma una captura de pantalla cuando ocurre un error Example: true |
respect_robots | boolean | Optional | true | Respeta el robots.txt del sitio de destino. Si lo omite, se aplica el valor predeterminado conforme (`true`): una URL que robots.txt no permita a `CrawlForge` se rechaza antes de abrir el navegador y este endpoint no cobra nada por ello, y cada acción `navigate` se comprueba igual. La página se vuelve a comprobar allí donde acabe —tras una redirección, un clic o el envío de un formulario—, y si llega a una ruta no permitida la llamada falla sin devolver nada de esa página. El `Crawl-delay` del sitio se respeta diga lo que diga este parámetro: un `navigate`, y un `click`, `press`, `select` o `check` que pueda llevar la página a otro sitio, espera ese tiempo desde la última solicitud al host de la página antes de ejecutarse. Póngalo en `false` solo para destinos con los que tenga su propio acuerdo: la anulación queda registrada en su API key. Example: true |
max_inline_chars | number | Optional | 40000 | Tamaño máximo del resultado que se devuelve en línea, en caracteres de su JSON (de 1.000 a 10.000.000). Por encima, la respuesta incluye `preview` (los primeros `max_inline_chars` caracteres del campo de texto principal del resultado), `result_handle`, `total_chars`, `truncated: true` y `expires_at`, y [read_result](/docs/api-reference/tools/read-result) lee el resto por 1 credit por llamada. Los resultados almacenados se conservan 1 hora. Example: 40000 |
redact_pii | boolean | object | Optional | false | Elimine los datos personales del texto que devuelve esta llamada, antes de que el resultado se almacene o se envíe de vuelta. `true` es la forma abreviada de `{ mode: "fast" }`: las cuatro clases de expresiones regulares, etiquetadas. Siempre que pida redacción, la respuesta lleva `redaction: { entities, count, mode }` dentro de `data`, incluso cuando no hubo coincidencias (`count: 0`), de modo que «no se encontró nada» nunca se confunde con «se ignoró el parámetro»; una clase sin coincidencias se omite en lugar de informarse como `0`. La redacción se ejecuta **antes** de almacenar el resultado, así que un resultado grande que se lea después con [read_result](/docs/api-reference/tools/read-result) ya viene redactado. Dos límites deliberados: las direcciones (`url`, `link`, `href`, `canonical_url`) nunca se redactan, y los contadores derivados del texto (`content_length`, `word_count`, `character_count`) lo describen tal como se extrajo, antes de la redacción. Example: true |
Tipos de acción disponibles
{"type": "wait", "duration": 2000}{"type": "click", "selector": "#button"}{"type": "type", "selector": "#search", "text": "query"}{"type": "press", "key": "Enter"}{"type": "scroll", "toElement": "#content"}{"type": "screenshot"}ALLOW_JAVASCRIPT_EXECUTION=true; se rechaza en la API alojada.{"type": "executeJavaScript", "script": "window.scrollTo(0, 0)"}<select>: selector indica el desplegable y luego value (una opción) o values (varias). Una cadena simple coincide con una opción por su valor o por su etiqueta visible.selector, para menús y descripciones emergentes que solo aparecen al pasar el ratón. force y position se comportan igual que en click.url en la misma sesión del navegador, de modo que las cookies, el localStorage y el estado de sesión de las acciones anteriores se conservan. El waitUntil opcional es load, domcontentloaded (el predeterminado), networkidle o commit. La nueva URL pasa las mismas comprobaciones de robots.txt y SSRF que la inicial, así que no sirve para eludir el control. Cada navegación se comprueba en busca de un muro anti-bots: el result de la acción incluye httpStatus y, cuando se identificó un muro, blocked: { vendor, evidence }. La acción en sí sigue teniendo éxito: el muro se informa, no la hace fallar.[document] "Example Domain", luego @e1 [textbox] "Search..." y @e2 [button] "Search". Un elemento dentro de un iframe o de un shadow root recibe una referencia @eN normal, como cualquier otro. Pon interactiveOnly (predeterminado true) en false para enumerar también los encabezados y las regiones (landmarks), que nunca reciben una referencia; maxNodes (predeterminado 200, máximo 1000) limita cuántos nodos enumera el árbol —con el valor predeterminado, una referencia por nodo— y el resultado indica truncated: true cuando ese límite detuvo el recorrido. La entrada de esta acción en actionResults trae el árbol en result.tree, junto a snapshotId, url, title, refCount, nodeCount, truncated, interactiveOnly y source: aria cuando el árbol procede del snapshot de accesibilidad nativo del navegador, walk cuando lo generó el recorrido de respaldo.{"type": "snapshot", "interactiveOnly": true, "maxNodes": 200}Apuntar a elementos por referencia
Un snapshot coloca una referencia estable en cada elemento interactivo que encuentra, así que la cadena se lee como snapshot, actuar sobre las referencias y volver a hacer un snapshot: [{"type": "snapshot"}, {"type": "type", "selector": "@e1", "text": "crawlforge"}, {"type": "click", "selector": "@e2"}]. Una referencia solo es válida en el documento del que se tomó su snapshot: cualquier navegación —una acción navigate o un clic que carga otra página— invalida todas las referencias, y usar una obsoleta falla con un error que te pide tomar un snapshot nuevo. Los elementos dentro de shadow roots abiertos y de iframes reciben referencias @eN normales y se apuntan de la misma forma.
Muros anti-bots en cada navegación
Cada navegación de una cadena —la carga inicial de la página y cada acción navigate— se comprueba en busca de un muro anti-bots, una página de error HTTP o un intersticial de desafío, incluido un intersticial HTTP 202 como un desafío de AWS WAF o de Amazon. El resultado las enumera en orden en navigations, con el índice 0 para la carga inicial, cada una como { url, finalUrl, httpStatus, blocked }: httpStatus es null cuando se desconoce, y blocked: { vendor, evidence } solo aparece cuando se identificó un muro (cloudflare, akamai, datadome, perimeterx, amazon, vercel, …). Un clic o el envío de un formulario que carga otra página también es una navegación: recibe su propia entrada, con trigger indicando el tipo de acción (click, press, …), y el finalUrl de nivel superior es donde terminó la cadena, mientras que url sigue siendo la que solicitaste. Un muro a mitad de la cadena no la detiene, así que una acción posterior puede seguir navegando. Si la llamada tiene éxito lo decide el documento en el que termina la cadena: una cadena que termina en un muro nunca se informa como éxito. En esta API falla con un 502 BLOCKED, que lleva blocked y navigations, y no se cobra nada; una página de error HTTP sin un muro identificado falla con un 502 TOOL_ERROR. La herramienta informa de los muros; no los resuelve: si el bloqueo recae sobre la dirección de red, pasa tu propio proxy en browserOptions.proxyRotation (con stealth: true).
Ejemplos de solicitud
# 1. Snapshot the page to learn its interactive elements and their refs.
curl -X POST https://crawlforge.dev/api/v1/tools/scrape_with_actions \
-H "X-API-Key: cf_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.example.com/login",
"actions": [{"type": "snapshot"}],
"formats": ["json"]
}'
# actionResults[0].result.tree comes back as:
# [document] "Sign in"
# @e1 [textbox] "Email"
# @e2 [textbox] "Password"
# @e3 [button] "Sign in"
# 2. Target those refs instead of guessing selectors. The chain snapshots
# again first, which stamps the refs onto this fresh load.
curl -X POST https://crawlforge.dev/api/v1/tools/scrape_with_actions \
-H "X-API-Key: cf_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.example.com/login",
"actions": [
{"type": "snapshot"},
{"type": "type", "selector": "@e1", "text": "user@example.com"},
{"type": "type", "selector": "@e2", "text": "secret123"},
{"type": "click", "selector": "@e3"},
{"type": "wait", "duration": 1000},
{"type": "screenshot"}
],
"formats": ["markdown"]
}'Ejemplo de respuesta
{ "success": true, "data": { "success": true, "url": "https://app.example.com/login", "finalUrl": "https://app.example.com/dashboard", "httpStatus": 200, "engine": "chromium", "navigations": [ { "url": "https://app.example.com/login", "finalUrl": "https://app.example.com/login", "httpStatus": 200 }, { "url": "https://app.example.com/dashboard", "finalUrl": "https://app.example.com/dashboard", "httpStatus": 200, "trigger": "click" } ], "actionResults": [ { "id": "action_1759480001102_k2m8x1q0z", "type": "click", "success": true, "executionTime": 640 }, { "id": "action_1759480001790_p7c4n9d2a", "type": "wait", "success": true, "executionTime": 1001 }, { "id": "action_1759480002805_h5t1v8r3e", "type": "screenshot", "success": true, "executionTime": 212, "result": { "format": "png", "fullPage": false, "resourceUri": "crawlforge://screenshot/action_1759480002805_h5t1v8r3e" } } ], "totalActions": 3, "successfulActions": 3, "failedActions": 0, "actionsExecuted": 3, "content": { "markdown": "# Dashboard\n\nWelcome back — you are now signed in..." }, "screenshots": [ { "actionId": "action_1759480002805_h5t1v8r3e", "format": "png", "fullPage": false, "timestamp": 1759480002805, "resourceUri": "crawlforge://screenshot/action_1759480002805_h5t1v8r3e" } ] }, "credits_used": 5, "credits_remaining": 995, "processing_time": 9000}data.urlLa URL que solicitaste. No cambia por mucho que se haya movido la cadenadata.finalUrlDónde terminó la cadena, tras cualquier redirección, clic o envío de formulariodata.httpStatusEstado del último documento del marco principal que cargó la cadenadata.navigationsTodas las navegaciones en orden, con el índice 0 para la carga inicial. Una entrada provocada por un clic o el envío de un formulario lleva `trigger`, el tipo de la acción que movió la páginadata.actionResultsUna entrada por acción, con su `type`, `success`, su duración y el `result` propio de la accióndata.actionsExecutedNúmero de acciones que se ejecutaron; `successfulActions` y `failedActions` lo desglosandata.content.markdownContenido de la página en cada formato solicitado después de completar todas las accionesdata.screenshotsSiempre presente: una entrada por cada acción `screenshot`, más una tomada en caso de error, y vacío cuando no hubo ninguna. Cada una es un URI de recurso `crawlforge://screenshot/{actionId}` en lugar de base64 en línea, y este endpoint REST pasa ese URI sin resolverlo, así que por ahora los bytes solo se pueden leer desde el servidor CrawlForge MCPcredits_usedCredits descontados por esta solicitud (5 por scrape)processing_timeTiempo total en ms, incluidas todas las acciones y esperasManejo de errores
Runtime de navegador no configurado (503 TOOL_NOT_AVAILABLE)
Esta herramienta necesita un runtime de automatización de navegador. Cuando el backend de ejecución alojado no está configurado, la llamada devuelve 503 de inmediato y no se cobran credits.
El backend de ejecución agotó el tiempo (504 MCP_UPSTREAM_TIMEOUT)
La navegación más la cadena de acciones superó el presupuesto de tiempo del backend de ejecución. Acorte las esperas, reduzca el número de acciones o reintente. Los fallos no se cobran.
Bloqueado por robots.txt (502 TOOL_ERROR)
El robots.txt del destino no permite esta URL a CrawlForge, así que no se lanzó ningún navegador y no se cobró nada. Cada acción navigate se comprueba igual, no solo la URL inicial. También se comprueba allí donde acabe la página: si una redirección, un clic o el envío de un formulario la lleva a una ruta no permitida, el mensaje nombra ambas URL («The page moved from … to …»), la cadena se detiene ahí y no se cobra nada. Las páginas de inicio de sesión y de búsqueda suelen estar excluidas, así que un flujo que pase por una necesita la anulación. Establezca respect_robots: false para anularlo si tiene su propio acuerdo con el destino: la anulación queda registrada en su API key.
Terminó en un muro anti-bots (502 BLOCKED)
El documento en el que terminó la cadena es un muro anti-bots o un intersticial de desafío, así que la llamada falla en lugar de devolver el muro como si fuera la página, y no se cobra nada. El cuerpo lleva blocked: { vendor, evidence }, que nombra el muro, las navigations de la cadena y, cuando se conoce, el httpStatus final, para que veas en qué navegación apareció. Una página de error HTTP o un marcador de posición que la herramienta no puede atribuir a un proveedor falla en su lugar con un 502 TOOL_ERROR, también sin coste. Un muro en una navegación anterior no hace fallar la llamada; se informa en navigations. La herramienta informa de los muros y no los resuelve; si el bloqueo recae sobre la dirección de red, enruta la cadena a través de tu propio proxy con browserOptions.proxyRotation.
Acción no válida (400 Bad Request)
Una o más acciones tienen parámetros no válidos. Verifique el tipo de acción y los campos obligatorios.
Credits insuficientes (402 Payment Required)
Su cuenta no tiene suficientes credits (se necesitan 5). Compre más credits o actualice su plan.
Límite de tasa excedido (429 Too Many Requests)
Ha superado el límite de tasa de su plan. Espere un momento o actualice su plan para obtener límites más altos.
Usa acciones de espera entre interacciones para asegurarte de que JavaScript tenga tiempo de ejecutarse. Para depurar, añade una acción screenshot después de cada paso y verás lo que ve el navegador.
Costo en credits
Plan Free: 1,000 credits de prueba por única vez = 200 cadenas de acciones
Plan Hobby: 5,000 credits/mes = 1,000 cadenas de acciones ($19/mo)
Plan Professional: 100,000 credits/mes = 20,000 cadenas de acciones ($99/mo)
Plan Business: 500,000 credits/mes = 100,000 cadenas de acciones ($399/mo)
Herramientas relacionadas
¿Listo para probar scrape_with_actions? Regístrese gratis y obtenga 1,000 credits para empezar a desarrollar.