AACsearch
API de Búsqueda

Errores y Límites de Velocidad

Códigos de error, códigos de estado HTTP, estrategias de reintento y encabezados de límite de velocidad para la API pública de AACsearch.

AACsearch mapea todos los errores upstream a códigos de error tipados antes de devolverlos a los llamadores. Los mensajes de error crudos de AACSearch nunca se reenvían — esto evita la fuga de información y proporciona un contrato de errores estable.

Formato de respuesta de error

Todas las respuestas de error siguen este formato:

{
	"error": "error_code",
	"message": "Descripción legible por humanos del error"
}

El campo error es legible por máquina y estable. El campo message es para consumo humano y puede cambiar entre versiones.

Códigos de estado HTTP y códigos de error

4xx — Errores del cliente (no reintentar automáticamente)

HTTPCódigo errorCausa
400invalid_requestJSON malformado o campo requerido faltante
400invalid_filterSintaxis de expresión filterBy inválida
400invalid_sortExpresión sortBy inválida
401unauthorizedAPI key o token con ámbito faltante, inválido o expirado
401token_expiredEl TTL del token con ámbito ha expirado
403forbiddenLa clave no tiene el ámbito requerido (search o connector_write)
403origin_not_allowedEl origen de la solicitud no está en allowedOrigins para la clave
404index_not_foundEl indexSlug especificado no existe para esta organización
429rate_limit_exceededLímite de velocidad por clave superado
429quota_exceededLa cuota mensual de unidades de búsqueda de la organización está agotada

5xx — Errores del servidor (puede reintentar con retroceso)

HTTPCódigo errorCausa
502search_failedError upstream de AACSearch (clúster inalcanzable, tiempo de espera de consulta)
502ingest_failedNo se pudieron poner en cola los documentos en el buffer de ingestión
503service_unavailableLa API de AACsearch no está disponible temporalmente

Límite de velocidad

El límite de velocidad se aplica por API key usando un contador de ventana deslizante.

Límite predeterminado: 60 solicitudes por minuto por clave.

Cuando se supera el límite, la respuesta es:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 15
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1717201234

{
  "error": "rate_limit_exceeded",
  "message": "Límite de velocidad de 60 req/min superado. Reintente después de 15 segundos."
}

Encabezados de respuesta

Estos encabezados se incluyen en todas las respuestas exitosas y con límite de velocidad:

EncabezadoDescripción
X-RateLimit-LimitTotal de solicitudes permitidas por ventana
X-RateLimit-RemainingSolicitudes restantes en la ventana actual
X-RateLimit-ResetMarca de tiempo Unix cuando se resetea la ventana
Retry-AfterSegundos a esperar antes de reintentar (solo en 429)

Agotamiento de cuota

Cuando se agota la cuota del plan mensual:

HTTP/1.1 429 Too Many Requests

{
  "error": "quota_exceeded",
  "message": "Cuota mensual de unidades de búsqueda agotada. Actualice su plan o espere el reseteo."
}

Las unidades de búsqueda se resetean el día 1 de cada mes calendario. Actualice el plan en Configuración → Facturación para restaurar la cuota inmediatamente.

Estrategia de reintento para clientes

Clase de errorAcción
4xx (excepto 429)No reintentar — corrija la solicitud
429 rate_limit_exceededEspere los segundos de Retry-After, luego reintente
429 quota_exceededNo reintentar hasta que se resetee la cuota o se actualice el plan
502 search_failedReintente una vez después de 1 segundo; si falla nuevamente, muestre error al usuario
503Reintente con retroceso exponencial: 1s → 2s → 4s → abandone
Error de redReintente con retroceso exponencial

Comportamiento de reintento del SDK

El SDK @repo/search-client no reintenta automáticamente en errores 5xx. Implemente la lógica de reintento en la capa de su aplicación o use una biblioteca como p-retry.

import pRetry from "p-retry";

const results = await pRetry(() => client.search({ q: "auriculares" }), {
	retries: 3,
	factor: 2,
	minTimeout: 1000,
	shouldRetry: (err) => err.status >= 500, // solo reintentar errores del servidor
});

Comportamiento de reintento del módulo CMS

