Pulsa / para buscar

Toda la documentación
docs Extraer Crawlear un sitio entero

Se ejecuta enWrit CloudDesktopAutoalojado

crawl ▸ sitios enteros

Una página, o todas.

La superficie de crawl son tres llamadas que crecen con el trabajo: una página, un mapa de las URL, o todas las páginas dentro de un alcance que tú fijas. Guarda el alcance y se vuelve invocable — POST /api/crawl/definitions/{ref}/run responde desde la última ejecución si está lo bastante fresca, y vuelve a crawlear si no.

Un crawl se queda dentro del alcance que fijas: mismo dominio por defecto, filtros de ruta que tú controlas, un presupuesto de páginas que no rebasa, y una pausa entre peticiones.

superficies ▸ tres tamaños

Tres llamadas, tres tamaños de trabajo.

Usa la más pequeña que responda tu pregunta. Una sola página es una página medida; un mapa es una lista de URL; un crawl recorre el alcance y llena un dataset que puedes leer, buscar y exportar.

LlamadaQué haceCoste
POST /api/crawl/scrapeUna página, devuelta en markdown con recuento de caracteres y de tokens.1 página
POST /api/crawl/mapLas URL que expone un sitio, sin cargar cada una entera.Medido
POST /api/crawlInicia un crawl sobre el alcance. Devuelve un job que consultas.Por página
POST /v1/keyless/crawlUnas pocas páginas del mismo dominio, un nivel, SIN cuenta. Devuelve las páginas en línea, no un job.Gratis, con tope diario
POST /api/crawl/previewEl alcance que un crawl USARÍA — include/exclude/profundidad efectivos, más una muestra de URL conservadas frente a descartadas.Nada

Una página, dos niveles

Con una clave wt_ la llamada se mide contra tu plan. Sin clave alguna, el nivel keyless devuelve la misma forma para páginas públicas — más abajo. El nivel sin clave también rastrea: POST /v1/keyless/crawl carga hasta 5 páginas del mismo dominio, un nivel de profundidad, y las devuelve en línea — sin flota, sin persona, sin salida residencial. Cada página gasta la misma asignación diaria que una llamada a <code>/v1/keyless/scrape</code>: el tope diario, no el de la petición, es el límite real, y la respuesta indica ambos.

cloud.ts

const cloud = new CloudApi({ apiKey: process.env.WRIT_API_KEY }); // wt_…
const page = await cloud.scrape("https://example.com");
const site = await cloud.map("https://example.com", { search: "pricing", limit: 20 });
console.log(cloud.tier); // "metered" | "keyless"

Qué devuelve una página

La llamada de una página devuelve un cuerpo plano, no un handle de job:

verbQué operación respondió.
url · titleLa página leída y su título.
formatSiempre "markdown" en esta llamada.
markdownEl cuerpo de la página, limpio.
countschars, raw_tokens_est y clean_tokens_est — lo que costaría leerla a un modelo.
tier"metered" cuando una clave wt_ autenticó la llamada.

Dos fallos que conviene manejar: 422 scrape_unreachable cuando la página no se puede cargar, y 402 insufficient_credits cuando la página excede tu plan y el wallet no la cubre.

alcance ▸ qué se carga

Di qué crawlear, y hasta dónde.

Solo url es obligatorio. Todo lo demás estrecha el alcance, cambia cómo se lee una página, o pone tope al trabajo. Los filtros de ruta son expresiones regulares aplicadas a la ruta.

