Discover

POST /v2/discover enumera las URLs de un sitio sin renderizar una sola página. No hay navegador, ni captura de pantalla, ni PDF, ni artefacto almacenado — solo un rastreo exclusivamente por HTTP más el análisis del sitemap que devuelve una lista plana de URLs y un recuento de la procedencia de cada una. Es la primitiva económica de "mapear el sitio": apúntalo a un dominio, recupera las páginas que vale la pena procesar y luego pasa esa lista al endpoint perceive o a un trabajo de ingesta en modo crawl.

Aquí tienes la llamada útil más pequeña. Envía una URL, recupera su mapa:

curl -X POST https://api.enconvert.com/v2/discover \
  -H "X-API-Key: sk_live_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": []
}

Conviene advertirlo de entrada. /v2/discover es un endpoint de V2, controlado por el flag de plan discover_enabled. A diferencia de la mayoría de los endpoints de V2, no tiene un contador de cuota por operación — es económico (sin navegador, sin almacenamiento), así que no hay un medidor mensual que agotar. Si tu plan no incluye discover de V2, la llamada devuelve 402. Consulta la página de precios para ver qué planes lo incluyen.


Endpoints

Method Path 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 fila de operación que volver a consultar ni trabajo que sondear, así que no hay un endpoint GET complementario como el que tiene perceive.

Content-Type: application/json en el POST.


Autenticación

Autentícate con una clave privada en la cabecera X-API-Key para las llamadas servidor a servidor. Esta es la ruta que usan los ejemplos de abajo.

X-API-Key: sk_live_your_private_key

Las claves públicas con un token bearer JWT 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 token, está en la guía de autenticación.

Cada clave de API lleva una lista de endpoints permitidos. Si /v2/discover no está en la lista de la clave, la solicitud se rechaza con 403 y un mensaje que nombra la ruta bloqueada.


Cómo funciona discover

Una solicitud se ejecuta enteramente por HTTP. El Chrome headless único que impulsa perceive nunca se toca — la ruta de rastreo usa la estrategia de crawler 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 feed exclusivamente por HTTP que el resto de la plataforma.

  1. Filtrar la semilla. La URL que envías se comprueba contra SSRF antes de cualquier fetch: el esquema, las credenciales embebidas, los hostnames bloqueados y la IP resuelta se validan todos. Una URL que resuelve a una dirección privada, de loopback, link-local o de metadatos de la nube se rechaza con 400. La semilla siempre se incluye como la primera entrada de su propio mapa.
  2. Recolectar de los sitemaps. En modo sitemap o hybrid, discover lee robots.txt, sondea sitemap.xml (recursando en los hijos <sitemapindex>) y extrae las páginas de feeds RSS/Atom. Un sitemap ausente o defectuoso se convierte en una advertencia, no en un error.
  3. Rastrear por HTTP. En modo crawl o hybrid, discover ejecuta un rastreo en anchura desde la semilla hasta max_depth, recolectando los enlaces <a href> del HTML crudo de cada página obtenida. Cada enlace seguido se filtra contra SSRF antes de obtenerse.
  4. Normalizar y filtrar. La lista cruda combinada se canonicaliza (se eliminan los fragmentos y los parámetros de seguimiento, se quitan los puertos por defecto, se ordenan las claves de la query), luego se pasa por la comprobación de mismo dominio, tus patrones regex de inclusión y exclusión, el filtro de robots.txt, la deduplicación y, por último, el límite max_urls.

Como no hay renderizado, una single-page app renderizada en el cliente devuelve solo su cascarón HTML en modo crawl — normalmente la semilla más cero o una URL. Ese es el comportamiento correcto de un endpoint sin navegador, no un fallo. Para SPAs, usa el modo sitemap (la mayoría de las SPAs conscientes del SEO publican un sitemap) o recurre a llamadas perceive por URL.


Parámetros de la solicitud

Núcleo

Parameter Type Default Descripción
url string 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 Máximo de URLs devueltas. 1–1.000. La lista se recorta aquí y truncated se marca 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, los enlaces fuera del host descubiertos durante el rastreo también se conservan.

Filtrado

