Suggest
GET /api/v1/indexes/{slug}/suggest and POST /api/v1/indexes/suggest/multi — prefix autocomplete blended with popular queries, ETag-revalidated and never metered.
GET /api/v1/indexes/{slug}/suggest
POST /api/v1/indexes/suggest/multiAutocomplete is a different workload from search: shorter queries, smaller payloads, no quota cost, aggressive caching. Both endpoints require search:read and are rate-limited per key, but they do not consume the plan's search quota.
Single index
curl -sS -G "$AAC_API/indexes/documentation/suggest" \
-H "Authorization: Bearer $AAC_KEY" \
--data-urlencode "q=pri" \
--data-urlencode "query_by=title" \
--data-urlencode "limit=5" \
--data-urlencode "filter_by=tags:=[billing]" \
--data-urlencode "include_popular=true"{
"index": "documentation",
"query": "pri",
"suggestions": [
{
"text": "Pricing",
"source": "document",
"documentId": "pricing",
"url": "https://example.com/pricing",
"score": 3,
"highlight": "<mark>Pri</mark>cing"
},
{
"text": "pricing plans",
"source": "query",
"score": 2
}
],
"did_you_mean": null,
"sources": { "documents": 1, "queries": 1 },
"cache": "miss",
"request_id": "req_1c2d3e4f5a6b7c8d9e0f1a2b"
}| Parameter | Type | Notes |
|---|---|---|
q | string 1–120 | Required. Prefix typed by the user |
query_by | string ≤ 256 | Fields to suggest from; defaults to the index query fields |
limit | int 1–10 | Default 5 |
filter_by | string ≤ 2000 | Same syntax as search; tenant filter is still injected |
include_popular | boolean | Blend popular queries from this tenant's log; default true |
source is document for a document title and query for a completion mined from the query log. documentId and url are present only for document suggestions. highlight wraps the typed prefix in <mark> so a widget needs no client-side matching.
did_you_mean is a corrected spelling when the typo-tolerant fallback found something better than the raw prefix.
Prefix, suffix and price completions
Prefix matches are ranked first. Additional infix/suffix matches are requested
only from fields with infix: true; a field such as content without an infix
index does not disable matching on title. New and rebuilt collections enable
infix on title. Existing collections need the normal asynchronous reindex.
плат от 1000 р searches completions for плат with a minimum price of 1000.
When a public numeric price field exists and price understanding is enabled,
active_filters describes the removable price chip. Select a suggestion using
its completionQuery when present, so its price constraint is retained.
The widget uses POST /api/v1/embed/suggest with its embed session token and
{token, index, q, limit}. It is rate-limited and does not consume search quota.
The console and widget support arrow keys, Enter, Escape and pointer selection.
Word and phrase completions, corrections
completionType: "word" | "phrase" distinguishes a word/next-word completion
from a full public title. For example, shirt bl can complete to shirt blue
alongside the phrase Shirt blue cotton. The original query is retained.
Validated spelling, adjacent-letter and keyboard-layout corrections are shown
as a selectable “Did you mean” row using did_you_mean and did_you_mean_kind.
Selecting a completion or correction preserves recognized price bounds.
Caching and revalidation
Responses carry a strong etag and cache-control: private, max-age=30; the internal result cache is 30 seconds per tenant and is reported as cache: "hit" or "miss". Send the ETag back to save bandwidth:
curl -sS -i -G "$AAC_API/indexes/documentation/suggest" \
-H "Authorization: Bearer $AAC_KEY" \
-H 'If-None-Match: "3f9a1c7e5b2d8a0c4e6f"' \
--data-urlencode "q=pri"HTTP/1.1 304 Not Modified
etag: "3f9a1c7e5b2d8a0c4e6f"The ETag is computed without the cache field, so a cache hit and a cache miss of the same suggestions validate identically.
Several indexes at once
curl -sS -X POST "$AAC_API/indexes/suggest/multi" \
-H "Authorization: Bearer $AAC_KEY" \
-H "Content-Type: application/json" \
-d '{ "indexes": ["documentation", "blog"], "q": "pri", "limit": 5 }'{
"query": "pri",
"results": [
{
"index": "documentation",
"suggestions": [
{
"text": "Pricing",
"source": "document",
"documentId": "pricing",
"url": "https://example.com/pricing",
"score": 3
}
],
"did_you_mean": null,
"cache": "miss"
},
{
"index": "blog",
"suggestions": [],
"did_you_mean": null,
"cache": "miss"
}
],
"request_id": "req_8b4f2a6c0d1e3f5a7b9c1d3e"
}indexes takes 1–10 slugs; each one is access-checked and rate-limited, and results are grouped per index in the order you sent. Invalid input is rejected with 422 before any engine call:
{
"error": {
"code": "INVALID_QUERY",
"message": "indexes must contain 1-10 slugs"
}
}Note that duplicates are removed before the fan-out, so ["docs","docs"] is one lookup, not two.
Presearch (empty search box)
GET /api/v1/indexes/{slug}/presearch
POST /api/v1/embed/presearchSuggestions to show before the first keystroke. The REST route needs a key with search:read and is rate-limited per key; the embed route takes the embed session token (body token or Authorization: Bearer aac_sess_…) and is rate-limited per client address. Neither consumes search quota.
curl -sS -G "$AAC_API/indexes/documentation/presearch" \
-H "Authorization: Bearer $AAC_KEY" \
--data-urlencode "session_id=visitor-4f7c2a9e1b3d" \
--data-urlencode "q=pricing" \
--data-urlencode "limit=8"{
"index": "documentation",
"items": [
{ "query": "pricing plans", "source": "continue", "score": 0.82 },
{ "query": "invoice download", "source": "recent" },
{ "query": "api keys", "source": "trending", "score": 41 }
],
"request_id": "req_6a1f0c3e9b2d4a7c8e5f1b0d"
}| Parameter | Type | Notes |
|---|---|---|
session_id | string ≤ 128 | Durable anonymous visitor id; scopes recent. Ids shorter than 12 characters are ignored as guessable |
q | string ≤ 500 (embed: ≤ 200) | The visitor's last query; seeds continue from the query graph |
limit | int 1–20 | Default 8 |
index | string ≤ 64 | Embed only: must match the session's index, otherwise 403 INDEX_MISMATCH |
source is where an item came from, in this order:
continue— narrowing, refinement or correction edges from the query graph forq.recent— this visitor's own successful queries.trending— popular queries of the last 7 days, only if at least 3 distinct visitors typed them.
Queries that look like personal data or secrets (emails, phone numbers, long digit runs such as order or card numbers, links) and hashed queries are never returned. Invalid parameters return 422 VALIDATION_ERROR.