Perceive

POST /v2/perceive renderiza una URL una sola vez en un navegador real y devuelve todas las salidas que solicites a partir de ese único renderizado: Markdown, HTML limpio o crudo, una captura de pantalla, un PDF, el inventario de enlaces e imágenes, y datos estructurados (metadatos de la página, JSON-LD, encabezados, tablas). Reemplaza un conjunto de llamadas separadas — url-to-markdown, url-to-screenshot, url-to-pdf, además de tu propio scraping — con una sola solicitud.

Aquí tienes la llamada útil más pequeña. Envía una URL y recibe Markdown limpio y los metadatos estructurados de la página:

curl -X POST https://api.enconvert.com/v2/perceive \
  -H "X-API-Key: sk_live_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/pricing",
    "outputs": ["markdown", "structured"]
  }'

La respuesta incluye una URL de descarga prefirmada para el archivo Markdown y el bloque estructurado en línea:

{
    "operation_id": "per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7",
    "status": "completed",
    "url": "https://example.com/pricing",
    "url_final": "https://example.com/pricing",
    "content_hash": "9f2b8c1a...d4e5",
    "render_quality": 0.93,
    "cache_hit": false,
    "outputs": {
        "markdown": {
            "url": "https://spaces.example.com/...signed...",
            "object_key": "env/files/4127/v2-perceive/per_3f9a..._markdown.md",
            "size_bytes": 8421,
            "content_type": "text/markdown; charset=utf-8",
            "expires_in": 900
        }
    },
    "structured": {
        "metadata": {
            "title": "Pricing",
            "description": "Simple, usage-based pricing."
        },
        "structured_data": [
            {"@type": "Product", "name": "Pro", "offers": {"price": "99"}}
        ]
    },
    "extraction_tier": "heuristic",
    "tokens": {"input": 0, "output": 0},
    "cost_cents": 0.0,
    "duration_ms": 6230,
    "warnings": []
}

Conviene señalarlo de entrada. /v2/perceive es un endpoint V2 con su propio contador de cuota. Los planes de conversión V1 no incluyen ninguna operación perceive de V2 — necesitas un plan que incluya V2 para llamarlo. Consulta la página de precios para ver las asignaciones por nivel.


Endpoints

Method Path Purpose
POST /v2/perceive Percibe una sola URL y devuelve las salidas que solicitaste.
GET /v2/perceive/{operation_id} Vuelve a obtener una operación pasada con URLs de descarga recién firmadas.
POST /v2/perceive/batch Percibe hasta 1.000 URLs que comparten un mismo conjunto de opciones.
GET /v2/perceive/batch/{job_id} Consulta el estado y los resultados por URL de un lote.

Content-Type: application/json en cada POST.


Authentication

Autentícate con una clave privada en el encabezado X-API-Key para llamadas servidor a servidor. Esta es la vía 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 todos los demás endpoints — genera un token con tu clave pk_ y luego envíalo como Authorization: Bearer <token>. El flujo completo, incluyendo el bloqueo de dominio y la renovación del token, está en la guía de autenticación.

Cada clave API lleva una lista blanca de endpoints permitidos. Si /v2/perceive no está en la lista de la clave, la solicitud se rechaza con 403.


How perceive works

Una solicitud desencadena un renderizado del navegador a través de un singleton compartido de Chrome headless, y luego materializa cada salida a partir de ese renderizado. Nunca pagas dos veces por la misma página en una sola llamada.

  1. Renderizar. La página se carga en Chrome headless. Se descartan los banners de cookies, se desplaza la página para activar el contenido de carga diferida, se gestionan los encabezados fijos y se da tiempo a que las imágenes carguen — el mismo pipeline de captura que impulsa el endpoint url-to-pdf.
  2. Materializar. A partir del DOM renderizado, perceive construye lo que hayas listado en outputs: Markdown, HTML limpio/crudo, enlaces, imágenes, una captura de pantalla, un PDF.
  3. Extraer. Si solicitaste la salida structured, perceive ejecuta una pasada heurística para los metadatos de la página, JSON-LD, encabezados y tablas. Si además envías un schema y tu plan incluye el nivel LLM, un modelo Anthropic Claude Haiku rellena el esquema cuando la pasada heurística se queda corta.
  4. Puntuar. Una puntuación de calidad del renderizado (0.0–1.0) marca las páginas que parecen bloqueadas por protección anti-bot u ocultas tras un muro de inicio de sesión, para que puedas distinguir un renderizado real de una página de desafío.

Las salidas binarias y de texto (Markdown, HTML, capturas de pantalla, PDFs, el JSON de enlaces e imágenes) se suben al almacenamiento y se devuelven como URLs prefirmadas que expiran tras 15 minutos. El bloque structured se devuelve en línea en el JSON. Vuelve a obtener cualquier operación con GET /v2/perceive/{operation_id} para conseguir un nuevo conjunto de URLs firmadas.


Request parameters

Core

Parameter Type Default Description
url string La página a percibir. Debe empezar por http:// o https://. Máx. 2.048 caracteres. Obligatorio.
outputs string[] ["markdown", "structured"] Qué salidas producir. Consulta Outputs.
extract string[] [] Qué campos estructurados extraer cuando structured está en outputs. Consulta Structured extraction.
schema object null Un esquema JSON que describe los campos que quieres extraer. Activa el nivel de extracción LLM en los planes que lo incluyen.
cache_mode string "enabled" enabled, bypass o refresh. Consulta Caching.

Outputs

outputs acepta cualquier combinación de estos nombres:

Output Returned as What you get
markdown URL firmada Markdown limpio del contenido principal, con enlaces e imágenes resueltos a URLs absolutas.
markdown_fit URL firmada Markdown "Fit" — una variante podada y más densa, ajustada para las ventanas de contexto de LLM.
html_cleaned URL firmada El HTML renderizado con los scripts, estilos y código repetitivo eliminados.
html_raw URL firmada El HTML renderizado completo, exactamente como lo produjo el navegador.
screenshot URL firmada Un PNG del viewport al tamaño de viewport solicitado (o predeterminado).
screenshot_full_page URL firmada Un PNG de página completa que captura toda la altura de desplazamiento.
pdf URL firmada Un PDF de la página. Acepta toda la superficie de pdf_options (ver más abajo).
links URL firmada Un array JSON de cada enlace encontrado, con URLs absolutas y el texto del ancla.
images URL firmada Un array JSON de cada imagen, con src absoluto y texto alt.
structured JSON en línea Datos estructurados extraídos de la página (el campo de respuesta structured).

Rendering and waiting

Parameter Type Default Description
viewport object 1920 x 1080 {"width": <int>, "height": <int>}. Ancho 320–3840, alto 240–2160.
mobile boolean false Renderiza en un viewport móvil (390 x 844) salvo que se establezca viewport explícitamente.
wait_for string null Espera tras la navegación a un selector CSS (".price" o "css:.price") o a una expresión JS ("js:window.dataReady === true").
wait_timeout_ms integer 30000 Cuánto tiempo puede esperar wait_for, en milisegundos. 0–60.000. Un timeout se degrada a una advertencia; la página se captura tal cual.
js_code string null JavaScript para ejecutar en la página tras la navegación. Máx. 20.000 caracteres. Un error se convierte en advertencia, no en fallo.
block_resources string[] [] Tipos de recursos a abortar antes de que carguen. Cualquiera de image, media, font, stylesheet, script, xhr, fetch, websocket, manifest, other. Útil para renderizados más rápidos, solo de texto.
respect_robots boolean false Cuando es true, una URL no permitida por el robots.txt del sitio se rechaza con 403.
pdf_options object null Formato de página, márgenes, encabezados, pies de página, escala y orientación para la salida pdf. El mismo objeto que url-to-pdf. Sin pdf_options, perceive produce una única página continua, idéntica byte a byte a url-to-pdf de V1.