CampoQué hace
urlObligatorio. La semilla desde la que arranca el crawl.
nameUna etiqueta para el job, para que la lista se lea como tu trabajo y no como URL.
executorregular | ai — regular por defecto. El executor ai razona sobre cada página y pesa 5×.
extract_modemarkdown | schema — markdown por defecto.
extract_schemaLa forma de los campos a extraer cuando extract_mode es schema.
extract_promptInstrucción en lenguaje llano para el executor ai.
render_modeauto | http | browser. Un render de navegador pesa 2×.
ocr_modeauto | off | force — ver documentos más abajo. Una página con OCR pesa 2×.
persona_idCrawlear con sesión iniciada, usando una identidad de acceso guardada tuya.
use_residentialRuta de red premium. Disponible en planes premium.
intentUn objetivo en lenguaje llano. Deriva el alcance y ordena qué URL vale la pena visitar primero.
seed_urls[]Puntos de partida adicionales, además de url.
relevance_threshold0–1. Cuánto debe encajar una página con el intent para conservarse.
include_paths[] · exclude_paths[]Regex de ruta. Include estrecha, exclude resta.
max_depth0–20 enlaces desde la semilla.
page_budget1–50000, 1000 por defecto. El tope duro de este crawl.
max_concurrent_shards1–64. Cuán ancho corre el crawl.
shard_size1–200, 25 por defecto. Páginas por unidad de trabajo.
delay_ms0–60000, 250 por defecto. Pausa entre peticiones.
respect_robotstrue por defecto.
same_domain · allow_subdomainsAmbos true por defecto.
content_spec{ preset, include_comments, exclude_selectors, include_selectors, keep } — qué parte de cada página se conserva.

Dos formas de apuntar. Dale include_paths y max_depth y habrás descrito la forma con exactitud. Dale intent en su lugar y habrás descrito el objetivo — el crawl deriva de ahí un alcance y ordena la frontera por cuánto encaja cada URL, con relevance_threshold como corte.

POST /api/crawl/preview devuelve el alcance que un crawl usaría de verdad — los patrones include y exclude efectivos, la profundidad, y una muestra de URL que conservaría junto a otras que descartaría. No carga nada y no cuesta nada. Lánzalo antes de un presupuesto grande.

Iniciar un crawl

En el agente local el mismo job arranca en 127.0.0.1:8131 con un token local, y el dataset que llena se relee por la superficie de datos.

crawl.ts

const job = await client.crawl.start({
  url: "https://example.com",
  max_depth: 3,
  page_budget: 500,
});
const status = await client.crawl.get(job.id);
const table = await client.data.workflowData(job.data_workflow_id);

progreso ▸ estado

Míralo trabajar. Párala cuando quieras.

Un crawl es un job, no una petición: sobrevive con normalidad a cualquier timeout HTTP sensato, así que recibes un handle y lo consultas. GET /v1/crawl los lista (limit 1–500, 50 por defecto) bajo una clave crawls; GET /v1/crawl/{id} lee uno, o 404 si no es tuyo.

statusQué significa
queuedAceptado, esperando para empezar.
mappingAveriguando qué URL están dentro del alcance.
crawlingCargando páginas.
stoppingCancelación reconocida, terminando lo que está en vuelo.
completedTerminal. Todo el alcance se visitó o lo cortó el presupuesto.
failedTerminal. Lee error para saber por qué.
cancelledTerminal. Pediste que parara.

Los contadores de un crawl

Cada lectura de un crawl lleva los mismos campos, así que un solo consultador sirve para todos los sitios de ejecución:

CampoQué contiene
id · name · seed_urlIdentidad y punto de partida.
include_paths · exclude_paths · max_depthEl alcance, tal como quedó resuelto.
same_domain · allow_subdomains · respect_robotsLas reglas de frontera vigentes.
extract_mode · extract_schemaQué se extrae de cada página.
persona_idLa identidad de acceso usada, si la hubo.
delay_ms · max_concurrent · page_budgetEl ritmo y el techo.
workflow_id · data_workflow_idDónde aterrizan las filas recogidas.
pages_discovered · pages_done · pages_failed · pages_skippedLos cuatro contadores que vale la pena graficar.
workers_active · current_depthCuán ancho y cuán profundo va ahora mismo.
status · error · cancel_requested · is_terminalDónde está, y si volverá a moverse.
created_at · updated_at · started_at · completed_atLa cronología.

Una rareza que conviene saber antes de escribir el cliente: los campos booleanos de un crawl vuelven como los enteros 0 y 1, no como true y false de JSON. Compáralos como números, o conviértelos a la entrada.

POST /v1/crawl/{id}/cancel responde siempre 200, en el estado que estuviera el job, y devuelve el crawl actualizado más cancel_requested_now — verdadero cuando fue tu llamada la que lo cambió. Cancelar un crawl ya terminado no es un error, así que reintentar es seguro.

guardado ▸ invocable

Guarda un crawl y llámalo como una API.

