Skip to content

Semantic Search

Semantic search lets users search by describing what they want rather than typing the exact words that appear in your documents. A query like movies about a man who loses his memory or tom hanks movies is interpreted by an LLM, expanded into targeted keyword searches against your index, and the results are fused into a single ranked list. Scores are calibrated onto the same BM25 scale as a standard keyword search, so semantic results read like any other Searchcraft search response.

Unlike vector search, Searchcraft semantic search does not use embeddings or a vector index. There is no re-ingestion or backfill step. It works on your existing index the moment AI configuration is in place.

Semantic search runs through the standard search endpoints as an additional query kind alongside fuzzy, exact, and dynamic.

Most semantic search offerings are built on vector embeddings, and that choice commits you to a pipeline you have to maintain forever:

  • Every document must be run through an embedding model at ingestion time before it is semantically searchable, adding cost and lag to every write.
  • The resulting vectors must be stored in a separate ANN (approximate nearest neighbor) index, which grows your storage and memory footprint alongside the text index.
  • Every document update means re-embedding the document and updating the vector index to keep it in sync.
  • Upgrading or switching the embedding model, which is the main lever for improving vector search quality, invalidates every stored vector and forces a full re-embedding of the entire corpus.

Searchcraft skips all of that. The LLM interprets the query rather than your documents, so the only index involved is the full-text index you already have:

  • Instant enablement. Turning semantic search on is a single PATCH to the index’s ai configuration. Every document already in the index is semantically searchable immediately, with no backfill job and no embedding queue to drain.
  • Nothing to keep in sync. Newly ingested documents are semantically searchable as soon as they are committed, exactly like keyword search. There is no window where a document is findable by keyword but invisible to semantic queries.
  • Free model upgrades. Changing intent_detection_model to a newer, smarter, or cheaper model takes effect on the next query. There is no corpus to reprocess; the intent cache simply repopulates against the new model.
  • No extra infrastructure. No vector database, no ANN index tuning, no additional storage or memory beyond the standard index. The per-query cost is one small LLM call (cached across repeated queries), not a per-document embedding cost across your whole corpus.

It also means semantic quality benefits from the relevancy tuning you have already done. Field weights, synonyms, stopwords, and language stemming all apply, because the searches being run are standard searches.

Searchcraft Cloud: The intent detection model is pre-determined by Searchcraft and cannot be changed. If your plan includes AI and AI is enabled, semantic search is configured automatically, so there is nothing for you to set up. The configuration options below (llm_provider, intent_detection_model, and related fields) are intended only for self-hosted installs.

  • POST /index/:index/search Accepts a semantic query clause when the index has AI configured.
  • POST /federation/:federation_name/search Accepts a semantic query clause when the federation itself has AI configured.
  • GET /index/:index/capabilities Returns AI capability flags, including whether semantic search is configured.
  • GET /federation/:federation_name/capabilities Returns AI capability flags for a federation. See Search Result Summaries for details.

Semantic clauses are not supported on POST /index/:index/search/summary or POST /federation/:federation_name/search/summary. A summary request containing a semantic clause returns a 400 response.

Authenticated requests containing a semantic clause require a key with the LLM_RAG_SUMMARIES permission. A plain read/search key is not sufficient. See the Access Keys reference for key types. If your instance runs without authentication enabled, no key is required.

Searchcraft Cloud: Semantic search is configured automatically when your plan supports AI and AI is enabled. The prerequisites below apply to self-hosted installs only.

Before an index can serve semantic queries it needs:

  1. ai_enabled set to true on the index.
  2. An ai configuration block with an llm_provider (plus that provider’s required credential fields) and an intent_detection_model.

See Optional AI configuration for the full configuration reference and provider credential requirements. For federations, the federation itself needs ai_enabled and a matching ai configuration block. A federation resolves semantic search configuration entirely from its own ai_enabled and ai fields, never from member indexes, even when every member index has AI fully configured. See Optional AI configuration on the Federation Management page.

Use the capabilities endpoint to verify readiness:

Terminal window
curl -H "Authorization: read-key-value" https://searchcraft-cluster-url/index/data_test/capabilities
{
"status": 200,
"data": {
"ai": {
"enabled": true,
"searchSummaryConfigured": false,
"llmProviderConfigured": true,
"llmModelConfigured": false,
"semanticSearchConfigured": true,
"intentDetectionModelConfigured": true
}
}
}

semanticSearchConfigured is true when both llm_provider and intent_detection_model are set on the index.

This section applies to self-hosted installs only. On Searchcraft Cloud the intent detection model is pre-determined and cannot be changed.

Semantic search adds one new field to the ai configuration block:

  • ai.intent_detection_model - Optional string. The model identifier used for semantic intent detection. Must be non-empty when present. Required for the semantic query kind to be available. Intent detection is a small, latency-sensitive prompt, so a fast lightweight model is a good fit.

Example CURL request to enable semantic search with Bedrock

Section titled “Example CURL request to enable semantic search with Bedrock”
Terminal window
curl -X PATCH \
-H "Content-Type: application/json" \
--data '{
"ai": {
"llm_provider": "bedrock",
"llm_region": "us-east-1",
"intent_detection_model": "global.anthropic.claude-haiku-4-5-20251001-v1:0"
}
}' \
https://searchcraft-cluster-url/index/data_test

