AACsearch
Such-API

Fehler & Rate-Limits

Fehlercodes, HTTP-Statuscodes, Retry-Strategien und Rate-Limit-Header für die öffentliche AACsearch-API.

AACsearch bildet alle vorgelagerten Fehler auf typisierte Fehlercodes ab, bevor sie an Aufrufer zurückgegeben werden. Rohe Suchmaschinenfehlermeldungen werden niemals weitergeleitet — dies verhindert Informationslecks und bietet einen stabilen Fehlervertrag.

Fehlerantwortformat

Alle Fehlerantworten folgen diesem Format:

{
	"error": "error_code",
	"message": "Für Menschen lesbare Beschreibung des Fehlers"
}

Das error-Feld ist maschinenlesbar und stabil. Das message-Feld ist für den menschlichen Konsum gedacht und kann sich zwischen Versionen ändern.

HTTP-Statuscodes und Fehlercodes

4xx — Client-Fehler (nicht automatisch wiederholen)

HTTPerror-CodeUrsache
400invalid_requestFehlerhaftes JSON oder fehlendes Pflichtfeld
400invalid_filterUngültige filterBy-Ausdruckssyntax
400invalid_sortUngültiger sortBy-Ausdruck
401unauthorizedFehlender, ungültiger oder abgelaufener API-Schlüssel oder Token
401token_expiredTTL des eingeschränkten Tokens ist abgelaufen
403forbiddenSchlüssel hat nicht den erforderlichen Bereich (search oder connector_write)
403origin_not_allowedAnfrage-Ursprung nicht in allowedOrigins für den Schlüssel
404index_not_foundDer angegebene indexSlug existiert für diese Organisation nicht
429rate_limit_exceededRate-Limit pro Schlüssel überschritten
429quota_exceededMonatliches Sucheinheiten-Kontingent für die Organisation erschöpft

5xx — Server-Fehler (kann mit Backoff wiederholt werden)

HTTPerror-CodeUrsache
502search_failedVorgelagerter Suchmaschinenfehler (Cluster nicht erreichbar, Abfrage-Timeout)
502ingest_failedDokumente konnten nicht in den Ingest-Buffer eingereiht werden
503service_unavailableAACsearch-API ist vorübergehend nicht verfügbar

Rate-Limiting

Rate-Limiting wird pro API-Schlüssel über einen Gleitfenster-Zähler durchgesetzt.

Standard-Limit: 60 Anfragen pro Minute pro Schlüssel.

Wenn das Limit überschritten wird, ist die Antwort:

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": "Rate-Limit von 60 Req/Min überschritten. Bitte nach 15 Sekunden erneut versuchen."
}

Antwortheader

Diese Header sind bei allen erfolgreichen und rate-limitierten Antworten enthalten:

HeaderBeschreibung
X-RateLimit-LimitGesamtanfragen pro Fenster erlaubt
X-RateLimit-RemainingVerbleibende Anfragen im aktuellen Fenster
X-RateLimit-ResetUnix-Zeitstempel, wenn das Fenster zurückgesetzt wird
Retry-AfterSekunden bis zum nächsten Versuch (nur bei 429)

Kontingenterschöpfung

Wenn das monatliche Plan-Kontingent überschritten wird:

HTTP/1.1 429 Too Many Requests

{
  "error": "quota_exceeded",
  "message": "Monatliches Sucheinheiten-Kontingent erschöpft. Upgraden Sie Ihren Plan oder warten Sie auf die Rücksetzung."
}

Sucheinheiten werden am 1. eines jeden Kalendermonats zurückgesetzt. Upgraden Sie den Plan unter Einstellungen → Abrechnung, um das Kontingent sofort wiederherzustellen.

Retry-Strategie für Clients

FehlerklasseAktion
4xx (außer 429)Nicht wiederholen — die Anfrage korrigieren
429 rate_limit_exceededRetry-After-Sekunden warten, dann erneut versuchen
429 quota_exceededNicht wiederholen, bis Kontingent zurückgesetzt oder Plan upgraded
502 search_failedEinmal nach 1 Sekunde wiederholen; wenn erneut fehlschlägt, Fehler anzeigen
503Exponentielles Backoff: 1s → 2s → 4s → aufgeben
NetzwerkfehlerExponentielles Backoff

SDK-Retry-Verhalten

Das @repo/search-client-SDK wiederholt bei 5xx-Fehlern nicht automatisch. Implementieren Sie Retry- Logik in Ihrer Anwendungsschicht oder verwenden Sie eine Bibliothek wie p-retry.

import pRetry from "p-retry";

const results = await pRetry(() => client.search({ q: "headphones" }), {
	retries: 3,
	factor: 2,
	minTimeout: 1000,
	shouldRetry: (err) => err.status >= 500, // nur Server-Fehler wiederholen
});

CMS-Modul-Retry-Verhalten

Der AACsearch-Ingest-Buffer ist für Retry ausgelegt:

  • Connector-Sync-Anfragen (sync/full, sync/delta) reihen Dokumente in SearchIngestBuffer ein
  • Der Hintergrund-Worker wiederholt fehlgeschlagene Zeilen mit exponentiellem Backoff
  • Das CMS-Modul muss kein Retry für einzelne Dokumente implementieren — der Buffer behandelt es
  • Das CMS-Modul sollte die HTTP-Anfrage selbst bei 5xx-Antworten wiederholen (nicht bei 4xx)

Entwicklungs-Debugging

Für 502-Fehler während der Entwicklung:

  1. Prüfen, ob AACSearch läuft: curl http://localhost:8108/health
  2. API-Protokolle auf den ursprünglichen Suchmaschinenfehler prüfen (abgebildet vor der Antwort an den Client)
  3. AACSEARCH_HOST, AACSEARCH_PORT, AACSEARCH_API_KEY in .env.local prüfen

Für 401-Fehler:

  1. Sicherstellen, dass der Schlüssel nicht abgelaufen ist
  2. Sicherstellen, dass das Schlüsselpräfix zur Operation passt (ss_search_* für Suche, ss_connector_* für Ingest)
  3. Für eingeschränkte Token: expiresAt-Feld im dekodierten Payload prüfen

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