Una definición es una configuración de crawl con nombre y slug. Convierte un job puntual en algo que una clave puede llamar: /v1/crawl/definitions en el agente local, /api/crawl/definitions en Writ Cloud. Ambos aceptan un slug o un id como {ref}.

CampoQué hace
nameHasta 200 caracteres.
slugHasta 120 caracteres. El nombre que usan tus llamadas.
descriptionTexto libre, para quien lea la lista después.
default_max_age_secondsLa ventana de frescura a aplicar cuando una llamada no dice nada.
configLa configuración de crawl a ejecutar. Los mismos campos que al iniciar un crawl.
from_crawl_idO bien: copiar la configuración de un crawl que ya ejecutaste.

Envía exactamente uno de config o from_crawl_id. No enviar ninguno responde 400 — la definición no tendría nada que ejecutar.

Ejecutarla

El cuerpo de ejecución son cuatro campos, todos opcionales:

max_age0 segundos o más. La ventana de frescura de esta llamada. También se acepta como ?max_age= o un Cache-Control max-age.
waitfalse por defecto. En true, la llamada HTTP bloquea hasta que el crawl se resuelve.
timeout5–300 segundos, 120 por defecto. Solo tiene sentido con wait.
limit1–500, 50 por defecto. Cuántas filas recogidas vuelven en línea.

El contrato de frescura

Esta es la parte contra la que programar. El código de estado dice qué pasó, y ninguna respuesta es un callejón sin salida:

RespuestaQué pasó
200 · cached: trueLa última ejecución cayó dentro de la ventana. Sus datos vuelven en línea. No se crawleó nada y no se midió nada.
202Fallo de ventana. Arrancó un crawl fresco; el cuerpo lleva el crawl y su status_url. Consúltalo.
504wait: true rebasó su timeout. El crawl sigue corriendo y el cuerpo aún lleva crawl_id y status_url — recógelo, no reintentes.
Cache-Control: no-cache · max_age: 0Vuelve a crawlear siempre, diga lo que diga el valor por defecto de la definición.

Cada respuesta lleva un objeto _cachehit, age_seconds y source_crawl_id — para que un cliente pueda registrar por qué recibió lo que recibió. Y GET /v1/crawl/definitions/{ref}/data es una lectura pura de la última ejecución completada, a cualquier edad: nunca crawlea y nunca factura.

Guardar, llamar, leer

Tres llamadas: crear la definición, ejecutarla con una ventana, leer lo que ya contiene.

save-and-run.sh

# 1. Save the crawl — name + slug + the config it should always run.
curl -X POST https://api.usewrit.app/api/crawl/definitions \
  -H "Authorization: Bearer $WRIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Docs index",
    "slug": "docs-index",
    "default_max_age_seconds": 86400,
    "config": {
      "url": "https://example.com/docs",
      "include_paths": ["^/docs/"],
      "max_depth": 3,
      "page_budget": 500
    }
  }'

# 2. Call it. Fresh enough? You get the data. Stale? It re-crawls.
curl -X POST https://api.usewrit.app/api/crawl/definitions/docs-index/run \
  -H "Authorization: Bearer $WRIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"max_age": 86400, "wait": true, "timeout": 120, "limit": 50}'

documentos ▸ ocr

PDF, hojas de cálculo y páginas escaneadas.

Un sitio rara vez es solo HTML. Los PDF, los archivos de Word, Excel y PowerPoint, las imágenes y las páginas escaneadas se leen dentro del crawl, con OCR donde el texto son solo píxeles. Lo gobierna un único mando:

ocr_modeQué hace
autoSe usa OCR cuando una página o documento no tiene capa de texto legible.
offNunca ejecuta OCR. Los documentos con capa de texto se siguen leyendo.
forceOCR en cada página, incluso cuando existe capa de texto.

Dónde aplica: crawls que corren en la flota de Writ Cloud, y crawls autoalojados. Un crawl cloud que enrutas a tu propia máquina vinculada no hace extracción de documentos — esa vía lee páginas, no documentos. Elige el sitio de ejecución en consecuencia. Las páginas con OCR se miden 2×.

precio ▸ por página

Una página cuesta $0,0005. Algunas pesan más.

