API de Búsqueda Web para Agentes LLM#
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.
- 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. - 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. - Normalización. Cada resultado del proveedor se aplana en un
LookupResultneutral que llevatitle,url,snippetyposition. Los extras específicos de la categoría van a parar aextra, de modo que el contrato nunca crece con una columna por cada particularidad del proveedor. - 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 comolookup_idpara la correlación con soporte. - 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/perceivecompleta: su propia op facturada contra la cuota compartida, su propia fila de operación, su propiooperation_id. De forma predeterminada, la auto-percepción solo solicita Markdown, sin captura de pantalla, PDF ni extracción con LLM; envía un objetoenrichpara 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.