API de Búsqueda Web para Agentes LLM#

Beta privada. Lookup ya se puede llamar hoy con tu clave de API habitual, en cualquier plan incluido Founding, y descuenta de tu cuota mensual de ops igual que cualquier otra llamada. No está anunciado ni disponible de forma general: las formas de la solicitud y de la respuesta pueden cambiar sin avisar, y no hay ningún compromiso de estabilidad ni de soporte, así que todavía no montes nada crítico encima. La hoja de ruta está en Próximamente, y cada versión se anuncia en el changelog.

POST /v2/lookup es una API de búsqueda web construida para agentes LLM: ejecuta una búsqueda en una de seis categorías y devuelve una lista de resultados plana y neutral respecto al proveedor. Define perceive_top y también renderiza las URLs de los N primeros resultados en un navegador real, de modo que un agente obtiene la página de resultados del motor de búsqueda (SERP) y el contenido de la página detrás de cada resultado en un único viaje de ida y vuelta. Como alternativa a una API SERP, colapsa la pila habitual (llamar a una API de búsqueda, analizar sus resultados y luego lanzar un scraper) en una sola llamada. Será la respuesta de EnConvert a /search de Firecrawl.

Serper es el proveedor de búsqueda que está detrás. La solicitud y la respuesta hablan un vocabulario de búsqueda neutral (category, country, locale, time_filter), de modo que un futuro cambio de proveedor no altera el contrato contra el que programas.

Esta es la llamada útil más pequeña. Envía una consulta y obtén de vuelta los mejores resultados web:

curl -X POST https://api.enconvert.com/v2/lookup \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "headless chrome pdf rendering"
  }'

La respuesta es una lista de resultados plana más procedencia para la correlación con soporte:

{
    "lookup_id": 81423,
    "query": "headless chrome pdf rendering",
    "category": "web",
    "total": 10,
    "results": [
        {
            "title": "Generate PDFs with headless Chrome",
            "url": "https://example.com/guide/chrome-pdf",
            "snippet": "Render a page and print it to PDF...",
            "position": 1
        },
        {
            "title": "Print to PDF with the Chrome DevTools Protocol",
            "url": "https://example.dev/cdp/print-to-pdf",
            "snippet": "Page.printToPDF returns base64 PDF data...",
            "position": 2
        }
    ],
    "perceive_top": 0,
    "perceive_operation_ids": [],
    "credits": 1,
    "cost_cents": 0.06,
    "warnings": []
}

Endpoints#

Método Ruta Propósito
POST /v2/lookup Ejecuta una búsqueda y, opcionalmente, auto-percibe las URLs de los N primeros resultados.

/v2/lookup es un endpoint de llamada única, sin una ruta separada de estado o de recuperación. Cuando auto-percibes, cada página renderizada se convierte en una operación de percepción de primer nivel con su propio operation_id, que puedes volver a obtener más tarde mediante GET /v2/perceive/{operation_id}.

Content-Type: application/json.


Autenticación#

Autentícate con una clave privada en el encabezado X-API-Key para llamadas de servidor a servidor. Este es el camino que usan los ejemplos siguientes.

X-API-Key: sk_your_private_key

Las claves públicas con un token bearer JWT también funcionan, usando el mismo flujo que cualquier otro endpoint: 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 de API lleva una lista blanca de endpoints permitidos. Si /v2/lookup no está en la lista de la clave, la solicitud se rechaza con 403.


Cómo funciona la búsqueda#