Parameter Type Default Descripción
include_patterns string[] [] Allowlist de regex de Python (semántica de 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[] [] Denylist de 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 descartan de la lista devuelta.

Los patrones son expresiones regulares reales de Python, compiladas en el momento de la validación. Un patrón mal formado es un 422 en el borde, no un 500 a mitad del rastreo. Como la semántica es re.search, una subcadena pelada como "/blog/" coincide en cualquier parte de la URL — ánclala con ^/$ si necesitas una coincidencia posicional.

Conviene advertirlo de entrada. respect_robots se aplica en el momento del filtrado de salida, no en el momento del fetch. Crawl4AI 0.8.9 no tiene una compuerta nativa de robots, así que en modo crawl o hybrid una página no permitida puede aun así obtenerse por HTTP y luego descartarse antes de llegar a la respuesta. A diferencia de perceive, donde respect_robots=true rechaza una URL no permitida con 403, discover nunca devuelve 403 por una regla de robots — descarta la URL en silencio y no informa nada.

Modos

mode Qué hace
sitemap Entradas de sitemap del robots.txt, sitemap.xml sondeado (con recursión de índice) y páginas de feeds RSS/Atom. Instantáneo, sin rastreo.
crawl Rastreo en anchura exclusivamente por HTTP desde la semilla, recolectando enlaces <a href> del marcado crudo.
hybrid (por defecto) La unión deduplicada de sitemap y crawl.

Para ser directos: el modo crawl limita el número de páginas que realmente obtiene a min(max_urls, 50). Un max_urls de 1.000 no emite mil GETs HTTP — discover deja de obtener a las 50 páginas, pero cada página aporta muchos enlaces <a href>, así que la lista devuelta puede aun así alcanzar max_urls. El techo de 50 páginas obtenidas mantiene la solicitud síncrona muy por debajo del timeout de solicitud de 300 segundos. pages_crawled en la respuesta te dice exactamente cuántos GETs se ejecutaron.


Respuesta

POST /v2/discover devuelve este objeto directamente — no hay trabajo asíncrono ni segunda llamada.

Field Type 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 (tras dedup, filtrado y el límite).
urls string[] La lista de URLs deduplicada, normalizada y recortada. La semilla siempre es la primera candidata.
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 max_urls permitía.
robots_respected boolean Refleja el valor respect_robots que enviaste.
sources object Recuento crudo de URLs por fuente antes del dedup/filtro, p. ej. {"sitemap": 42, "crawl": 30}. 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 inalcanzable.

Los recuentos de sources son crudos — son el número de URLs que produjo cada ruta antes de ejecutarse la normalización, la comprobación de mismo dominio, tus filtros y la deduplicación. Rutinariamente 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á cargando el mapa, no como un conteo 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_live_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_live_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_live_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_live_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 habitual es descubrir primero y luego renderizar: toma data.urls y pásalas al endpoint perceive — llamadas individuales para un puñado de páginas, o su ruta batch para la lista entera.


Control por plan

/v2/discover está controlado por el flag de plan discover_enabled y se comporta de forma distinta a los endpoints medidos de V2. Los medidos — perceive entre ellos — consumen cada uno un contador mensual (perceive_operations, lookup_queries, ingest_pages, watch_checks, distill_operations). Discover no tiene tal contador: es exclusivamente HTTP, no almacena nada, así que no hay un medidor por operación que agotar.

Lo que eso significa en la práctica:

  • Si tu plan lleva discover_enabled, puedes llamarlo. No hay una asignación mensual de discover que se pueda agotar.
  • Si tu plan no lo lleva, la llamada devuelve 402 con un aviso de mejora.
  • Los planes de administrador omiten la compuerta por completo.

Qué planes incluyen discover_enabled se establece por nivel; las asignaciones actuales están en la página de precios. Los planes de conversión de V1 no incluyen endpoints de V2 — una llamada a discover nunca consume la cuota de conversión de V1.


Respuestas de error

Status Condición
400 Bad Request La URL no es http(s), lleva credenciales embebidas, no tiene hostname o resuelve a una dirección privada, de loopback, link-local o de metadatos de la nube (protección SSRF).
401 Unauthorized Clave de API / token JWT ausente o inválido.
402 Payment Required Discover no está habilitado en tu plan actual.
403 Forbidden /v2/discover no está entre los endpoints permitidos de la clave de API.
422 Unprocessable Entity La validación de la solicitud falló: enum mode incorrecto, max_urls fuera de 1–1.000, max_depth fuera de 1–5, más de 50 patrones de inclusión/exclusión, un regex mal formado o una url de más de 2.048 caracteres.
500 Internal Server Error El descubrimiento de URLs falló inesperadamente. El cliente recibe un mensaje genérico; el detalle completo va solo a los logs del servidor.

Ten en cuenta que un fetch de sitemap fallido o un fallo de rastreo no produce un estado de error — esos se degradan a entradas en el array warnings, y la solicitud aun así 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 URL 2.048 caracteres
max_urls 1–1.000 (por defecto 100)
max_depth 1–5 (por defecto 2)
include_patterns 50 patrones máximo
exclude_patterns 50 patrones máximo
Páginas obtenidas en modo crawl 50 (min(max_urls, 50))
Timeout de solicitud 300 segundos
Cuota por operación Ninguna — sin contador mensual de discover