Authenticated and custom requests

Parameter Type Default Description
auth object null HTTP Basic Auth para la página de destino: {"username": "...", "password": "..."}.
cookies array null Cookies a inyectar antes de la navegación. Máx. 50. Cada una necesita name, value y domain o url.
headers object null Encabezados de solicitud personalizados. Máx. 20. Nombres bloqueados: host, content-length, transfer-encoding, connection, upgrade, te, trailer.
Reservados, aún no activos. proxy_url (Business+), geolocation y action_chain son aceptados por el esquema de la solicitud pero hoy devuelven 422. Llegarán en una versión posterior; enviarlos ahora te indica exactamente qué control no está listo en lugar de ignorarlo en silencio.

Structured extraction

Cuando structured está en outputs, la lista extract controla qué campos extrae perceive. No pidas nada y por defecto será metadata y structured_data.

extract value Field in structured Status
metadata metadata Activo
structured_data structured_data (JSON-LD) Activo
headings headings Activo
tables tables Activo
main_content main_content (texto, limitado a 50.000 caracteres) Activo
all se expande a todos los campos activos anteriores Activo
prices Aún no — devuelve una advertencia, se omite
contacts Aún no — devuelve una advertencia, se omite
technologies Aún no — devuelve una advertencia, se omite

Para ser claros: prices, contacts y technologies son nombres reservados. Solicita uno hoy y no da error — aterriza en el array warnings y se descarta de structured.

Schema-driven extraction

Envía un schema para extraer campos específicos a structured.extracted:

{
    "url": "https://example.com/product/widget",
    "outputs": ["markdown", "structured"],
    "schema": {
        "type": "object",
        "properties": {
            "name": {"type": "string"},
            "price": {"type": "number"},
            "in_stock": {"type": "boolean"}
        }
    }
}

El nivel de extracción LLM (Anthropic Claude Haiku) rellena el esquema, y solo se dispara cuando se cumplen todas estas condiciones: enviaste un schema, tu plan incluye el nivel LLM (Starter en adelante), la página no se puntuó como bloqueada y la pasada heurística dejó vacíos los campos del esquema. Cuando se ejecuta, extraction_tier es "llm", y tokens y cost_cents informan de lo que costó esa extracción; en caso contrario extraction_tier es "heuristic" y ambos son cero.

Nota. La extracción por esquema tiene un tope estricto para proteger tu factura: una sola extracción está limitada por solicitud, y el gasto diario por proyecto está limitado por el plan. Si se alcanza un tope, perceive devuelve el resultado heurístico con una nota en warnings en lugar de gastar de más. En un plan sin el nivel LLM, solo obtienes datos structured heurísticos.


Response

Tanto POST /v2/perceive como GET /v2/perceive/{operation_id} devuelven el mismo objeto.

Field Type Description
operation_id string ID opaco (per_...). Úsalo con el endpoint GET y cítalo al soporte.
status string queued, processing, completed o failed.
url string La URL que enviaste.
url_final string La URL tras las redirecciones.
content_hash string SHA-256 de la página renderizada. Impulsa la caché de 1 hora.
render_quality number 0.0–1.0. Las puntuaciones bajas marcan desafíos anti-bot o muros de inicio de sesión.
cache_hit boolean true cuando el resultado provino de la caché en lugar de un renderizado nuevo.
outputs object Mapa de nombre de salida a {url, object_key, size_bytes, content_type, expires_in}. Las URLs firmadas expiran en 900 segundos.
structured object Datos estructurados en línea, presentes cuando se solicitó structured.
extraction_tier string heuristic, css o llm.
tokens object {input, output} tokens LLM utilizados. Cero salvo que se ejecutara el nivel LLM.
cost_cents number Coste LLM en céntimos para esta operación. Cero salvo que se ejecutara el nivel LLM.
duration_ms integer Tiempo de renderizado de extremo a extremo.
error string Establecido solo cuando status es failed.
warnings string[] Notas no fatales: un timeout de wait_for, un extract omitido, una marca de página bloqueada.