Una solicitud ejecuta una búsqueda del proveedor y, solo si lo pides, renderiza después los mejores resultados.

  1. Verificación de cuota. Antes de que se facture nada, el handler comprueba la cuota mensual unificada de ops de tu plan. Un plan deshabilitado o una cuota agotada se rechaza con 402, de modo que no se cobra nada ante un rechazo.
  2. Búsqueda. La consulta va al proveedor de búsqueda (Serper) en el endpoint correspondiente a tu category. La actualidad, el país, la configuración regional, la ubicación, el tamaño de página y la autocorrección se mapean a los parámetros del proveedor.
  3. Normalización. Cada resultado del proveedor se aplana en un LookupResult neutral que lleva title, url, snippet y position. Los extras específicos de la categoría van a parar a extra, de modo que el contrato nunca crece con una columna por cada particularidad del proveedor.
  4. Cobro y auditoría. La búsqueda tuvo éxito, así que se factura una op y se escribe una fila de auditoría ch_lookup_queries. El id de la fila vuelve como lookup_id para la correlación con soporte.
  5. Auto-percepción (opcional). Si perceive_top > 0, las URLs de los N primeros resultados se renderizan una a una a través del singleton compartido de Chrome headless, el mismo pipeline que el endpoint de percepción. Cada renderizado es una operación /v2/perceive completa: su propia op facturada contra la cuota compartida, su propia fila de operación, su propio operation_id. De forma predeterminada, la auto-percepción solo solicita Markdown, sin captura de pantalla, PDF ni extracción con LLM; envía un objeto enrich para ampliar las salidas, ejecutarlas en paralelo, o añadir extracción por esquema y una respuesta sintetizada.

La auto-percepción es de mejor esfuerzo. Una única URL que falle, o que la cuota de ops se agote a mitad de camino, degrada a una advertencia y aun así devuelve los resultados de la búsqueda. El SERP es el producto principal aquí, así que un problema de percepción nunca hunde toda la llamada.


Parámetros de la solicitud#

Consulta y categoría#

Parámetro Tipo Predeterminado Descripción
query string ninguno La consulta de búsqueda. 1–512 caracteres, recortada de espacios en blanco al inicio/final. Una consulta que quede vacía tras el recorte se rechaza con 422. Obligatorio.
category string "web" Uno de web, news, images, scholar, patents, maps.

Segmentación y actualidad#

Parámetro Tipo Predeterminado Descripción
country string null Código de país gl de Google, p. ej. us, in. Máximo 8 caracteres.
locale string null Idioma de interfaz hl de Google, p. ej. en. Máximo 16 caracteres.
time_filter string null Restringe a resultados del período pasado: hour, day, week, month, o year.
location string null Cadena de ubicación en texto libre, p. ej. "Austin, Texas". Máximo 128 caracteres.
autocorrect boolean true Si el proveedor puede autocorregir la ortografía de la consulta.

Paginación#

Parámetro Tipo Predeterminado Descripción
num_results integer 10 Resultados por página. 1–100.
page integer 1 Número de página. 1–10.

Auto-percepción#

Parámetro Tipo Predeterminado Descripción
perceive_top integer 0 Auto-percibe las URLs de los primeros N resultados que tengan un enlace navegable. 0–10. Cada una es un renderizado completo del navegador que factura una op de tu cuota mensual, por eso está limitado a 10. Para conjuntos más grandes, toma los campos url y llama a el endpoint de percepción por lotes. 0 deshabilita la auto-percepción.

Enriquecimiento (enrich)#

Un objeto enrich opcional ajusta cómo se leen los N primeros resultados (perceive_top), y puede sintetizar una única respuesta fundamentada a partir de ellos. Cuando se omite enrich, perceive_top conserva su comportamiento predeterminado (solo Markdown, un resultado a la vez).

