Search API Instant¶
Search API Instant shows results while your visitors type. It works with the search indexes your site already has, from any Search API server, and needs no external service.
What visitors get:
- Answers as they type. Results appear after the first few letters, and half-typed words already match titles: "muse" finds "Museum of Art".
- Results they can trust. Each result says what a click does (opens a page, opens a PDF, goes to another site), shows a short excerpt, and is grouped as a strong match or as possibly related. A relevance cutoff keeps unrelated pages out.
- Search where they look for it. A small button floating over every page,
a button you place in the header, or a full search page whose address can
be shared and bookmarked. Pressing
/opens it. - Accessible search. Keyboard use, a screen-reader announcement of the results, and focus that returns where it was are built in.
What site builders get:
- Works with what you have. The keyword engine searches any Search API index, including the database backend that comes with Search API, so search-as-you-type needs no Solr or hosted service.
- One search over several indexes. A site-wide index and a documents index, for example, answer together; their results are merged by rank.
- As many searches as you need. Each search has its own engine, indexes and settings: one for the whole site, one for documents, one for a section.
- Search by meaning, if you want it. The semantic engine searches AI Search indexes (vectors in a vector database) and merges in literal matches from keyword indexes, so names and one-word queries still work.
- A fallback. A search can hand over to another while its index fills or when its server fails, for example from semantic to keyword.
- Safe to cache. Every result is checked against the visitor's access before it is shown, and Drupal's caches never serve one visitor's results to another.
- Easy to fit. Searches and settings are configuration you can export or ship in a recipe; colours and spacing are CSS custom properties; one hook changes results; other modules can add engines.
For a full description of the module, visit the project page.
Submit bug reports and feature suggestions, or track changes, in the issue queue.
Table of contents¶
- Requirements
- Recommended modules
- Installation
- Quick start
- Configuration
- Showing the search
- Search by meaning (the semantic engine)
- Changing the results and the look
- Access, privacy and caching
- Tools for AI agents and MCP clients
- Working with an AI coding assistant
- Drush commands
- Troubleshooting
- FAQ
- Similar projects and how they are different
- For developers
- How this module is built
- Maintainers
Requirements¶
- Drupal 11.2 or later.
- Search API 1.38 or later, with at least one index.
Recommended modules¶
- Search API Database Search (part of Search API): a keyword index stored in your site's database, with no extra server.
- AI and AI Search: AI Search (a submodule of AI until 1.5.x, now its own project) provides the vector index the semantic engine searches.
- Search API Attachments: makes the text of PDFs and other files searchable.
Installation¶
Install as you would normally install a contributed Drupal module. For further information, see Installing Drupal Modules.
composer require drupal/search_api_instant
drush en search_api_instant
Nothing appears on the site until you have added a search.
Quick start¶
For a site with no search yet, about ten minutes:
- At Administration » Configuration » Search and metadata » Search API, add a server with the "Database" backend.
- Add an index on that server for your content. Add the title and body as "Fulltext" fields, and the "Entity status", "Tokenizer", "Ignore case" and "HTML filter" processors. Save and index it.
- Go to Administration » Configuration » Search and metadata » Search API
Instant (
/admin/config/search/search-api-instant) and click Add search. - Name it, choose the Keyword engine and tick your new index. Save.
- Open the Settings tab, choose the search as the Default search, turn on Show the floating launcher on every front-end page and save.
Visit any page of the site: a search button sits in the bottom right corner.
If your site already has Search API indexes (on a database, Solr or another server), skip the first two steps and tick those indexes.
Configuration¶
Both pages need the "Administer Search API" permission. Visitors need "View
published content" (access content) to search.
Each search (Searches tab, /admin/config/search/search-api-instant):
| Setting | What it does |
|---|---|
| Engine | Keyword (any index) or Semantic (AI Search indexes) |
| Indexes | The indexes the engine searches; results from several are merged by rank. The semantic engine also takes keyword indexes for literal matches |
| Cutoff and strong-match threshold | Semantic engine only: see below |
| Fallback | Another search that answers while this one is not ready: its index is less than a set share indexed, or its server is unavailable or fails |
| Results per search | How many results to show (8 by default) |
| Most results per entity type | For example media|4, so documents cannot fill every place |
| Partial-word title matches | Which entity types' titles match half-typed words (all by default) |
| Summary fields | Fields used for an excerpt when the match has none (body by default) |
| Only results in the current language | On by default; items without a language are always included |
| Characters before the first search | 3 by default; 2 suits Chinese and Japanese |
| Excerpts from the index's text | On by default: the matched chunk or Highlight excerpt. Off: excerpts come only from the summary fields the visitor may view (see "Access, privacy and caching") |
| Only roles with the permission to use this search | Off by default. On: the search, its launchers and its address need "Use the … instant search" as well as "View published content" |
| Label and sample question | The text on the launcher and panel, and a one-click example |
The site (Settings tab):
| Setting | What it does |
|---|---|
| Default search | The search the floating launcher shows, and blocks and search pages that name no search |
| Floating launcher | Shows the default search's button on every front-end page |
| Paths without the floating launcher | Path prefixes, for example your search page |
| Searches per minute from one address | 90 by default; an IPv6 address counts by its /64 network; 0 for no limit |
| Searches per minute from all addresses together | 0 (no limit) by default; a ceiling on uncached searches, for example to cap embedding costs |
Each search is a configuration entity (search_api_instant.search.ID) and the
site settings are search_api_instant.settings, so you can export them or
ship them in a recipe.
Showing the search¶
Choose one of these per page:
- The floating launcher is a small button over the bottom right corner of every front-end page, for the default search. It never appears on administration pages or on the paths you exclude.
- The launcher block ("Search API Instant launcher") puts the button where you place it, for example in the header. Each block shows the default search or the one you choose, so a documents section can have its own. Leave the floating launcher off when you use the block for the default search.
- The search page is a render element a module or theme puts on a page.
The query is kept in the address (
?q=), so a search can be shared, reloaded or bookmarked. Give it a fallback for visitors without JavaScript, such as a Search API view, and add its path to the excluded paths.
$build['search'] = [
'#type' => 'search_api_instant_page',
'#query' => (string) $request->query->get('q', ''),
// Optional: the search's ID; the default search when left out.
'#search' => 'documents',
// Optional: a search that works without JavaScript.
'#fallback_url' => Url::fromRoute('view.search.page_1'),
// Optional: the form's label; the search's label by default.
'#label' => t('Search this site'),
];
Search by meaning (the semantic engine)¶
The semantic engine finds pages that answer a question even when they use other words ("bringing my bike on the train" finds the bicycle policy). It needs an AI Search index, which embeds your content with an AI provider and stores the vectors in a vector database. Both run in accounts you control.
- Choose keyword indexes for it too. Their literal matches are merged in and count as strong, which fixes names and one-word queries that vectors handle poorly.
- Set the cutoff (results below it are hidden) and the strong-match
threshold for your embedding model.
drush search-api-instant:calibrateprints the scores of off-topic questions and of your own, to choose them. 0.36 and 0.45 suit OpenAI'stext-embedding-3-small. - Give it a keyword search as its fallback. A new semantic index takes hours to fill, about one or two items a second, each embedded by your provider; until it reaches the share you set (95% by default), the keyword search answers. The fallback also answers when the vector database is unreachable.
- Several AI Search indexes are merged by similarity, which assumes they use the same embedding model.
Supported versions and known issues: the semantic engine was built and
checked against the AI module 1.5.x with AI Search 1.3. It relies on two
details of AI Search that are not a documented API (the
search_api_ai_get_chunks_result query option, and the best chunk's text in
the result's content extra data). AI Search 2.0 has not been checked yet.
- Use AI Search's tracker (
ai_search_tracker) on a semantic index. With Search API's default tracker, an item queued again after a change keeps its chunk counters at "complete", and AI Search reports it indexed without embedding it. - Chunk counters advance when embedding fails, so a failed item looks
done.
drush search-api-instant:reindexrepairs both. - Moderation runs the provider's moderation model on every chunk unless
the server sets
skip_moderation: true, which halves indexing speed for public content.
Changing the results and the look¶
Results. Implement hook_search_api_instant_result_alter() (documented
in search_api_instant.api.php) to change the title, link, badge, excerpt or
the text that says what a click does, for example to mark documents as PDFs
or to link a passage of a recording to the moment it plays. Set a result to
NULL to leave it out.
Queries. Every Search API query a search makes has the search ID
search_api_instant:SEARCH_ID, so a subscriber to Search API's
QUERY_PRE_EXECUTE event can identify and change it (see
search_api_instant.api.php).
Look. Colours, corners and spacing are CSS custom properties. Map them to your theme's own:
:root {
--search-api-instant-accent: var(--my-theme-accent);
--search-api-instant-launcher: var(--my-theme-primary);
}
The properties are --search-api-instant-accent, -ink, -launcher,
-text, -muted, -line, -bg, -soft, -field-bg, -radius,
-badge-radius and -gap. The markup is in
templates/search-api-instant-widget.html.twig (launcher and panel) and
templates/search-api-instant-page.html.twig (page); override them in your
theme only when the markup itself must change, and keep their
data-search-api-instant-* attributes.
Access, privacy and caching¶
- Each result is checked with its entity's own view access before it is shown. Unpublished or restricted content never appears for visitors who cannot see it.
- A search can be restricted to roles with its own permission; otherwise anyone with "View published content" may use any enabled search, at its address, whether or not it is shown on a page. A fallback answers only for visitors who may use it, and a search open to everyone cannot fall back to a restricted one.
- Title matches are confirmed through the index with the search's query, so query alterations, Search API's processors (content access, exclusions) and items not yet indexed apply to them too. AI Search ignores query conditions, so on an AI Search index this confirmation is skipped; there, limit "Partial-word title matches" to entity types every visitor may find.
- Answers vary by every access check made, by the visitor when an index filters by access at query time (content access), and by the content language when a search keeps to it, so Drupal's caches never serve one visitor's results to another; publishing or unpublishing content clears the cached answers.
- The endpoint follows the site's page cache policy: anonymous answers are public for the site's page cache max-age, signed-in visitors' answers are private. Drupal's own page cache keeps an anonymous answer for at most an hour (it reads the answer's Expires header), and an answer from a fallback for at most five minutes; one given after an engine failed is not cached.
- Each address may search 90 times a minute (a setting). Behind a CDN or
load balancer, configure Drupal's
reverse_proxysettings insettings.php, or every visitor shares the proxy's address and its limit. - Excerpts show indexed text. An excerpt comes from the text the index holds for the result (the matched chunk, or the Highlight processor's excerpt), so index only fields every visitor who may see the item may also read, or index the "Rendered item" as an anonymous visitor. Summary fields, used when the match has no text, are skipped when the visitor may not view them.
- Result links are made safe. Whatever an alter hook sets, a result's link
loses dangerous schemes (
javascript:and the like) and has quotes, angle brackets and spaces percent-encoded; the script escapes every value it shows and follows only web addresses. - The browser talks only to your site: no fonts, scripts or requests from anyone else. With the semantic engine, your site sends each query to your embedding provider, and failed searches are logged with their query; say so in your privacy notice if that matters.
- Search engines need not crawl the endpoint; add
Disallow: /search-api-instant/torobots.txt, as each crawled query can cost an embedding.
Tools for AI agents and MCP clients¶
Two optional submodules offer the search to AI agents. Each needs its own module and depends on nothing else; each has its own README (tools, MCP).
| Submodule | Needs | Gives |
|---|---|---|
Search API Instant tools (search_api_instant_tool) |
Tool | Tool API tools, used by the AI module's agents and by site agents |
Search API Instant MCP (search_api_instant_mcp) |
MCP Server | Model Context Protocol tools, for AI clients connected to the site |
Both offer the same two tools:
- Search the site (
search_api_instant_search): takes a query, an optional search ID (the default search when empty) and an optional limit (up to the search's own), and returns what the JSON endpoint returns: titles, links, excerpts, strength, and which search answered. It runs as the current user, so entity access, field access and restricted searches apply as on the site; a fallback answers only for those who may use it. The Tool API tool refuses to run for any account but the current user, so an agent acting for someone must switch to their account first. It counts against the same rate limits, a signed-in agent by its account rather than its address. It needs "View published content", and a restricted search's permission. - Instant search status (
search_api_instant_status): for users with "Administer Search API". Lists the searches, their engines and indexes, how much of each index is indexed, and which search is answering (the search itself or its fallback).
Results are the site's content: an agent should read them as data, never as instructions, since anyone who can write content can write text that looks like an instruction. With a semantic search, the query goes to the site's embedding provider.
Both tools answer through the search_api_instant.searcher service, as the
JSON endpoint does. hook_search_api_instant_answer_alter() changes every
answer (its context says whether the endpoint, a Tool API tool or an MCP
tool asked), and hook_search_api_instant_status_alter() the status. A site
can also replace or decorate the service
(Drupal\search_api_instant\SearcherInterface).
Working with an AI coding assistant¶
This module ships instructions for AI coding assistants in AGENTS.md (Claude Code reads them through CLAUDE.md). They walk an assistant through adding the module to a site: what to check first, which engine and indexes to choose, the searches and settings, the search page, the result hook, the checks to run, and what to ask you before doing anything slow or costly, such as reindexing a semantic index.
To use them, ask your assistant to read
web/modules/contrib/search_api_instant/AGENTS.md (or wherever Composer put
the module) before it starts, or point to it from your site's own
AGENTS.md:
Before changing site search, read web/modules/contrib/search_api_instant/AGENTS.md.
Drush commands¶
drush search-api-instant:calibrate [--search=ID] [--queries=file]prints the semantic scores of off-topic questions, and of your own on-topic ones (one per line in the file), to choose a semantic search's cutoff and strong-match threshold. Without--search, the default search.drush search-api-instant:reindex <index>resets AI Search's chunk counters, marks every item for reindexing and indexes in batches, without clearing the index. It works around the AI Search issues above and becomes unnecessary once they are fixed there. On other indexes it is a plain reindex.
Troubleshooting¶
- No launcher appears: no default search is set, it is disabled, neither
it nor its fallback has an enabled index on an available server, the
floating launcher is off, the path is excluded or is an administration
page, or the visitor lacks
access content. - "Search is not available right now": the search failed. The reason is
in the site's log (Reports » Recent log messages), type
search_api_instant. - Partial words do not match: check "Partial-word title matches" on the search, and that the index has been indexed.
- The semantic engine finds nothing, or too much: check the cutoff with
drush search-api-instant:calibrate. - The keyword search answers instead of the semantic one: the semantic index is below the fallback's share, or its server is unavailable. The searches page shows "Answering with its fallback".
- New content does not appear: the index has not indexed it yet. Turn on "Index items immediately" on the index, or run cron.
FAQ¶
Q: Do I need Solr, Elasticsearch or a hosted search service?
A: No. The keyword engine works on Search API's database backend. It also works on Solr and other backends if you have them.
Q: Do I need AI?
A: No. The keyword engine uses none; the semantic engine is optional.
Q: Does it need JavaScript?
A: The launcher and the page do. The page takes a fallback address for a search that works without it, such as a Search API view.
Q: Can it search several indexes?
A: Yes. Tick several indexes on a search and their results are merged by rank, for example a site-wide index and a PDF documents index.
Q: Can a site have several searches?
A: Yes. Add as many as you need; the floating launcher shows the default one, and blocks and pages can show any.
Q: Does it replace my search results page?
A: It can, with the search page element, or it can sit beside it: keep your existing view as the fallback for visitors without JavaScript.
Q: Does it work on multilingual sites?
A: Yes. A search shows results in the current language, and items without a language, unless you turn that off.
Similar projects and how they are different¶
- Search API Autocomplete adds suggestions, or live results, to the text field of a Search API view or page, configured per search. Search API Instant is a site-wide search with its own launcher, panel and page, result kinds and excerpts, strong and weak groups, a relevance cutoff, several indexes per search, fallbacks, and keyword and vector fusion. The two can be used together.
- AI Search is a vector backend with no search interface; this module can be one for it. AI Search's own hybrid processors ("Boost Database by AI Search") add vector results to a keyword search; the semantic engine here works the other way round, with the vector index leading and keyword literals added.
- AI Search Block writes an answer with a language model on every query. Search API Instant returns pages and documents, with no chat model involved.
- Search API Typesense and Search API Lunr ship instant search for their own backends. Search API Instant works on any backend and loads nothing from a CDN.
- Search Suggestions had a similar plugin design but has no release for Drupal 10 or 11.
- Instant Search is an unrelated, deprecated Drupal 6 module.
For developers¶
The endpoint. /search-api-instant/query/SEARCH_ID?q=… answers JSON:
query, search, engine, engine_label, fallback (whether a fallback
answered, with answered_by), status (strong, weak, none, short or
error), took_ms and results. /search-api-instant/query?q=… answers
with the default search. Each result has title, url, kind (page,
pdf, document, external, or a site's own), type, host, size,
excerpt, context, action, score and strength. Queries shorter than
the search's minimum answer short; a disabled search answers 404. Failures
answer 200 with status: error and no-store, because on some hosts a 5xx
from a JSON route reaches browsers as a redirect.
Engines. Both engines merge title matches: every word typed must appear in the title, the last one only as the start of a word. Titles are matched with an entity query on the label, then checked word by word, so nothing is cached and large indexes cost a database query, not a scan in PHP.
| Engine | Indexes | Scores |
|---|---|---|
keyword |
Any Search API indexes, merged by reciprocal rank | Every match counts; title matches are strong, other matches weak |
semantic |
AI Search (search_api_ai_search) indexes, and keyword indexes for literal matches |
Cosine similarity against the cutoff (min_score) and the strong-match threshold (strong_score); literal matches count as strong |
With the semantic engine and "most results per entity type", each keyword index is asked once per listed type, so each kind of record gets its own literal matches whatever the backend's scoring.
An engine is a configurable plugin in a module's
src/Plugin/SearchApiInstantEngine directory with the
#[SearchApiInstantEngine] attribute, usually extending
\Drupal\search_api_instant\Engine\EngineBase, which provides the index
setting and form, dependencies on the indexes, the cutoff, access checks,
limits and grouping. Its settings are stored in each search's
engine_settings; declare their schema as
search_api_instant.engine.ENGINE_ID.
The service. search_api_instant.searcher
(Drupal\search_api_instant\SearcherInterface) answers every query: the
JSON endpoint and the agent tools call it, so they share results, rate
limits, fallbacks and errors. Replace or decorate it to change all of them.
Tests. Unit, kernel, functional and FunctionalJavascript tests are in
tests/, and each submodule's in its own tests/. Run them with Drupal core's PHPUnit configuration:
vendor/bin/phpunit -c web/core web/modules/contrib/search_api_instant/tests
The FunctionalJavascript tests need a WebDriver (MINK_DRIVER_ARGS_WEBDRIVER).
AGENTS.md lists the code checks and the release steps.
How this module is built¶
Much of this module's code, tests and documentation was drafted with an AI coding assistant (Claude Code). The maintainers review, test and take responsibility for every change, and the tests and code checks above run on drupal.org's CI. Contributions to this project follow Drupal's policy on the use of AI when contributing.
Maintainers¶
This project has been sponsored by: