Integración por API
Referencia de la API de búsqueda de XEYE: endpoint POST /api/v1/search, autenticación con clave API, parámetros, respuesta, errores y límites.
Claves API
Las claves API autentican las llamadas a la API de búsqueda. Créalas desde la página Claves API, etiquétalas según dónde se usan y rótalas creando una nueva y borrando la antigua.
Una clave da acceso a las listas públicas de tu cuenta, y solo a ellas. Las listas privadas se prueban desde la página Búsqueda de la consola (que usa tu sesión, no una clave). Para que una integración pueda consultar una lista, márcala como pública.
El endpoint de búsqueda
La búsqueda es una única llamada HTTP POST con cuerpo JSON, autenticada con la cabecera X-API-Key. Solo sirve listas públicas.
curl -X POST https://search.xeye.es/api/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"list_name": "products",
"search_term": "wireless headphones",
"limit": 5
}'Cuerpo de la petición
El cuerpo es estricto: un campo desconocido, un término en blanco o un valor fuera de rango responden 422 con el detalle por campo.
list_nameobligatorio Nombre de la lista (pública) donde buscar, exactamente como aparece en la consola (máx. 100 caracteres).
search_termobligatorio La consulta del usuario (1–500 caracteres, se recortan los espacios). Las erratas y palabras parciales no son problema: la coincidencia es difusa y semántica.
limitopcional Número máximo de resultados a devolver, de 1 a 1000. Por defecto 50.
sessionopcional Identificador libre de la sesión del usuario final (máx. 255). Agrupa sus búsquedas en el historial de la lista y enlaza con /target.
include_score_breakdownopcional Si es true, cada resultado incluye text_score y semantic_score además de score. Por defecto false.
register_logopcional Obsoleto e ignorado: toda búsqueda se guarda en el historial de la lista. Se acepta solo para no romper integraciones antiguas.
const response = await fetch('https://search.xeye.es/api/v1/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.XEYE_API_KEY,
},
body: JSON.stringify({
list_name: 'products',
search_term: 'wireless headphones',
limit: 5,
session: sessionId, // optional: groups a user's searches
include_score_breakdown: true,
}),
})
const { results } = await response.json()La respuesta
Los resultados vienen ordenados por puntuación (0–1, cuanto más alta mejor). Cada uno incluye el texto del elemento («item»), sus parámetros intactos y, si lo pediste, el desglose texto/semántica. duration_ms es el tiempo de búsqueda en el servidor.
{
"success": true,
"results": [
{
"item": "Sony WH-1000XM5 wireless headphones",
"score": 0.93,
"params": {
"sku": "SONY-XM5",
"url": "/products/sony-wh-1000xm5",
"price": 348
},
"text_score": 0.71,
"semantic_score": 0.96
}
],
"total_results": 1,
"search_term": "wireless headphones",
"list_name": "products",
"duration_ms": 42,
"degraded": false,
"degradation_reasons": []
}Respuestas degradadas
Toda respuesta lleva degraded y degradation_reasons. Si degraded es true, la búsqueda se ha servido con menos calidad y el motivo lo dice: no_embeddings (la lista no tiene entrenamiento en uso: solo texto), model_unavailable (el modelo de embeddings no pudo cargarse), model_mismatch (los vectores no cuadran con el modelo: reentrena) o stale_data (datos caducos que no se pudieron refrescar). La cabecera X-Search-Degraded: true lo indica también.
Registrar la elección: /target
Opcional. Cuando el usuario final elige un resultado, envía POST /api/v1/target con list_name, target_term (el texto del elemento elegido) y la misma session de la búsqueda. Solo registra la elección en el historial; no busca nada. Responde {"success": true}.
curl -X POST https://search.xeye.es/api/v1/target \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"list_name": "products",
"target_term": "Sony WH-1000XM5 wireless headphones",
"session": "b1f2-…"
}'Límites de uso
El cupo de búsquedas por minuto es por cuenta: todas tus claves API (y el playground de la consola) lo comparten. Por defecto son 60 búsquedas/min; un administrador puede fijar otro valor para tu cuenta. Además hay un límite por dirección IP que se aplica antes de validar la clave.
Cada respuesta autenticada incluye X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset (segundos hasta la siguiente ventana). Al agotarse el cupo recibes 429 con Retry-After.
Errores
Todos los errores comparten el mismo cuerpo JSON: status, error (frase HTTP), code (identificador estable para tu código) y message (texto en inglés que puede cambiar); los 422 añaden details con el motivo por campo. Decide por code, no por message.
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 23
{
"status": 429,
"error": "Too Many Requests",
"code": "RATE_LIMITED",
"message": "Rate limit of 60 requests per minute per account exceeded; retry in 23 seconds"
}| HTTP | code | Significado | Qué hacer |
|---|---|---|---|
| 400 | INVALID_HOST | La cabecera Host no es la del servicio. | Llama a la URL pública del servicio de búsqueda, sin proxies que reescriban Host. |
| 401 | API_KEY_MISSING · API_KEY_INVALID | Falta la cabecera X-API-Key, o la clave no existe o fue revocada. | Revisa la clave o crea una nueva. |
| 403 | LIST_NOT_PUBLIC | La lista es privada. | Haz la lista pública desde sus ajustes; las privadas solo se prueban desde la consola. |
| 404 | LIST_NOT_FOUND | No hay ninguna lista con ese nombre en tu cuenta. | Revisa list_name: debe coincidir exactamente con el nombre de la lista. |
| 413 | REQUEST_TOO_LARGE | El cuerpo de la petición supera el tamaño máximo (16 KB). | Envía solo los campos documentados; search_term admite hasta 500 caracteres. |
| 422 | VALIDATION_FAILED | Cuerpo inválido: campo desconocido, término en blanco o valor fuera de rango. | Mira details: indica el campo y el motivo. |
| 429 | RATE_LIMITED | Cupo de búsquedas por minuto de tu cuenta (o de tu IP) agotado. | Espera los segundos de Retry-After y reintenta; usa X-RateLimit-Remaining para no llegar al límite. |
| 503 | SERVICE_NOT_READY · BACKEND_UNAVAILABLE | El buscador aún está cargando sus catálogos (SERVICE_NOT_READY) o no pudo cargar los datos de la lista (BACKEND_UNAVAILABLE). | Reintenta pasados los segundos de Retry-After; si persiste, consulta la página de estado (/status). |
Recomendaciones
Llama a la API desde tu backend y guarda la clave en una variable de entorno, no en código cliente.
Usa una clave por entorno o integración: así puedes revocar una sin romper el resto.
Controla el 429 con un pequeño reintento/backoff respetando Retry-After: el cupo es por cuenta y por minuto.
Envía session e informa de la elección con /target: el historial de búsquedas te dirá qué consultas no encuentran lo que esperan tus usuarios.