El crawl se paga por uso, por página. Dentro de las páginas de crawl mensuales de tu plan no cuesta nada extra; por encima se cobra del wallet a la misma tarifa, y un wallet que no cubre la página responde 402 insufficient_credits. Los despliegues autoalojados rastrean con tus propios agentes y no llevan ningún cargo por página por nuestra parte.

Cómo se leyó la páginaPeso
Página HTTP simple, o documento
Render de navegador
Página con OCR
executor: "ai"

Qué incluye cada plan

Tres números distintos por plan. Léelos como tres, no como uno — responden a tres preguntas diferentes.

Plan Páginas de crawl al mes Páginas en un crawl Crawls a la vez
Free 1.000 1.000 1
Starter 15.000 10.000 2
Pro 75.000 25.000 3
Growth 400.000 50.000 6
Scale 1.000.000 50.000 12
Enterprise 2.000.000 50.000 24

Son números distintos. «Páginas en un crawl» es el techo de un solo job; «páginas de crawl al mes» es lo que tu plan incluye entre todos los jobs del periodo. En Pro son 25.000 y 75.000 — el mismo plan hace tres crawls a tamaño completo al mes antes de que se facture nada.

También fallan de forma distinta. Un page_budget mayor que tu tope por crawl se recorta al tope y el crawl se ejecuta — no se te rechaza por pedirlo. Las dos barreras que sí pueden negarse son el límite de concurrencia (un crawl de más mientras otros corren) y la asignación mensual una vez que el wallet no cubre el exceso.

Leer el contador

GET /api/crawl/meta/usage devuelve tu posición actual, para que un cliente decida antes de gastar:

pages_included_per_monthLa asignación del plan para el periodo.
pages_used_this_period · pages_remainingDónde estás dentro de ella.
per_job_page_capEl recorte que se aplica a un solo crawl.
max_concurrent_crawlsCuántos pueden correr a la vez.
overage_price_micros_per_pageLo que cuesta una página por encima de la asignación.
browser_page_units · ocr_page_unitsLos multiplicadores de peso, para que tu estimación cuadre con la factura.

usage.sh

curl https://api.usewrit.app/api/crawl/meta/usage \
  -H "Authorization: Bearer $WRIT_API_KEY"

keyless ▸ sin cuenta

¿Sin cuenta? Una página cada vez.

El nivel keyless lee páginas públicas sin cuenta y sin clave. Un dispositivo se identifica por una cabecera de id de cliente, y la asignación es pequeña a propósito: existe para que un SDK o la app de escritorio funcione antes de registrarte.

LlamadaQué hace
POST /v1/keyless/scrapeMarkdown completo de una página pública. Cuesta 1 petición y 1 página.
POST /v1/keyless/mapHasta 200 URL de un sitio. Cuesta 1 petición, 0 páginas.
GET /v1/keyless/quotaLo que queda. No gasta nada.
POST /v1/keyless/crawlSiempre 402 api_key_required — crawlear un sitio entero exige cuenta.

Los topes

  • 10 peticiones y 20 páginas al día, por dispositivo.
  • 30 peticiones al día y 10 por minuto, por dirección IP.
  • Un mapa devuelve como mucho 200 URL.
  • Al pasarse de cualquiera: 429 keyless_rate_limited.

Cada llamada keyless lleva X-Writ-Client-Id — un id de dispositivo estable. Los SDKs y la app de escritorio lo ponen por ti; una llamada sin él responde 400 client_id_required.

claves ▸ scopes

Tres scopes, desiguales a propósito.

Leer una página es algo más pequeño que crawlear un sitio, así que es un scope más pequeño. Una clave entregada a un socio para lecturas de una página no puede iniciar un crawl contra tu asignación.

ScopeQué concede
crawl:executeIniciar y cancelar crawls; crear, actualizar y ejecutar crawls guardados.
crawl:readListar y leer crawls, crawls guardados y sus datos recogidos.
scrape:executeLecturas de una página, mapa y preview. Aparte, y menor.

mcp ▸ herramientas

El mismo crawl, como herramientas MCP.

El servidor MCP de escritorio expone la superficie de crawl como herramientas, para que un cliente de IA lance y relance un crawl sin que tú escribas nada de HTTP:

