Skip to content
Back to top
View this page
Edit this page

# Plugin Development

Plugins can extend or replace functionality of various components of SearXNG.

Entry points (hooks) define when a plugin runs. Right now only three hooks are implemented. So feel free to implement a hook if it fits the behaviour of your plugin / a plugin doesn’t need to implement all the hooks.

- pre search: `Plugin.pre_search`

- post search: `Plugin.post_search`

- on each result item: `Plugin.on_result`

Below you will find some examples, for more coding examples have a look at the built-in plugins git://searx/plugins/ or Only show green hosted results.

## Add Answer example

Here is an example of a very simple plugin that adds a “Hello World” into the answer area:

    from flask_babel import gettext as _
    from searx.plugins import Plugin
    from searx.result_types import Answer

    class MyPlugin(Plugin):

        id = "hello world"

        def __init__(self, plg_cfg):
            super().__init__(plg_cfg)
            self.info = PluginInfo(id=self.id, name=_("Hello"), description=_("demo plugin"))

        def post_search(self, request, search):
            return [ Answer(answer="Hello World") ]

## Filter URLs example

The `Result.filter_urls(..)` can be used to filter and/or modify URL fields. In the following example, the filter function `my_url_filter`:

    def my_url_filter(result, field_name, url_src) -> bool | str:
        if "google" in url_src:
            return False              # remove URL field from result
        if "facebook" in url_src:
            new_url = url_src.replace("facebook", "fb-dummy")
            return new_url            # return modified URL
        return True                   # leave URL in field unchanged

is applied to all URL fields in the `Plugin.on_result` hook:

    class MyUrlFilter(Plugin):
        ...
        def on_result(self, request, search, result) -> bool:
            result.filter_urls(my_url_filter)
            return True

## Implementation

class searx.plugins.Plugin(plg_cfg: PluginCfg)

  Abstract base class of all Plugins.

  id: str = ''

    The ID (suffix) in the HTML form.

  active: ClassVar[bool]

    Plugin is enabled/disabled by default (`PluginCfg.active`).

  keywords: list[str] = []

    Keywords in the search query that activate the plugin. The keyword is the first word in a search query. If a plugin should be executed regardless of the search query, the list of keywords should be empty (which is also the default in the base class for Plugins).

  info: PluginInfo

    Information about the plugin, see `PluginInfo`.

  log: Logger

    A logger object, is automatically initialized when calling the constructor (if not already set in the subclass).

  init(app: flask.Flask) → bool

    Initialization of the plugin, the return value decides whether this plugin is active or not. Initialization only takes place once, at the time the WEB application is set up. The base method always returns `True`, the method can be overwritten in the inheritances,

    - `True` plugin is active

    - `False` plugin is inactive

  pre_search(request: SXNG_Request, search: SearchWithPlugins) → bool

    Runs BEFORE the search request and returns a boolean:

    - `True` to continue the search

    - `False` to stop the search

  on_result(request: SXNG_Request, search: SearchWithPlugins, result: Result) → bool

    Runs for each result of each engine and returns a boolean:

    - `True` to keep the result

    - `False` to remove the result from the result list

    The `result` can be modified to the needs.

    Hint

    If `Result.url` is modified, `Result.parsed_url` must be changed accordingly:

        result["parsed_url"] = urlparse(result["url"])

  post_search(request: SXNG_Request, search: SearchWithPlugins) → None | list[Result | LegacyResult] | EngineResults

    Runs AFTER the search request. Can return a list of `Result` objects to be added to the final result list.

class searx.plugins.PluginInfo(id: str, name: str, description: str, preference_section: Literal['general', 'ui', 'privacy', 'query'] | None='general', examples: list[str] = <factory>, keywords: list[str] = <factory>)

  Object that holds information about a plugin, these infos are shown to the user in the Preferences menu.

  To be able to translate the information into other languages, the text must be written in English and translated with `flask_babel.gettext`.

  id: str

    The ID-selector in HTML/CSS #<id>.

  name: str

    Name of the plugin.

  description: str

    Short description of the answerer.

  preference_section: Literal['general', 'ui', 'privacy', 'query'] | None = 'general'

    Section (tab/group) in the preferences where this plugin is shown to the user.

    The value `query` is reserved for plugins that are activated via a keyword as part of a search query, see:

    - `PluginInfo.examples`

    - `Plugin.keywords`

    Those plugins are shown in the preferences in tab Special Queries.

  examples: list[str]

    List of short examples of the usage / of query terms.

  keywords: list[str]

    See `Plugin.keywords`

class searx.plugins.PluginStorage

  A storage for managing the plugins of SearXNG.

  plugin_list: set[Plugin]

    The list of `Plugins` in this storage.

  load_settings(cfg: dict[str, dict[str, Any]])

    Load plugins configured in SearXNG’s settings plugins:.

  register(plugin: Plugin)

    Register a `Plugin`. In case of name collision (if two plugins have same ID) a `KeyError` exception is raised.

  init(app: flask.Flask) → None

    Calls the method `Plugin.init` of each plugin in this storage. Depending on its return value, the plugin is removed from this storage or not.

  post_search(request: SXNG_Request, search: SearchWithPlugins) → None

    Extend `search.result_container <searx.results.ResultContainer`> with result items from plugins listed in `search.user_plugins`.

class searx.plugins.PluginCfg(active: bool = False)

  Settings of a plugin.

      mypackage.mymodule.MyPlugin:
        active: true

  active: bool = False

    Plugin is active by default and the user can opt-out in the preferences.