Parámetro Tipo Predeterminado Descripción
enrich.outputs string[] ["markdown"] Qué outputs de perceive producir por cada resultado enriquecido, p. ej. markdown, html_cleaned, links, screenshot, structured. Consulta los outputs de perceive.
enrich.concurrency integer 3 Cuántas URLs de resultados enriquecer en paralelo. 1–5. Los renderizados de Markdown/HTML se paralelizan; los de captura de pantalla/PDF se serializan en el navegador compartido.
enrich.schema object null Ejecuta la extracción estructurada guiada por schema contra cada resultado enriquecido. Los datos extraídos aparecen bajo el perceive.structured de cada resultado. Un objeto JSON-Schema o un mapa plano {field: description}.
enrich.synthesize_answer boolean false Sintetiza una única respuesta citada y fundamentada a la consulta a partir de los resultados enriquecidos, devuelta como answer (con answer_sources). Usa el contenido percibido de la página cuando está disponible; en caso contrario, los fragmentos de los resultados.
enrich.answer_prompt string null Una pregunta a responder en lugar de la consulta original. Solo se usa cuando synthesize_answer es true. Máximo 1,000 caracteres.

enrich.schema y enrich.synthesize_answer usan el nivel de extracción LLM. Si ese paso de extracción no se puede ejecutar, se degradan a una advertencia y el resto de la respuesta no se ve afectado.

{
  "query": "best open-source vector databases",
  "perceive_top": 3,
  "enrich": {
    "outputs": ["markdown"],
    "concurrency": 3,
    "synthesize_answer": true
  }
}

Categorías#

Cada categoría llama a un endpoint distinto del proveedor y expone una forma de resultado ligeramente diferente. Los campos universales (title, url, snippet, position) siempre están tipados; los campos específicos de la categoría van a parar a extra.

category Qué busca Campos destacados poblados
web Resultados web generales title, url, snippet, date, position
news Artículos de noticias añade source, image_url
images Resultados de imágenes image_url, thumbnail_url, source (a menudo sin snippet)
scholar Resultados académicos misma forma que web; conteos de citas en extra
patents Resultados de patentes misma forma que web; campos de patentes en extra
maps Lugares locales url es el sitio web del lugar; snippet lleva la dirección; calificación, coordenadas en extra

Vale la pena señalarlo: para images y maps, url puede ser null para un resultado dado cuando el proveedor no devuelve ningún enlace navegable. La auto-percepción omite cualquier resultado cuyo url sea null, así que un perceive_top de 5 en un SERP con dos resultados sin URL percibe como máximo tres páginas.


Respuesta#

El endpoint omite los campos null, de modo que un resultado web mínimo lleva solo los campos que están realmente poblados.

Campo Tipo Descripción
lookup_id integer El id de la fila de auditoría ch_lookup_queries. Cítalo a soporte. null si falló la escritura de auditoría, aunque los resultados siguen siendo válidos.
query string La consulta (recortada) que enviaste.
category string La categoría buscada.
country string Eco del country que enviaste, si lo hubo.
locale string Eco del locale que enviaste, si lo hubo.
time_filter string Eco del time_filter que enviaste, si lo hubo.
total integer Número de resultados devueltos.
results LookupResult[] La lista de resultados. Ver más abajo.
perceive_top integer Cuántos resultados fueron realmente percibidos: como máximo el valor que solicitaste, y menor si se agotó la cuota de ops o fallaron URLs.
perceive_operation_ids string[] Los ids de operación per_... de los resultados percibidos, en orden.
answer_box object El cuadro de respuesta del proveedor, cuando está presente.
knowledge_graph object El panel de grafo de conocimiento del proveedor, cuando está presente.
answer string La respuesta citada y sintetizada a partir de los resultados enriquecidos. Presente solo cuando enrich.synthesize_answer es true y tuvo éxito.
answer_sources string[] Las URLs usadas como fundamento para answer, en orden de citación.
credits integer Créditos del proveedor consumidos por esta consulta.
cost_cents number Costo monetario de la búsqueda en centavos. Un valor fijo de 0.06 por consulta hoy.
warnings string[] Notas no fatales: un resultado sin URL omitido, un fallo de auto-percepción, la cuota de ops agotándose a mitad de bucle.

LookupResult#

