Documentation: Chrome Search API, Firefox Search API.
Search using the browser's default provider, or select an available engine in Firefox.
Search operations navigate to a results page and return Promise<void>; they do not return results or a tab ID,
and completion does not mean the results page has finished loading. This API has no events.
- isAvailableSearch()
- canQuerySearch()
- canGetSearchEngines()
- canSearchWithEngine()
- querySearch(options)
- getSearchEngines()
- searchWithEngine(text, engine, options?)
- searchInTab(text, tabId)
- searchInCurrentTab(text)
- searchInNewTab(text)
- searchInNewWindow(text)
- getDefaultSearchEngine()
- hasSearchEngine(name)
Declare the search permission in extension builds that use the native Search API:
{"permissions": ["search"]}Use these methods from an extension context where the API is exposed, such as a background script or popup.
| Browser | Default-provider query and destination helpers | List/select engines |
|---|---|---|
| Chrome 87+ | Supported | Unavailable |
| Edge / Opera | Supported in versions implementing Chromium's Search API | Unavailable |
| Firefox desktop 111+ | Supported | Supported |
| Firefox desktop 63–110 | query unavailable |
Supported; disposition requires Firefox 111+ |
| Safari macOS / iOS | Unavailable | Unavailable |
| Firefox for Android | Unavailable | Unavailable |
The table follows MDN browser compatibility data. Real-browser integration scenarios additionally exercise current Chromium and Firefox builds; they do not verify every listed browser or historical version. Check individual methods at runtime in restricted contexts.
isAvailableSearch() checks namespace presence. The can… functions check whether a specific native method is callable;
they do not invoke it or guarantee permission, valid arguments, supported optional fields, or successful execution.
All these checks are synchronous, uncached, silent, and return false if API access throws.
Operations preserve native failures. Only the custom predicate hasSearchEngine() converts an unconfirmed result to false.
The package exports SearchEngine and SearchTargetOptions:
type SearchEngine = {
name: string;
isDefault: boolean;
alias?: string;
favIconUrl?: string;
};
type SearchTargetOptions =
| {tabId?: number; disposition?: undefined}
| {tabId?: undefined; disposition?: "CURRENT_TAB" | "NEW_TAB" | "NEW_WINDOW"};tabId and disposition are mutually exclusive. Use engine names returned by getSearchEngines();
the browser's installed engines are not a fixed list. Their metadata does not include search URL templates.
The Firefox additions to the global declarations are a type superset, not a runtime guarantee in Chrome.
isAvailableSearch(method?: ApiMethodName<typeof chrome.search>): booleanThe optional method is a native function name, for example isAvailableSearch("query").
Import the public ApiMethodName type from @addon-core/browser for your own typed helpers.
This checks callable presence without invoking the method; permissions and argument support still require handling.
Returns whether the search namespace exists on the API selected by browser().
For example, older Firefox versions can expose the namespace without supporting query.
import {isAvailableSearch} from "@addon-core/browser";
const available = isAvailableSearch();canQuerySearch(): booleanChecks search.query, used by querySearch() and the four destination helpers.
import {canQuerySearch, searchInNewTab} from "@addon-core/browser";
if (canQuerySearch()) {
await searchInNewTab("WebExtensions");
}canGetSearchEngines(): booleanChecks search.get, used for engine enumeration and the default-engine helper.
import {canGetSearchEngines, getSearchEngines} from "@addon-core/browser";
if (canGetSearchEngines()) {
const engines = await getSearchEngines();
console.log(engines);
}canSearchWithEngine(): booleanChecks search.search. This does not verify that a particular engine name exists.
import {canSearchWithEngine, getDefaultSearchEngine, canGetSearchEngines, searchWithEngine} from "@addon-core/browser";
if (canSearchWithEngine() && canGetSearchEngines()) {
const engine = await getDefaultSearchEngine();
if (engine) {
await searchWithEngine("WebExtensions", engine.name);
}
}querySearch(options: chrome.search.QueryInfo): Promise<void>Forwards {text, tabId?, disposition?} to native search.query with the user's default provider.
With no destination specified, the browser uses CURRENT_TAB. Text is passed unchanged; do not URL-encode it.
Callback failures through runtime.lastError, Promise rejections, and synchronous native errors reject the returned Promise.
import {querySearch} from "@addon-core/browser";
await querySearch({text: "café & extensions", disposition: "NEW_TAB"});getSearchEngines(): Promise<SearchEngine[]>Returns Firefox's engine list unchanged. It rejects if the method is absent or the native call fails. See Firefox search.get.
import {getSearchEngines} from "@addon-core/browser";
const engines = await getSearchEngines();
console.log(engines.map(engine => engine.name));searchWithEngine(text: string, engine: string, options?: SearchTargetOptions): Promise<void>Calls Firefox's search.search with {query: text, engine, ...destination}. With no destination, Firefox uses
NEW_TAB, unlike querySearch. An unknown engine rejects; this method does not substitute another provider.
It does not change the browser's default engine. disposition requires Firefox 111+; tabId works in older versions.
See Firefox search.search.
import {getSearchEngines, searchWithEngine} from "@addon-core/browser";
const engine = (await getSearchEngines()).find(item => item.isDefault);
if (engine) {
await searchWithEngine("WebExtensions", engine.name, {tabId: 42});
}searchInTab(text: string, tabId: number): Promise<void>Uses querySearch to search in an existing tab with the default provider. Does not create a tab first.
import {searchInTab} from "@addon-core/browser";
await searchInTab("WebExtensions", 42);searchInCurrentTab(text: string): Promise<void>Uses querySearch with CURRENT_TAB; the native browser decides which tab is current in the calling context.
import {searchInCurrentTab} from "@addon-core/browser";
await searchInCurrentTab("WebExtensions");searchInNewTab(text: string): Promise<void>Uses querySearch with NEW_TAB.
import {searchInNewTab} from "@addon-core/browser";
await searchInNewTab("WebExtensions");searchInNewWindow(text: string): Promise<void>Uses querySearch with NEW_WINDOW.
import {searchInNewWindow} from "@addon-core/browser";
await searchInNewWindow("WebExtensions");getDefaultSearchEngine(): Promise<SearchEngine | undefined>Finds the engine with isDefault: true in a fresh engine list. Returns undefined if none is listed.
Unavailable APIs and lookup failures reject; they are not treated as an empty list.
import {getDefaultSearchEngine} from "@addon-core/browser";
const engine = await getDefaultSearchEngine();
console.log(engine?.name);hasSearchEngine(name: string): Promise<boolean>Returns true only when a fresh list contains an exact, case-sensitive engine name. Returns false for a missing
engine, unavailable API, or lookup error. This predicate does not log and cannot guarantee that a later search succeeds.
import {hasSearchEngine} from "@addon-core/browser";
const installed = await hasSearchEngine("DuckDuckGo");Safari has no native Search API. Adding the search permission does not enable it. The availability/capability checks
return false; search operations, including destination helpers, reject. The package does not implement an automatic fallback.
An application can choose a provider itself and open its results URL with the existing createTab() helper:
import {createTab} from "@addon-core/browser";
const url = new URL("https://duckduckgo.com/");
url.searchParams.set("q", "café & WebExtensions");
await createTab({url: url.href});This explicitly searches DuckDuckGo, regardless of Safari's default provider, and does not change browser settings.
URLSearchParams encodes the query. Use updateTab(tabId, {url: url.href}) to navigate an existing tab instead.
See Tabs API documentation and Safari Tabs API compatibility.