Note: On Bedrock, newer Claude models cannot be invoked with a bare foundation model ID. Use an inference-profile prefixed ID. The global. prefix carries no pricing premium, while us./eu. regional prefixes add a surcharge. A bare model ID will fail with a Bedrock ValidationException.

The same providers available for Search Result Summaries are supported for intent detection: anthropic, bedrock, google, llamacpp, mistral, ollama, openai, and xai. PATCH replaces the entire ai block when present, so include your full desired AI configuration (including any existing search_summary settings) when adding intent_detection_model.

A semantic clause carries a single ctx value containing the natural-language search term:

Terminal window
curl -X POST -H "Content-Type: application/json" -H "Authorization: llm-key-value" --data '{"query":{"semantic":{"ctx":"tom hanks movies"}}}' https://searchcraft-cluster-url/index/data_test/search

Semantic clauses compose with the rest of the query payload. Filters and other clauses in the same payload are applied to every expanded search, and the standard top-level parameters (limit, offset, order_by, sort) apply to the final fused result set:

Terminal window
curl -X POST -H "Content-Type: application/json" -H "Authorization: llm-key-value" --data '{"limit":10,"query":[{"semantic":{"ctx":"movies about a man who loses his memory"}},{"occur":"must","term":{"ctx":"/movies","fields":"category"}}]}' https://searchcraft-cluster-url/index/data_test/search

Composition rules:

  • Only one semantic clause is allowed per query.
  • A semantic clause cannot use "occur": "mustnot".
  • The ctx value must be text.

Violating any of these returns a 400 response.

Semantic search is a query-side feature. Your documents and your index are left alone; the LLM only ever sees the search term, and your existing full-text index does the actual searching.

  1. Intent detection. The model you configure as intent_detection_model reads the search term along with your index’s searchable fields, and returns a short statement of what the user is after plus a ranked set of expansion keywords grounded in the vocabulary that actually appears in your corpus. Detected intents are cached, so repeat queries skip this step.
  2. Targeted full-text search. The expansion runs as a set of weighted standard searches, executed concurrently. Every non-semantic clause in your payload (filters, term clauses, occur rules) is applied to each of them. On a federation, each one runs as a full federated search, so your per-index weighting still applies.
  3. Fused ranking. The result sets are merged, deduplicated by document, and returned as a single ranked list with scores calibrated onto the normal BM25 scale. A semantic response reads on the same scale as a plain keyword search, so relative score thresholds you already rely on carry over.

The response is the standard search response shape plus a semantic metadata block:

{
"status": 200,
"data": {
"hits": [
{
"doc": {
"title": "Forrest Gump",
"id": "1187"
},
"document_id": "1187",
"score": 14.82
}
],
"count": 37,
"time_taken": 0.214,
"semantic": {
"original_query": "tom hanks movies",
"detected_intent": "The user wants films starring Tom Hanks.",
"bm25_keywords": ["Forrest Gump", "Cast Away", "Saving Private Ryan"],
"intent_cached": false
}
}
}
FieldDescription
original_queryThe ctx value from the semantic clause.
detected_intentThe LLM’s one-sentence interpretation of the query.
bm25_keywordsThe expansion keywords that were searched, in ranked order (maximum 5).
intent_cachedtrue when intent detection was served from cache and no LLM call was made.

count reflects the total number of unique documents matched across all expanded searches, and time_taken covers the search execution (not the LLM call). hits, facets, and federation source_index values behave exactly as they do for standard searches.

Detected intents are cached, keyed on the query text, provider, and model, so repeated semantic queries skip the LLM call entirely (intent_cached: true). The cache honors the server’s standard cache settings (SEARCHCRAFT_CACHE_ENABLED, SEARCHCRAFT_CACHE_TTL_SECONDS, SEARCHCRAFT_CACHE_MAX_MEMORY_MB), and because the intent depends only on the query and model, cached intents survive re-indexing. Disabling the server cache means every semantic request pays an LLM round trip.

On a cache miss, a semantic query makes at least one LLM round trip before the search runs, so it will always be slower than a plain keyword query. Semantic search is best suited to deliberate, submitted searches rather than search-as-you-type.

If the LLM provider fails at request time (unreachable, rate limited, or returns an error), the request fails with a 502. Configuration errors (missing model, invalid provider) return a 400 HTTP error.

StatusMessageCause
400only one semantic clause is allowed per queryMultiple semantic clauses in the payload.
400a semantic clause cannot use occur "mustnot"Semantic clause negated with mustnot.
400a semantic clause requires a text valueNon-text ctx value.
401semantic queries require a key with LLM permissionsKey lacks the LLM_RAG_SUMMARIES permission.
400AI features are disabled for this indexai_enabled is false on the index.
400AI features not configured for this indexNo ai block on the index.
400AI features are disabled for this federationai_enabled is false on the federation.
400AI features not configured for this federationNo ai block on the federation.
400intent_detection_model is not configured for this indexai block exists but intent_detection_model is missing.
400semantic query clauses are not supported on the summary endpointSemantic clause sent to POST /index/:index/search/summary.
500All keyword searches failedEvery expanded search failed to execute.
502Semantic search failed: the LLM provider could not complete intent detection (...)The LLM provider failed during intent detection; the provider’s error is included in parentheses.