Retrieve an operation

Las URLs firmadas expiran tras 15 minutos. Para descargar una salida más tarde, vuelve a obtener la operación — perceive vuelve a firmar cada URL a partir de las claves de objeto almacenadas. No ocurre ningún re-renderizado, así que esto no consume cuota.

curl https://api.enconvert.com/v2/perceive/per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7 \
  -H "X-API-Key: sk_live_your_private_key"

Un ID de operación desconocido, o uno que pertenece a un proyecto distinto, devuelve 404 — la existencia nunca se filtra entre proyectos.


Batch perception

POST /v2/perceive/batch percibe una lista de URLs que comparten un mismo bloque options. Cada URL se renderiza a través del mismo pipeline que una llamada individual y produce su propia fila de operación.

curl -X POST https://api.enconvert.com/v2/perceive/batch \
  -H "X-API-Key: sk_live_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": [
      "https://example.com/a",
      "https://example.com/b",
      "https://example.com/c"
    ],
    "options": {"outputs": ["markdown"]},
    "output_mode": "manifest"
  }'

Los lotes de 10 URLs o menos se ejecutan en línea y responden 200 con cada resultado poblado. Los lotes más grandes responden 202 con un job_id; las URLs se drenan de una en una y consultas los resultados:

curl https://api.enconvert.com/v2/perceive/batch/{job_id} \
  -H "X-API-Key: sk_live_your_private_key"

La respuesta del lote informa del progreso agregado y lleva un resultado perceive completo por URL una vez renderizada:

{
    "job_id": "bat_8c1a...",
    "status": "partial",
    "output_mode": "manifest",
    "total": 3,
    "completed": 2,
    "failed": 1,
    "pending": 0,
    "items": [
        {"operation_id": "per_...", "status": "completed", "url": "https://example.com/a"},
        {"operation_id": "per_...", "status": "completed", "url": "https://example.com/b"},
        {"operation_id": "per_...", "status": "failed", "url": "https://example.com/c"}
    ]
}

status es queued, processing, completed, failed o partial (algunas URLs tuvieron éxito, otras fallaron). Establece output_mode en zip para agrupar todos los artefactos en un único ZIP, devuelto bajo el campo zip una vez que el lote termina. El acceso a lotes y el tamaño máximo de lote están limitados por plan; consulta la página de precios.


Caching

cache_mode controla cómo perceive trata su caché de resultados de 1 hora, indexada por tu proyecto, la URL y las opciones de solicitud que afectan al renderizado.

cache_mode Behaviour
enabled (default) Devuelve un resultado en caché cuando una solicitud idéntica se renderizó en la última hora. cache_hit es true, cost_cents es 0.
bypass Omite la caché y renderiza de nuevo.
refresh Renderiza de nuevo y reemplaza la entrada en caché.

Conviene señalarlo: un acierto de caché sigue contando como una operación perceive contra tu cuota mensual. La cuota mide operaciones, no renderizados del navegador — la caché te ahorra tiempo de renderizado, no cuota.


Code examples

curl — Markdown only

curl -X POST https://api.enconvert.com/v2/perceive \
  -H "X-API-Key: sk_live_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/blog/post",
    "outputs": ["markdown"]
  }'

curl — Markdown plus structured data

curl -X POST https://api.enconvert.com/v2/perceive \
  -H "X-API-Key: sk_live_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/pricing",
    "outputs": ["markdown", "structured"],
    "extract": ["metadata", "structured_data", "tables"]
  }'

curl — Full outputs plus PDF

curl -X POST https://api.enconvert.com/v2/perceive \
  -H "X-API-Key: sk_live_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/report",
    "outputs": ["markdown", "html_cleaned", "screenshot_full_page", "pdf"],
    "pdf_options": {"format": "A4", "print_background": true}
  }'

