extract_ links
Descubra y extraiga todos los enlaces de una página web. Ideal para la generación de mapas de sitio, el análisis de enlaces y los flujos de trabajo de rastreo.
Casos de uso
Generación de mapas de sitio
Cree mapas de sitio completos extrayendo todos los enlaces internos
Descubrimiento de enlaces para rastreo
Encuentre todas las URLs que visitar en un flujo de trabajo de rastreo web
Análisis de enlaces internos
Analice la estructura de enlaces internos para optimizar el SEO
Detección de enlaces rotos
Encuentre y valide todos los enlaces de una página
Endpoint
/api/v1/tools/extract_linksParameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Optional | - | Página que se descargará para extraer los enlaces. Se requiere `url` o `html`. Example: https://example.com |
html | string | Optional | - | HTML sin procesar que se analizará en lugar de descargar. Combínelo con `base_url` para que los href relativos se resuelvan. Example: <a href='/about'>About</a> |
base_url | string | Optional | - | Base para resolver enlaces relativos; tiene prioridad sobre el `<base href>` de la propia página. Al descargar, se usa el `<base href>` de la página y, si no hay, `url`; indícala al pasar `html`. Example: https://example.com |
include_internal | boolean | Optional | true | Incluye los enlaces que apuntan al mismo host. Example: true |
include_external | boolean | Optional | true | Incluye los enlaces que apuntan a otros hosts. Póngalo en false para conservar solo los internos. Example: true |
filter_external | boolean | Optional | false | El nombre que usa la herramienta MCP para pedir solo enlaces salientes: `true` equivale a `include_internal: false` y tiene prioridad sobre él. Es un alias, aceptado para que el mismo cuerpo de solicitud funcione en ambas superficies. No elimina los enlaces de tipo `other`, `anchor` o `relative`. Example: true |
filter_domains | array | Optional | - | Conserva solo los enlaces cuyo host coincida con uno de estos dominios. Example: ["docs.example.com"] |
include_anchors | boolean | Optional | false | Incluye los enlaces que son solo un fragmento, como `#section`. Example: false |
deduplicate | boolean | Optional | true | Colapsa las URL repetidas en una sola entrada, comparándolas sin fragmento ni barra final (`/a`, `/a/` y `/a#x` son un mismo enlace); se conserva la primera aparición. Example: true |
include_metadata | boolean | Optional | true | Incluye el atributo `title` de cada enlace y el mapa de recuento `domains`. Cada registro de enlace lleva `href`, `text`, `type`, `domain`, `rel` y `original_href` en cualquier caso: la misma forma que devuelve la herramienta MCP. Example: true |
respect_robots | boolean | Optional | true | Respeta el robots.txt del sitio de destino. Con el valor `true`, una ruta que robots.txt no permita a `CrawlForge` se rechaza con un 403 antes de descargar nada y no se cobran credits. Póngalo en `false` solo para destinos con los que tenga su propio acuerdo: la respuesta incluye entonces una entrada `warnings` y la anulación queda registrada en su API key. Example: true |
escalate | boolean | Optional | false | Activa un único reintento automático a través del navegador stealth cuando la descarga simple de `url` encuentra un muro en lugar de la página: una página de desafío de Cloudflare, Amazon, DataDome, PerimeterX, Akamai o Vercel, un 403, 429 o 444 sin más, o un armazón vacío o una página de error servidos con un 2xx. La descarga simple siempre se ejecuta primero; un 404, un 5xx, un binario como un PDF o un host cuya ventana de `Retry-After` sigue abierta nunca escalan, y la entrada `html` nunca se descarga. Los enlaces se leen del HTML renderizado, de modo que la respuesta tiene la misma forma, más `escalated` (siempre que se indique este parámetro) y `stealth: { engine, vendor_detected }` cuando el renderizado se ejecutó. La escalada reutiliza la misma ruta stealth que [stealth_mode](/docs/api-reference/tools/stealth-mode) y la misma comprobación de robots.txt, y no añade ningún tratamiento nuevo de una defensa antibots; un muro en el que también se rechaza el renderizado sigue devolviéndose como página bloqueada con `escalated: true`, sin coste. Añade 5 credits, cobrados solo cuando el renderizado se ejecuta de verdad. Example: true |
escalate_engine | string | Optional | playwright | Motor de navegador para la etapa de escalada: "playwright" (por defecto) o "camoufox" (basado en Firefox, con mayor resistencia a la detección; disponible solo donde esté instalado en el backend). Se ignora salvo que `escalate` sea `true` y la descarga simple haya quedado bloqueada. Example: camoufox |
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 JSON formateado), `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 |
Descarga de una URL
- La página se descarga una vez con un tiempo de espera de 15 segundos. Un 429 o 503 cuyo
Retry-Aftersea de 10 segundos o menos se espera y se vuelve a descargar una vez; unRetry-Aftermás largo o ausente no se reintenta. - Se leen HTML, XHTML, XML y cualquier tipo
text/*, y una respuesta sinContent-Typese lee como HTML. Un PDF, una imagen u otro binario devuelve415 UNSUPPORTED_CONTENT_TYPEconnext_step: "process_document"y no cobra nada: léelo con process_document. - Un cuerpo JSON (
application/jsono*+json) no devuelve enlaces, con el avisothe target returned application/json; it has no HTML links. - Una página que se renderiza en el cliente —menos de 200 caracteres de texto visible y un
<div id="root">,"app"o"__next"vacío, o un texto que dice que la página necesita JavaScript ("Enable JavaScript…")— se devuelve con éxito, conrendered: falseendatay el avisopage renders client-side; use scrapeenwarningscuando no se ejecutó ningún renderizado stealth, para que un armazón vacío no se confunda con una página corta. La entradahtmlse comprueba igual. - Una llamada cuesta 1 credit. Con
escalate: truela proyección es el máximo de 6 credits; los 5 credits de la etapa de escalada solo se cobran cuando la descarga simple quedó bloqueada y la etapa se ejecutó de verdad. Ningún fallo se cobra.
Ejemplos de solicitud
cURL - Extraer todos los enlaces
curl -X POST https://crawlforge.dev/api/v1/tools/extract_links \
-H "X-API-Key: cf_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"include_internal": true,
"include_external": false,
"deduplicate": true
}'TypeScript - Solo enlaces internos
// npm install crawlforge-sdk
import { CrawlForge } from 'crawlforge-sdk';
const client = new CrawlForge({ apiKey: process.env.CRAWLFORGE_API_KEY });
const result = await client.extractLinks({
url: 'https://example.com',
// Internal-only: keep include_internal, switch include_external off.
include_internal: true,
include_external: false,
deduplicate: true,
});
// result.data is untyped in crawlforge-sdk 0.1 — its shape is the Response Example below.
const { base_url, extracted_from, links } = result.data as {
base_url: string; extracted_from: string; links: unknown[];
};
console.log('Base URL:', base_url);
console.log('Extracted from:', extracted_from);
for (const link of links) console.log(link);
// Narrow to specific hosts instead of a blanket internal/external split.
const docsOnly = await client.extractLinks({
url: 'https://example.com',
filter_domains: ['docs.example.com'],
});Ejemplo de respuesta
{ "success": true, "data": { "links": [ { "href": "https://example.com/about", "text": "About", "type": "internal", "domain": "example.com", "rel": null, "original_href": "/about", "title": "About us" }, { "href": "https://example.com/products", "text": "Products", "type": "internal", "domain": "example.com", "rel": null, "original_href": "/products" }, { "href": "https://other-site.com/", "text": "External Resource", "type": "external", "domain": "other-site.com", "rel": "nofollow", "original_href": "https://other-site.com" }, { "href": "mailto:hello@example.com", "text": "Email us", "type": "other", "domain": null, "rel": null, "original_href": "mailto:hello@example.com" } ], "statistics": { "total_links": 4, "internal_links": 2, "external_links": 1, "anchor_links": 0, "relative_links": 0, "other_links": 1, "unique_domains": 2 }, "domains": { "example.com": 2, "other-site.com": 1 }, "base_url": "https://example.com", "extracted_from": "https://example.com/" }, "credits_used": 1, "credits_remaining": 999, "processing_time": 210}data.linksCada enlace como { href, text, type, domain, rel, original_href } (+ title si include_metadata está activo): la misma forma de registro que devuelve la herramienta MCPdata.links[].typeinternal (mismo host que la página), external, anchor (#fragmento, con include_anchors), other (mailto:, tel:, javascript: y otros esquemas no web; href es null salvo en mailto: y tel:) o relative (entrada html sin base_url)data.links[].original_hrefEl href tal como aparece en la página; href es la URL absoluta resueltadata.statisticsRecuentos por tipo: total_links, internal_links, external_links, anchor_links, relative_links, other_links, unique_domainsdata.domainsEnlaces por dominio (solo con include_metadata)Manejo de errores
Bloqueado por un muro antibots (403, 444 o 502)
El destino respondió con una página de desafío antibots en lugar del contenido, sea cual sea su código HTTP. La respuesta es success: false con error.code BLOCKED, un objeto blocked que nombra el vendor (por ejemplo cloudflare o akamai) y la evidence por la que se reconoció, y un next_step: extract_links with escalate:true, or stealth_mode operation:"scrape". El código es el propio del destino cuando es 403, 404, 410, 429, 444 o 451, y 502 en cualquier otro caso, incluido un muro servido con 200. No se cobra ningún credit. Con escalate: true el muro se renderiza una vez en el navegador stealth; si el renderizado también se rechaza, el mismo error lleva escalated: true y ningún next_step.
El destino respondió con un error (502 Bad Gateway)
El destino respondió con un error HTTP y sin un muro antibots reconocido. error.code es UPSTREAM_HTTP y el mensaje es Target answered HTTP <n>. Un 403, 404, 410, 429, 444 o 451 se devuelve tal cual como código de la respuesta; cualquier otro error (un 5xx, o un 400 o 401 del propio destino) es un 502, para que no se confunda con un problema de su solicitud o de su API key. Un 403, 429 o 444 incluye además next_step. No se cobra ningún credit.
Bloqueado por robots.txt (403 Forbidden)
El robots.txt del sitio de destino no permite esta ruta a CrawlForge. Establezca respect_robots: false para anularlo si tiene su propio acuerdo con el destino: la anulación queda registrada en su API key. La anulación no alcanza a un host incluido en la lista de exclusión permanente de CrawlForge, que se rechaza sea cual sea el valor de respect_robots.
Tipo de contenido no admitido (415 Unsupported Media Type)
El destino sirvió un PDF, una imagen u otro binario. error.code es UNSUPPORTED_CONTENT_TYPE, el mensaje es Unsupported content type <type>: extract_links reads HTML y next_step es process_document. El cuerpo no se lee, no se escala nada y no se cobra ningún credit.
Backend de escalada no configurado (503 TOOL_NOT_AVAILABLE)
escalate: true ejecuta su etapa stealth en el backend de ejecución de CrawlForge. Cuando ese backend no está configurado, la etapa devuelve 503 y no se cobra ningún credit.
Herramientas relacionadas
¿Listo para extraer enlaces? Regístrese gratis y obtenga 1,000 credits.