Campo Tipo Descripción
title string Título del resultado.
url string Enlace canónico de la página, lo que percibirías. null para resultados sin URL navegable.
snippet string Fragmento del resultado. Para maps, lleva la dirección.
position integer La posición del resultado en el SERP.
source string Fuente/editor, para news e images.
date string Fecha de publicación, cuando el proveedor la reporta.
image_url string URL de la imagen, para images y news.
thumbnail_url string URL de la miniatura, para images.
extra object Campos específicos de la categoría que no están en el conjunto neutral: calificaciones, coordenadas, conteos de citas, etc.
perceive PerceiveResponse El resultado de percepción completo en línea para esta URL, presente solo para los N primeros cuando perceive_top > 0 y el renderizado tuvo éxito. Misma forma de objeto que el endpoint de percepción.

Cómo leer los resultados de auto-percepción#

Cuando envías perceive_top, recorre los resultados y comprueba el campo perceive. Está presente solo en los resultados que fueron percibidos, y solo cuando su renderizado tuvo éxito. El Markdown de cada uno vive detrás de una URL de descarga pre-firmada (un enlace firmado y de corta duración al almacenamiento de objetos) en perceive.outputs.markdown.url, igual que en una llamada directa a perceive.

{
    "lookup_id": 81910,
    "query": "react server components data fetching",
    "category": "web",
    "total": 10,
    "results": [
        {
            "title": "Data fetching with RSC",
            "url": "https://example.com/rsc/data",
            "snippet": "Fetch on the server, stream to the client...",
            "position": 1,
            "perceive": {
                "operation_id": "per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7",
                "status": "completed",
                "url": "https://example.com/rsc/data",
                "outputs": {
                    "markdown": {
                        "url": "https://spaces.example.com/...signed...",
                        "size_bytes": 7421,
                        "content_type": "text/markdown; charset=utf-8",
                        "expires_in": 900
                    }
                },
                "cost_cents": 0.0,
                "duration_ms": 5840
            }
        }
    ],
    "perceive_top": 1,
    "perceive_operation_ids": [
        "per_3f9a2c1b8e7d4a6f90b1c2d3e4f5a6b7"
    ],
    "credits": 1,
    "cost_cents": 0.06,
    "warnings": []
}

Esas URLs firmadas expiran después de 15 minutos. Para descargar una página percibida más tarde, vuelve a obtener su operación con GET /v2/perceive/{operation_id} usando el id de perceive_operation_ids. Eso vuelve a firmar las URLs y no vuelve a renderizar, así que no cuesta ops. Consulta la sección de recuperación de perceive para más detalles.


Ejemplos de código#

curl: búsqueda web#

curl -X POST https://api.enconvert.com/v2/lookup \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "open source vector database",
    "num_results": 20
  }'

curl: noticias recientes, localizadas#

curl -X POST https://api.enconvert.com/v2/lookup \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "rbi monetary policy",
    "category": "news",
    "country": "in",
    "locale": "en",
    "time_filter": "week"
  }'

curl: búsqueda más auto-percepción de los 3 primeros#

curl -X POST https://api.enconvert.com/v2/lookup \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "langchain retrieval augmented generation",
    "perceive_top": 3
  }'

Python#

import requests

response = requests.post(
    "https://api.enconvert.com/v2/lookup",
    headers={"X-API-Key": "sk_your_private_key"},
    json={
        "query": "langchain retrieval augmented generation",
        "perceive_top": 3,
    },
)
response.raise_for_status()
data = response.json()

# Pull the Markdown of every result that was perceived
for result in data["results"]:
    perceived = result.get("perceive")
    if not perceived:
        continue
    markdown_url = perceived["outputs"]["markdown"]["url"]
    page_text = requests.get(markdown_url).text
    print(result["url"], len(page_text), "chars")

for note in data["warnings"]:
    print("warning:", note)

Node.js#

const res = await fetch("https://api.enconvert.com/v2/lookup", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-API-Key": "sk_your_private_key"
    },
    body: JSON.stringify({
        query: "langchain retrieval augmented generation",
        perceive_top: 3
    })
});

const data = await res.json();