HerramientaQué hace
writ_crawl_siteurl, extract (markdown | schema), extract_schema, max_pages, max_depth, include[], exclude[], same_domain, allow_subdomains, content{}, persona, save_as, max_age.
writ_crawl_statusCómo va un crawl en curso.
writ_saved_crawlsLos crawls guardados que puedes llamar por su nombre.
writ_run_saved_crawlcrawl, max_age, limit — el contrato de frescura, como herramienta.
writ_saved_crawl_dataLo que un crawl guardado ya recogió. Nunca crawlea.
writ_scrape · writ_mapUna página, o la lista de URL.

Dos comportamientos que conviene saber. Reutilizar un nombre save_as actualiza ese crawl guardado en vez de crear un segundo — así un cliente de IA que va afinando su crawl te deja una definición, no doce. Y max_age solo significa algo junto a save_as: sin crawl guardado no hay ejecución previa que reutilizar.

crawl tools

// Crawl a site and save it under a callable name in one turn.
writ_crawl_site {
  "url": "https://example.com/docs",
  "extract": "markdown",
  "max_pages": 500,
  "max_depth": 3,
  "include": ["^/docs/"],
  "exclude": ["^/docs/legacy/"],
  "same_domain": true,
  "allow_subdomains": false,
  "content": { "preset": "article", "exclude_selectors": ["nav", "footer"] },
  "save_as": "docs-index"
}

// Re-using a save_as name UPDATES that saved crawl — it does not duplicate it.
// max_age only matters together with save_as.
writ_run_saved_crawl { "crawl": "docs-index", "max_age": 86400, "limit": 50 }
writ_saved_crawl_data { "crawl": "docs-index" }
writ_saved_crawls {}
writ_crawl_status { "crawl_id": 8811 }
writ_map  { "url": "https://example.com" }
writ_scrape { "url": "https://example.com/pricing" }

faq

Preguntas de crawl, respondidas.

¿Qué diferencia hay entre el tope de páginas por crawl y la asignación mensual?
Responden a preguntas distintas. El tope por crawl es el máximo de páginas que un solo job puede visitar — 25.000 en Pro. La asignación mensual es cuántas páginas de crawl incluye tu plan entre todos los jobs del periodo de facturación — 75.000 en Pro. Un page_budget por encima del tope se recorta y el crawl igual se ejecuta; es la asignación mensual la que, agotada, empieza a cobrar del wallet.
¿Cómo evito pagar por un crawl que ya lancé?
Guarda el crawl como definición y llámalo con max_age. Si la última ejecución completada terminó dentro de esa ventana recibes 200 con cached: true y los datos en línea — no se crawlea nada y no se mide nada. Si solo quieres lo que ya está, llama en su lugar al endpoint data de la definición: es una lectura pura, a cualquier edad.
Mi llamada wait:true devolvió 504. ¿Perdí las páginas?
No. Un 504 aquí significa que el crawl rebasó tu timeout, no que fallara. El cuerpo aún lleva crawl_id y status_url, así que consulta ese handle hasta que llegue a un estado terminal y recoge los datos. Reintentar la ejecución arrancaría un segundo crawl y pagaría dos veces.
¿Por qué los booleanos de un crawl vuelven como 0 y 1?
Es la forma documentada: campos como same_domain, respect_robots, cancel_requested e is_terminal se serializan como enteros en vez de booleanos JSON. Compáralos numéricamente, o conviértelos una vez en la frontera de tu cliente.
¿Se incluyen los PDF y las páginas escaneadas?
Sí, en la flota de Writ Cloud y en crawls autoalojados: PDF, archivos de Word, Excel y PowerPoint, imágenes y páginas escaneadas se leen, con OCR donde no hay capa de texto, gobernado por ocr_mode. Un crawl cloud enrutado a tu propia máquina vinculada no hace extracción de documentos. Las páginas con OCR se miden 2×.
¿Puedo crawlear un sitio entero sin cuenta?
No. El nivel keyless cubre una página pública y un mapa de URL; POST /v1/keyless/crawl responde siempre 402 api_key_required. Crawlear un sitio entero exige cuenta, porque hace falta una asignación contra la que cobrar.

fin ▸ lanzar uno

Previsualiza el alcance y lánzalo.

El preview no cuesta nada y muestra exactamente qué URL conservaría un crawl. Es la forma más barata de asegurarte antes de un presupuesto de páginas grande.