El buffer de ingestión de AACsearch está diseñado para reintentos:

  • Las solicitudes de sincronización del conector (sync/full, sync/delta) ponen en cola los documentos en SearchIngestBuffer
  • El trabajador en segundo plano reintenta las filas con error con retroceso exponencial
  • El módulo CMS no necesita implementar reintento para documentos individuales — el buffer lo maneja
  • El módulo CMS debe reintentar la propia solicitud HTTP en respuestas 5xx (no en 4xx)

Depuración en desarrollo

Para errores 502 durante el desarrollo:

  1. Verifique si AACSearch está en ejecución: curl http://localhost:8108/health
  2. Verifique los registros de la API para el error original de AACSearch (mapeado antes de responder al cliente)
  3. Verifique AACSEARCH_HOST, AACSEARCH_PORT, AACSEARCH_API_KEY en .env.local

Para errores 401:

  1. Verifique que la clave no haya expirado
  2. Verifique que el prefijo de la clave coincida con la operación (ss_search_* para búsqueda, ss_connector_* para ingestión)
  3. Para tokens con ámbito: verifique el campo expiresAt en el payload decodificado

Request ID and trace ID

Every response includes two correlation identifiers — pass them to support so we can find the request in logs without back-and-forth.

HeaderBody fieldWhere it comes from
X-Request-IdrequestIdGenerated per-request by the API edge. Always present.
X-Trace-IdtraceId (only present when distributed tracing is enabled)OpenTelemetry trace ID, propagated through worker → AACSearch → response

Capture them client-side from the SDK:

import { SearchClient } from "@aacsearch/client";

try {
	const result = await client.search({ q: "shoes" });
} catch (err) {
	if (err instanceof AacSearchError) {
		console.error("aacsearch error", {
			code: err.code,
			requestId: err.requestId, // for support tickets
			traceId: err.traceId, // for distributed tracing dashboards
		});
	}
}

Server-side handlers should log them on every request, success or failure:

const result = await client.search(opts);
logger.info({
	event: "search.ok",
	requestId: result.requestId,
	searchTimeMs: result.searchTimeMs,
});

SDK error class

The TypeScript SDK exposes a typed AacSearchError that wraps every non-2xx response:

import { AacSearchError } from "@aacsearch/client";

try {
	await client.search({ q: "shoes" });
} catch (err) {
	if (err instanceof AacSearchError) {
		err.code; // string — error code from the body, stable
		err.message; // string — human-readable
		err.status; // number — HTTP status (0 for network errors)
		err.retryable; // boolean — derived from body, fall back to status >= 500
		err.requestId; // string | undefined
		err.traceId; // string | undefined
		err.docsUrl; // string | undefined
		err.response; // Response | undefined — raw fetch Response if available
	}
}

Same shape in Python (SdkError) and PHP (AacSearchException).

UI state recipes

Concrete React components for the four most-common error states. All are headless — drop them into your design system.

Invalid key

function SearchInvalidKey({ requestId }: { requestId?: string }) {
	return (
		<div role="alert" className="search-error">
			<h3>Search is not configured correctly</h3>
			<p>Your API key is invalid, expired, or has been revoked.</p>
			{requestId && <small>Reference: {requestId}</small>}
		</div>
	);
}

Quota exceeded

function SearchQuotaExceeded({ resetDate }: { resetDate: Date }) {
	return (
		<div role="alert" className="search-error">
			<h3>Search is temporarily unavailable</h3>
			<p>The site has reached its monthly search limit.</p>
			<p>Service will resume on {resetDate.toLocaleDateString()}.</p>
		</div>
	);
}

No results

function NoResults({ query }: { query: string }) {
	return (
		<div className="no-results">
			<h3>No results for &quot;{query}&quot;</h3>
			<p>Try a shorter query, different keywords, or browse our categories below.</p>
		</div>
	);
}

For a richer no-results UI with did-you-mean, popular categories, and bestseller fallback, see the no-results recipe.

Server error / network

function SearchUnavailable({ requestId, onRetry }: { requestId?: string; onRetry: () => void }) {
	return (
		<div role="alert" className="search-error">
			<h3>Search is temporarily unavailable</h3>
			<p>We could not reach the search service. Please try again in a moment.</p>
			<button onClick={onRetry}>Retry</button>
			{requestId && <small>Reference: {requestId}</small>}
		</div>
	);
}

For the full troubleshooting flow per error code, see the troubleshooting hub.

On this page