Python

import requests

response = requests.post(
    "https://api.enconvert.com/v2/perceive",
    headers={"X-API-Key": "sk_live_your_private_key"},
    json={
        "url": "https://example.com/pricing",
        "outputs": ["markdown", "structured"],
        "extract": ["metadata", "tables"],
    },
)
response.raise_for_status()
data = response.json()

# Download the Markdown artifact from its signed URL
markdown_url = data["outputs"]["markdown"]["url"]
markdown_text = requests.get(markdown_url).text

print(data["structured"])
print(markdown_text)

Node.js

const res = await fetch("https://api.enconvert.com/v2/perceive", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-API-Key": "sk_live_your_private_key"
    },
    body: JSON.stringify({
        url: "https://example.com/pricing",
        outputs: ["markdown", "structured"],
        extract: ["metadata", "tables"]
    })
});

const data = await res.json();

// Download the Markdown artifact from its signed URL
const markdownText = await fetch(data.outputs.markdown.url).then(r => r.text());

console.log(data.structured);
console.log(markdownText);

Si llamas a EnConvert desde Claude, Cursor u otro cliente MCP, la misma capacidad se expone como la herramienta perceive_url — consulta la página del servidor MCP.


Plan gating

Las operaciones perceive de V2 son un contador separado de las conversiones de V1. Los planes V1 no incluyen cuota de perceive; necesitas un plan que incluya V2.

Plan Perceive operations / month Schema extraction (LLM tier)
Free 50 No incluido
Starter 850 Claude Haiku
Pro 8,500 Claude Haiku
Business 42,500 Claude Haiku
Enterprise Ilimitado Incluido

El acceso a lotes y los límites de tamaño por lote, el soporte de basic-auth/cookie/header y proxy_url (Business+, cuando se lance) están limitados por plan. Las cifras actuales están en la página de precios.


Error responses

Status Condition
400 Bad Request La URL no es http(s), lleva credenciales incrustadas, o se resuelve a una dirección privada, de loopback o de enlace local (protección SSRF).
400 Bad Request auth inválido (falta username/password), cookies (no es un array, más de 50 entradas, faltan campos) o headers (no es un objeto, más de 20 entradas, nombre bloqueado).
401 Unauthorized Clave API / token JWT ausente o inválido.
402 Payment Required Perceive no está en tu plan actual, o tu cuota mensual de perceive se agotó.
403 Forbidden /v2/perceive no está en los endpoints permitidos de la clave API.
403 Forbidden El lote no está disponible en tu plan, o el tamaño del lote supera el límite de tu plan.
403 Forbidden respect_robots=true y el robots.txt del sitio no permite la URL.
404 Not Found operation_id o job_id desconocido, o uno propiedad de otro proyecto.
422 Unprocessable Entity Falló la validación de la solicitud (enum incorrecto en outputs/extract, wait_timeout_ms fuera de rango, viewport fuera de los límites).
422 Unprocessable Entity Se envió proxy_url, geolocation o action_chain — reservados para una versión posterior.
500 Internal Server Error Falló el renderizado. El mensaje incluye el operation_id para citar al soporte.

La referencia completa de códigos de estado está en la guía de códigos de error.


Limits

Limit Value
Longitud de URL 2.048 caracteres
wait_timeout_ms 0–60.000 ms
Longitud de js_code 20.000 caracteres
Ancho del viewport 320–3.840 px
Alto del viewport 240–2.160 px
Cookies por solicitud 50
Encabezados personalizados por solicitud 20
Extract de main_content 50.000 caracteres
URLs por solicitud de lote 1.000 (tope del esquema; el límite de lote de tu plan es menor)
Umbral de lote en línea 10 URLs (los lotes más grandes se ejecutan de forma asíncrona)
TTL de la caché de resultados 1 hora
Expiración de URL firmada 15 minutos
Operaciones perceive mensuales Dependiente del plan (Free: 50)