API de rastreo de sitemaps#
POST /v2/discover es una API de rastreo de sitemaps que lista las URLs
de un sitio web de la forma económica: un rastreo que prioriza HTTP más
el análisis del sitemap, con un fallback opcional de renderizado de
JavaScript para aplicaciones de una sola página (SPA). No hay captura de
pantalla, ni PDF, ni artefacto almacenado. Devuelve una lista plana y sin
duplicados de URLs junto con un recuento de la procedencia de cada
una, de forma síncrona en una sola llamada. Será la primitiva ligera
para "mapear el sitio": apúntala a un dominio, obtén de vuelta las
páginas que vale la pena procesar y luego pasa esa lista a
el endpoint perceive o a un trabajo de ingesta en
modo crawl.
Aquí está la llamada mínima útil. Envía una URL, obtén su mapa de vuelta:
curl -X POST https://api.enconvert.com/v2/discover \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com"
}'
La respuesta es una lista plana de URLs con contadores de procedencia: sin URLs firmadas, sin ID de operación, sin sondeo:
{
"url": "https://example.com",
"mode": "hybrid",
"total": 47,
"urls": [
"https://example.com/",
"https://example.com/pricing",
"https://example.com/docs",
"https://example.com/blog/launch"
],
"pages_crawled": 12,
"truncated": false,
"robots_respected": false,
"sources": {"sitemap": 42, "crawl": 30},
"warnings": []
}
Endpoints#
| Método | Ruta | Propósito |
|---|---|---|
POST |
/v2/discover |
Mapea las URLs de un sitio solo por HTTP y devuelve una lista plana con recuentos por fuente. |
/v2/discover expone una única ruta. No tiene estado: no hay una fila
de operación que volver a consultar ni un trabajo que sondear, así que
no existe un endpoint GET complementario como sí
lo tiene perceive.
Content-Type: application/json en el POST.
Autenticación#
Autentícate con una clave privada en el encabezado X-API-Key para
llamadas de servidor a servidor. Este es el método que usan los
ejemplos siguientes.
X-API-Key: sk_your_private_key
Las claves públicas con un token JWT bearer también funcionan, usando
el mismo flujo que el resto de los endpoints: genera un token con tu
clave pk_ y luego envíalo como Authorization: Bearer <token>. El
flujo completo, incluido el bloqueo de dominio y la renovación de
tokens, está en la guía de autenticación.
Cada clave de API lleva una lista de permitidos de endpoints. Si
/v2/discover no está en la lista de la clave, la solicitud se
rechaza con 403 y un mensaje que indica la ruta bloqueada.
Cómo funciona discover#
Una solicitud se ejecuta enteramente por HTTP. El Chrome headless
singleton que impulsa perceive nunca se toca.
La ruta de rastreo usa la estrategia de rastreador HTTP de Crawl4AI (un
GET con httpx más un análisis de enlaces con lxml por página), y la
ruta del sitemap reutiliza los mismos helpers de sitemap y feeds
puramente HTTP que el resto de la plataforma.
- Filtra la semilla. La URL que envías se verifica contra SSRF
antes de cualquier solicitud: se validan el esquema, las
credenciales incrustadas, los hostnames bloqueados y la IP
resuelta. Una URL que resuelve a una dirección privada, loopback,
link-local o de metadatos de nube se rechaza con
400. La semilla siempre se incluye como la primera entrada en su propio mapa. - Recopila desde sitemaps. En modo
sitemapohybrid, discover leerobots.txty obtiene las URLsSitemap:que declara, sondeasitemap.xml(recurriendo en los hijos<sitemapindex>) y extrae páginas de feeds RSS/Atom. Ejecuta estos sondeos tanto contra el host que enviaste como contra el dominio registrable (apex) del sitio, de modo que un sitemap publicado solo en el apex se encuentra igualmente desde una semilla en un subdominio o en una ruta profunda. Un sitemap ausente o roto se convierte en una advertencia, no en un error. - Rastrea por HTTP. En modo
crawlohybrid, discover ejecuta un rastreo en anchura desde la semilla hastamax_depth, recolectando los enlaces<a href>del HTML crudo de cada página obtenida. Cada enlace seguido se filtra contra SSRF antes de solicitarlo. - Normaliza y filtra. La lista combinada en bruto se canonicaliza
(se eliminan los fragmentos y los parámetros de seguimiento, se
quitan los puertos por defecto, se ordenan las claves de consulta),
y luego pasa por la verificación de mismo dominio, tus patrones
regex de inclusión y exclusión, el filtro de
robots.txt, la deduplicación y finalmente el límite demax_urls.
En la ruta solo por HTTP, una aplicación de una sola página (SPA)
renderizada en el cliente devuelve solo su shell HTML en modo crawl,
típicamente la semilla más cero o una URL. El fallback opcional
render_js (consulta renderizado de JavaScript)
cierra esa brecha: en su valor por defecto "auto", discover renderiza
la página una vez en el navegador y recolecta los enlaces que esta
inyecta en tiempo de ejecución siempre que el rastreo por HTTP devuelve
solo un shell. El modo sitemap sigue siendo una ruta rápida y sin
navegador para las SPAs con buen SEO, que normalmente publican un
sitemap.
Parámetros de la solicitud#
Básicos#
| Parámetro | Tipo | Valor por defecto | Descripción |
|---|---|---|---|
url |
string |
ninguno | El sitio a mapear. Debe empezar con http:// o https://. Máximo 2,048 caracteres. Obligatorio. |
mode |
string |
"hybrid" |
sitemap, crawl o hybrid. Consulta Modos. |
max_urls |
integer |
100 |
Número máximo de URLs devueltas. 1–1,000. La lista se limita aquí y truncated indica si existían más. |
max_depth |
integer |
2 |
Profundidad de rastreo desde la semilla, en modo crawl/hybrid. 1–5. |
same_domain_only |
boolean |
true |
Conserva solo las URLs del host de la semilla. Cuando es false, también se conservan los enlaces fuera de ese host descubiertos durante el rastreo. |
render_js |
string |
"auto" |
Descubrimiento con renderizado en el navegador para sitios JavaScript/SPA (solo crawl/hybrid). auto renderiza solo cuando el rastreo por HTTP devuelve un shell; always lo fuerza; never se mantiene solo por HTTP. Consulta renderizado de JavaScript. |
Filtrado#
| Parámetro | Tipo | Valor por defecto | Descripción |
|---|---|---|---|
include_patterns |
string[] |
[] |
Lista de permitidos con regex de Python (semántica re.search, no glob). Una URL debe coincidir con al menos uno para conservarse. Vacío significa permitir todo. Máximo 50 patrones. |
exclude_patterns |
string[] |
[] |
Lista de denegados con regex de Python. Una URL que coincida con cualquier patrón se descarta. Se aplica después de include_patterns. Máximo 50 patrones. |
respect_robots |
boolean |
false |
Cuando es true, las URLs no permitidas por el robots.txt del sitio se eliminan de la lista devuelta. |
Los patrones son expresiones regulares de Python reales, compiladas en
el momento de la validación. Un patrón mal formado da un 422 en el
borde, no un 500 a mitad del rastreo. Como la semántica es
re.search, una subcadena simple como "/blog/" coincide en cualquier
parte de la URL: usa ^/$ como anclas si necesitas una coincidencia
posicional.
Vale la pena aclarar esto de entrada.
respect_robotsse aplica en el momento del filtrado de salida, no en el momento de la solicitud. Crawl4AI 0.8.9 no tiene una validación nativa de robots, así que en modocrawlohybriduna página no permitida puede igualmente obtenerse por HTTP y descartarse después, antes de llegar a la respuesta. A diferencia de perceive, donderespect_robots=truerechaza una URL no permitida con403, discover nunca devuelve403por una regla de robots: descarta la URL en silencio y no reporta nada.
Modos#
mode |
Qué hace |
|---|---|
sitemap |
Entradas de sitemap de robots.txt, sondeo de sitemap.xml (con recursión de índice) y páginas de feeds RSS/Atom. Instantáneo, sin rastreo. |
crawl |
Rastreo en anchura solo por HTTP desde la semilla, recolectando enlaces <a href> del markup crudo. |
hybrid (por defecto) |
La unión sin duplicados de sitemap y crawl. |
El modo crawl solicita hasta tu max_urls completo (hasta 1,000), así
que un sitio grande se enumera en una sola pasada. El modo crawl es solo
por HTTP (sin navegador), así que sigue siendo rápido. Los operadores
pueden bajar el techo con la variable de entorno
DISCOVER_CRAWL_MAX_PAGES si un rastreo muy grande alguna vez necesita
acotarse. pages_crawled en la respuesta te indica exactamente cuántos
GETs se ejecutaron.
La obtención de sitemaps maneja sitemaps comprimidos con gzip
(sitemap.xml.gz y cualquier sitemap servido como application/gzip),
y envía encabezados de navegador realistas, de modo que los sitios
detrás de un WAF tienen muchas más probabilidades de devolver su sitemap
en lugar de un 403/503.
Renderizado de JavaScript#
Los modos crawl y hybrid leen el HTML crudo que devuelve un servidor,
así que una aplicación de una sola página (SPA) renderizada en el cliente
que construye sus enlaces en el navegador es invisible para ellos.
render_js habilita un fallback acotado en el navegador que cierra esa
brecha:
render_js |
Qué hace |
|---|---|
auto (por defecto) |
Ejecuta primero el rastreo por HTTP; renderiza en el navegador solo cuando este devuelve un shell vacío (una URL o ninguna), y luego recolecta los enlaces inyectados por el cliente. |
always |
Ejecuta siempre el rastreo con renderizado en el navegador. |
never |
Se mantiene estrictamente solo por HTTP: el comportamiento preexistente. |
El fallback usa el mismo Chrome headless compartido que
perceive y está limitado a unas pocas páginas
para que la llamada se mantenga dentro del timeout de la solicitud. Los
enlaces que encuentra se contabilizan bajo la clave crawl_js en
sources. Se aplica solo a los modos crawl y hybrid: el modo
sitemap ya es sin navegador y no se ve afectado.
Respuesta#
POST /v2/discover devuelve este objeto directamente: no hay un
trabajo asíncrono ni una segunda llamada.
| Campo | Tipo | Descripción |
|---|---|---|
url |
string |
La URL semilla que enviaste. |
mode |
string |
El modo que se ejecutó: sitemap, crawl o hybrid. |
total |
integer |
Número de URLs en urls (después de la deduplicación, el filtrado y el límite). |
urls |
string[] |
La lista de URLs sin duplicados, normalizada y limitada. La semilla siempre es el primer candidato. |
pages_crawled |
integer |
GETs HTTP emitidos por el rastreo. 0 en modo sitemap puro. |
truncated |
boolean |
true cuando existían más URLs únicas de las que permitía max_urls. |
robots_respected |
boolean |
Refleja el valor de respect_robots que enviaste. |
sources |
object |
Recuento de URLs en bruto por fuente antes de la deduplicación/filtrado, p. ej. {"sitemap": 42, "crawl": 30}. Las claves incluyen sitemap, crawl, sitemap_apex (sitemaps encontrados en el dominio apex) y crawl_js (enlaces del fallback de renderizado de JavaScript). Los recuentos se solapan y suman más que total. |
warnings |
string[] |
Notas no fatales: un sitemap ausente, un rastreo que falló, un robots.txt inaccesible. |
Los recuentos de sources son en bruto: son el número de URLs que
produjo cada ruta antes de que se ejecutaran la normalización, la
verificación de mismo dominio, tus filtros y la deduplicación.
Habitualmente sumarán más que total, porque el modo hybrid
encuentra las mismas páginas tanto desde el sitemap como desde el
rastreo. Úsalos para ver qué ruta está aportando el mapa, no como un
recuento posterior al filtrado.
Ejemplos de código#
curl: mapa hybrid por defecto#
curl -X POST https://api.enconvert.com/v2/discover \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com"
}'
curl: solo sitemap, páginas de blog, limitado a 500#
curl -X POST https://api.enconvert.com/v2/discover \
-H "X-API-Key: sk_your_private_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"mode": "sitemap",
"max_urls": 500,
"include_patterns": ["/blog/"],
"exclude_patterns": ["/tag/", "/author/"],
"respect_robots": true
}'
Python#
import requests
response = requests.post(
"https://api.enconvert.com/v2/discover",
headers={"X-API-Key": "sk_your_private_key"},
json={
"url": "https://example.com",
"mode": "hybrid",
"max_urls": 200,
"include_patterns": [r"/docs/"],
},
)
response.raise_for_status()
data = response.json()
print(f"found {data['total']} URLs from {data['sources']}")
for page_url in data["urls"]:
print(page_url)
Node.js#
const res = await fetch("https://api.enconvert.com/v2/discover", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "sk_your_private_key"
},
body: JSON.stringify({
url: "https://example.com",
mode: "hybrid",
max_urls: 200,
include_patterns: ["/docs/"]
})
});
const data = await res.json();
console.log(`found ${data.total} URLs from`, data.sources);
data.urls.forEach((pageUrl) => console.log(pageUrl));
Un patrón común es primero descubrir y luego renderizar: toma
data.urls y pásalas al endpoint perceive:
llamadas individuales para un puñado de páginas, o su ruta por lotes
(batch) para la lista completa.
Respuestas de error#
| Estado | Condición |
|---|---|
400 Bad Request |
La URL no es http(s), lleva credenciales incrustadas, no tiene hostname, o resuelve a una dirección privada, loopback, link-local o de metadatos de nube (protección SSRF). |
401 Unauthorized |
Falta la clave de API / token JWT o es inválido. |
402 Payment Required |
Discover no está habilitado en tu plan actual, o tu cuota mensual de ops está agotada. Cada llamada a /v2/discover factura una op. |
403 Forbidden |
/v2/discover no está en los endpoints permitidos de la clave de API. |
422 Unprocessable Entity |
Falló la validación de la solicitud: enum mode inválido, max_urls fuera de 1–1,000, max_depth fuera de 1–5, más de 50 patrones de inclusión/exclusión, una regex mal formada, o una url de más de 2,048 caracteres. |
500 Internal Server Error |
El descubrimiento de URLs falló de forma inesperada. El cliente recibe un mensaje genérico; el detalle completo va solo a los logs del servidor. |
Ten en cuenta que un fallo al obtener el sitemap o un error de rastreo
no producen un estado de error: se degradan a entradas en el arreglo
warnings, y la solicitud igual devuelve 200 con lo que se haya
encontrado. La referencia completa de códigos de estado está en
la guía de códigos de error.
Límites#
| Límite | Valor |
|---|---|
| Longitud de la URL | 2,048 caracteres |
max_urls |
1–1,000 (por defecto 100) |
max_depth |
1–5 (por defecto 2) |
include_patterns |
Máximo 50 patrones |
exclude_patterns |
Máximo 50 patrones |
| Páginas solicitadas en modo crawl | hasta max_urls (máx. 1,000; se baja con DISCOVER_CRAWL_MAX_PAGES) |
| Timeout de la solicitud | 300 segundos |
| Ops por llamada | 1, facturada contra la cuota mensual unificada |
Preguntas frecuentes#
¿Cómo listo todas las URLs de un sitio web con una API?#
Envía POST /v2/discover con la URL del sitio. El modo hybrid por defecto combina el análisis del sitemap (entradas de robots.txt, sitemap.xml con recursión de índice, feeds RSS/Atom) con un rastreo en anchura por HTTP, y devuelve hasta max_urls (1–1,000, por defecto 100) URLs sin duplicados en una sola respuesta síncrona.
¿El endpoint discover usa un navegador headless o renderiza JavaScript?#
Por defecto se ejecuta por HTTP y solo renderiza cuando es necesario. La opción render_js controla esto: en el modo por defecto "auto", discover se mantiene solo por HTTP y renderiza una página en el navegador justo cuando el rastreo por HTTP devuelve un shell JavaScript vacío, recolectando los enlaces inyectados por el cliente; "always" fuerza el rastreo en el navegador y "never" lo mantiene estrictamente solo por HTTP. El modo sitemap sigue siendo una opción rápida y sin navegador para las SPAs con buen SEO.
¿Cuántas páginas solicita realmente el modo crawl?#
El modo crawl solicita hasta tu max_urls completo (hasta 1,000). Se mantiene solo por HTTP, así que sigue siendo rápido; los operadores pueden bajar el techo con la variable de entorno DISCOVER_CRAWL_MAX_PAGES. Cada página solicitada aporta además muchos enlaces. El campo pages_crawled de la respuesta indica exactamente cuántos GETs HTTP se ejecutaron.
¿Puedo filtrar qué URLs se devuelven?#
Sí: include_patterns y exclude_patterns aceptan hasta 50 regex de Python cada uno (semántica re.search, no glob), y respect_robots: true elimina de la lista devuelta las URLs no permitidas por el robots.txt del sitio.