// Pull the Markdown of every result that was perceived
for (const result of data.results) {
    if (!result.perceive) continue;
    const markdownUrl = result.perceive.outputs.markdown.url;
    const pageText = await fetch(markdownUrl).then(r => r.text());
    console.log(result.url, pageText.length, "chars");
}

for (const note of data.warnings) {
    console.log("warning:", note);
}

Si llamas a EnConvert desde Claude, Cursor u otro cliente de Model Context Protocol (MCP), la capacidad de búsqueda también se expondrá allí como una herramienta. Consulta la página del servidor MCP.


Respuestas de error#

El handler nunca devuelve texto crudo del proveedor al cliente. El detalle del proveedor y de SSRF se queda en los logs del servidor, y el cliente recibe un mensaje limpio y genérico.

Estado Condición
401 Unauthorized Falta la clave de API / token JWT, o es inválida.
402 Payment Required Lookup no está en tu plan actual, o tu cuota mensual de ops está agotada.
403 Forbidden /v2/lookup no está en los endpoints permitidos de la clave de API.
422 Unprocessable Entity Falló la validación de la solicitud: query vacío o demasiado largo, un category o time_filter desconocido, num_results o page fuera de rango, perceive_top superior a 10.
502 Bad Gateway El proveedor de búsqueda devolvió una respuesta de error o un fallo de transporte no reintentable (SearchUpstreamError). Reintentar puede ayudar.
503 Service Unavailable El proveedor de búsqueda está mal configurado del lado del servidor (falta una clave de nuestro lado, SearchConfigError), o está temporalmente no disponible: el circuit breaker está abierto, o el proveedor nos limitó la tasa (SearchUnavailableError). Reintenta más tarde.
500 Internal Server Error Un fallo inesperado. El mensaje es genérico; cita la hora de la llamada a soporte.

Una auto-percepción que falla nunca genera un error propio. Va a parar a warnings y la llamada igual responde 200. 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 query 1–512 caracteres (recortado)
Longitud de country 8 caracteres
Longitud de locale 16 caracteres
Longitud de location 128 caracteres
num_results 1–100
page 1–10
perceive_top 0–10
Ops por llamada 1 por la consulta, más 1 por cada resultado auto-percibido
Salidas de auto-percepción Markdown de forma predeterminada; se amplía con enrich.outputs
Concurrencia de auto-percepción Secuencial de forma predeterminada; 1–5 con enrich.concurrency
Costo por búsqueda 0.06 centavos fijos
Expiración de URL firmada de página percibida 15 minutos

Preguntas frecuentes#

¿Cómo ejecuto una búsqueda web y obtengo el contenido de la página en una sola llamada a la API REST?#

Envía POST /v2/lookup con un query y define perceive_top (0–10). Las URLs de los N primeros resultados se renderizan en un navegador real, y cada resultado percibido lleva un objeto perceive en línea cuyo Markdown está detrás de una URL pre-firmada en perceive.outputs.markdown.url.

¿Es /v2/lookup una alternativa a una API SERP que puedo adoptar sin atarme a un proveedor?#

Sí. Serper es el proveedor de búsqueda que está detrás, pero la solicitud y la respuesta hablan un vocabulario de búsqueda neutral (category, country, locale, time_filter), de modo que un futuro cambio de proveedor no altera el contrato contra el que programas.

¿Qué categorías de búsqueda admite la API de lookup?#

Seis: web (la predeterminada), news, images, scholar, patents y maps. Los campos universales (title, url, snippet, position) siempre están tipados, y los extras específicos de la categoría van a parar a extra.

¿Por qué la búsqueda percibió menos páginas que mi valor de perceive_top?#

La auto-percepción omite los resultados cuyo url es null, se detiene si la cuota mensual de ops se agota a mitad de bucle, y degrada un renderizado fallido a una advertencia. El perceive_top de la respuesta informa cuántas páginas fueron realmente percibidas, y warnings explica los vacíos.