Skip to main content

hermes-yandex-search-api

PyPI version CI E2E (live) Coverage CodeFactor Ruff

Give your Hermes Agent first-class Yandex search. This plugin wires the Yandex Search API into Hermes as a drop-in web-search backend and adds a grounded-answer tool — excellent results for Russian-language queries, on infrastructure you may already have in Yandex Cloud.

  • 🔎 yandex web-search backend — routes Hermes' built-in web_search tool to Yandex web search (links with titles and snippets). Nothing new for the model to learn.
  • 💬 yandex_generative_search tool — a single grounded answer synthesised from live web sources, with the source URLs it cites.

Quick start

# 1. Install into Hermes (other ways: see Installing the plugin). It asks for
#    your Yandex Cloud API key and folder id (see "Getting a token" below)
hermes plugins install akinfold/hermes-yandex-search-api/hermes_yandex_search --enable

# 2. Skipped the questions, or installed another way? Add the credentials yourself:
printf 'YANDEX_API_KEY=%s\nYANDEX_FOLDER_ID=%s\n' 'your-api-key' 'your-folder-id' >> ~/.hermes/.env

# 3. Select Yandex as the web-search backend
hermes config set web.search_backend yandex

Steps 1 and 3 leave this in ~/.hermes/config.yaml, if you would rather write it yourself:

web:
  search_backend: yandex
plugins:
  enabled:
    - yandex

That's it — web_search now goes through Yandex, and the yandex_generative_search tool is available to the agent. Don't have an API key and folder id yet? See Getting a Yandex Search API token. Other ways to install, and how to upgrade: see Installing the plugin into Hermes.

Why these two search modes

The Yandex Search API offers several modes — classic web search, generative search, image search, deferred (async) web search, and Wordstat keyword statistics. This plugin deliberately wires up the two that fit an autonomous agent, each in the shape Hermes expects:

  • Web search → a web_search backend. Hermes already ships a web_search tool whose backend returns a list of {title, url, description} results. The classic Yandex web search maps onto that contract exactly, so the model can use the search tool it already knows without learning a new one.
  • Generative search → its own tool. Generative search returns a synthesised answer with citations, not a list of links. That is a fundamentally different result shape, so it is exposed as a distinct yandex_generative_search tool. It is the best fit when the agent wants a direct, grounded answer to a factual question.

The other modes are intentionally left out: image search returns image URLs an agent cannot usefully consume, Wordstat is SEO keyword analytics unrelated to agentic search, and deferred web search has a multi-minute latency that is unusable for interactive turns. All of them can be added later on top of the same YandexSearchClient if a use case appears.

About the Yandex Search API

The Yandex Search API is Yandex Cloud's paid programmatic access to Yandex web search and its generative answer engine. Requests are authenticated with a Yandex Cloud API key and are billed to the Yandex Cloud folder that owns the key. Both search modes used here run synchronously:

  • POST /v2/web/search returns Base64-encoded XML search results.
  • POST /v2/gen/search returns a JSON grounded answer with cited sources.

See the pricing and quotas pages for current limits (roughly 10k web requests/hour and 1k generative requests/hour by default).

Requirements

  • Hermes Agent >= 0.18.1; earlier releases do not register the yandex web-search backend. Checked on every pull request, weekly, and before every release against Hermes v2026.9.24, the latest Hermes release and Hermes main, each installed by its official installer.

  • Python >= 3.11; the tests run on 3.11–3.14.

  • The Python package httpx >= 0.24, which Hermes itself depends on. Option C (PyPI) installs it; options A and B use the copy Hermes already has.

  • Expat 2.4.5 or newer in the Python that runs Hermes. On an older Expat the plugin refuses to parse at all, naming the version it found.

    Hermes' current installer brings its own Python, with a recent Expat. To check it anyway, use the interpreter <environment>/bin/python, where <environment> is the packages.venv.environment value in ~/.hermes/installs/<id>/facts.json. A Hermes in the older layout (see Option C below) runs on the Python its installer found:

    ~/.hermes/hermes-agent/venv/bin/python -c "import pyexpat; print(pyexpat.EXPAT_VERSION)"
    

    Every CPython release from 3.11.0 bundles a newer Expat. A distribution's own Python uses the system libexpat instead, and current distributions ship one new enough: 2.5.0 on Debian 12 (where the installer of Hermes v2026.9.24 picks the system python3.11) and RHEL 9, 2.4.7 on Ubuntu 22.04. Older releases ship an older one: RHEL 8 before its 2.5.0 update, 2.2.10 on Debian 11, 2.2.9 on Ubuntu 20.04. These numbers come from the distributions' package versions, not from CI. Distributions backport security fixes without raising the version, and the plugin goes by the number alone.

    On an older Expat, hermes update moves Hermes onto the current installer's own Python (Option C users: switch to Option A first, see below). To stay on the older layout, run ~/.hermes/bin/uv python install 3.11 and re-run the v2026.9.24 installer, which then builds its venv on uv's Python.

  • The plugin parses Yandex's XML with the standard library. It refuses, saying why, a response that declares a DTD or is not UTF-8, and an XML document larger than 2 MiB; the largest live 100-result report measured came to about 445 KB.

  • A Yandex Cloud account with the Search API enabled, an API key, and the folder id that owns it.

Getting a Yandex Search API token

  1. Create or open a Yandex Cloud account and a folder (catalog). Note its folder id — you can copy it from the console URL or with the CLI: how to get the folder id.
  2. Create a service account in that folder and grant it the search-api.webSearch.user role.
  3. Create an API key for that service account (Console → the service account → API keys → Create API key), or via CLI:
    yc iam api-key create --service-account-name <sa-name> --format json
    
    Copy the secret value — this is your YANDEX_API_KEY.
  4. Make sure the Search API is enabled for the folder and that billing is active.

You now have the two values the plugin needs: YANDEX_API_KEY and YANDEX_FOLDER_ID.

Installing the plugin into Hermes

hermes plugins install akinfold/hermes-yandex-search-api/hermes_yandex_search --enable

Note the /hermes_yandex_search at the end. The plugin lives in that directory, not at the repository root, and Hermes reads the manifest from whatever you point it at. Name the directory and the install is a plugin: Hermes prompts for YANDEX_API_KEY and YANDEX_FOLDER_ID, installs under the manifest name yandex, and --enable enables that name. It also scans only that directory, so the tests and workflows in this repository stay out of the security report.

Point it at the repository root instead and Hermes copies a directory with no manifest and no register(ctx) in it: it warns that it "may not be a valid Hermes plugin", asks for no credentials, and does not enable the plugin. hermes plugins list then still shows yandex — the package nested in the clone is found — but shows it as not enabled, which is the symptom to look for. If you installed that way, remove ~/.hermes/plugins/hermes-yandex-search-api and install again with the directory named.

Option B — drop-in directory

From a clone of this repository, copy the plugin directory into your Hermes plugins folder under the web category, then enable it:

mkdir -p ~/.hermes/plugins/web
cp -r hermes_yandex_search ~/.hermes/plugins/web/yandex
hermes plugins enable yandex

Or download hermes-yandex-search-plugin-<version>.zip from a GitHub Release and unzip it there instead:

unzip hermes-yandex-search-plugin-<version>.zip -d ~/.hermes/plugins/web/
hermes plugins enable yandex

Either way you end up with ~/.hermes/plugins/web/yandex/plugin.yaml. Nothing asks for credentials on this path — add them to ~/.hermes/.env as in the Quick start, and select the backend the same way.

Options A and B install no dependencies. Both copy the plugin's sources only, and the plugin directory declares nothing for Hermes to install. It needs nothing more: besides the standard library the plugin uses only httpx, one of Hermes' own core dependencies. The install check confirms that Hermes has it on every pull request, weekly, and before every release, on Hermes v2026.9.24, the latest Hermes release and main.

Option C — from PyPI

For a Hermes whose Python environment you manage yourself — your own virtualenv, a Nix build: install hermes-yandex-search-api into that environment, then enable the plugin. Hermes finds it through the hermes_agent.plugins entry point, and the package brings httpx with it.

hermes plugins enable yandex

Not for a standard Hermes install. Since 24 September 2026 the official installer runs Hermes from environments its package manager builds and replaces, and Hermes does not support adding packages to them by hand: use Option A or B.

A Hermes in the older layout — one that runs from ~/.hermes/hermes-agent/venv and keeps its own uv in ~/.hermes/bin, with no ~/.hermes/installs — takes the package like this:

~/.hermes/bin/uv pip install --python ~/.hermes/hermes-agent/venv/bin/python hermes-yandex-search-api
hermes plugins enable yandex

That layout is what the installer of Hermes v2026.9.24, the last release to build it, sets up, and what anything installed before that date and not updated since looks like. Tell it by those directories, not by the version number Hermes reports.

Switch such an install to Option A before you run hermes update: the update moves Hermes onto the new environments, and a package installed this way does not come along. The loss is silent: yandex stays enabled in the configuration, but the search backend and its tool are gone, and neither the update nor hermes doctor mentions it. If you have already updated, run the Option A install now: it brings them back and keeps your credentials in ~/.hermes/.env. A bare pip install hermes-yandex-search-api never reached Hermes at all — the pip on your PATH belongs to some other Python.

Nothing asks for credentials on this path — add them to ~/.hermes/.env as in the Quick start, and select the backend the same way.

Upgrading

Upgrade the way you installed. Option A — Hermes v2026.9.24 (0.21.5) and later install the new version from the source they recorded:

hermes plugins update yandex

Older Hermes cannot update an install from a plugin directory: run the install command again with --force, which installs what the default branch holds now, replaces the installed copy, and keeps your credentials and backend selection.

hermes plugins install akinfold/hermes-yandex-search-api/hermes_yandex_search --enable --force

An install pinned to a commit with --ref stays on that commit through both: hermes plugins update refuses to move it, and --force without a new --ref installs the same commit again. hermes plugins list shows the pin in its Source column, as git pinned@ and the first eight characters of the commit. To move it, run the install command with --force and the new commit, its full 40-character SHA, in place of <commit>:

hermes plugins install akinfold/hermes-yandex-search-api/hermes_yandex_search --enable --force --ref <commit>

Each of these scans the new version again. Unlike a first install, none of them stops to ask when Hermes' security scan reports a caution: for an install from a plugin directory, as here, they accept it and go on. A dangerous verdict still stops them.

Option B from a clone — pull it, then copy the directory's contents over the installed copy. Running the cp -r above again would put the new copy inside the old one instead:

cp -r hermes_yandex_search/. ~/.hermes/plugins/web/yandex/

Option B from a release — unzip the new release's archive over the old one:

unzip -o hermes-yandex-search-plugin-<version>.zip -d ~/.hermes/plugins/web/

Option C — upgrade the package in the same environment: in the older layout,

~/.hermes/bin/uv pip install --upgrade --python ~/.hermes/hermes-agent/venv/bin/python hermes-yandex-search-api

Options A and B and their upgrades, moving a pinned install included, are checked before every release by installing the build into a real Hermes exactly as written here: Hermes v2026.9.24, the latest release, and main, each set up by its own official installer. The Option C install is checked against Hermes v2026.9.24, the last release whose installer builds the older layout. See Checking the install paths.

Configuring the token in Hermes

The plugin opens no file itself. It reads its credentials through Hermes' get_provider_env, the helper Hermes' own web backends use, which resolves the active profile's settings and secrets: its environment variables first, then its .env file (~/.hermes/.env for the default profile). The simplest, persistent option is to put them in ~/.hermes/.env:

YANDEX_API_KEY=your-api-key
YANDEX_FOLDER_ID=your-folder-id
# Optional: market/domain, default SEARCH_TYPE_RU.
# One of SEARCH_TYPE_RU | SEARCH_TYPE_COM | SEARCH_TYPE_TR | SEARCH_TYPE_KK | SEARCH_TYPE_BE | SEARCH_TYPE_UZ
YANDEX_SEARCH_TYPE=SEARCH_TYPE_RU
# Optional: override the API base URL (for a private gateway / testing).
# YANDEX_SEARCH_API_URL=https://searchapi.api.cloud.yandex.net

hermes plugins install akinfold/hermes-yandex-search-api/hermes_yandex_search prompts for both values, because the manifest declares them. Installing any other way, or declining the prompt, leaves you to add them to ~/.hermes/.env yourself, as shown above.

Selecting Yandex as the web-search backend

To route Hermes' built-in web_search tool to Yandex, set the backend:

hermes config set web.search_backend yandex

That writes web.search_backend into ~/.hermes/config.yaml, which, with the plugin enabled by Option A or C, then reads:

web:
  search_backend: yandex
plugins:
  enabled:
    - yandex

After Option B the entry reads web/yandex instead. Hermes names a plugin in a category folder by its path under ~/.hermes/plugins: hermes plugins enable yandex finds the plugin by its name, enables it as web/yandex, and says Plugin web/yandex enabled. Writing the file yourself after Option B, list it that way:

plugins:
  enabled:
    - web/yandex

The yandex backend is search-only: the Yandex Search API returns result snippets, not page content, so the plugin does not serve web_extract. Page extraction keeps using whichever extract-capable backend Hermes resolves, which is why the selection above sets web.search_backend rather than web.backend.

The yandex_generative_search tool becomes available as soon as the plugin is enabled — no extra configuration needed. It is registered in the yandex_search toolset, which Hermes enables by default; you can switch it off (or back on) per platform with hermes tools.

The yandex_generative_search tool

Parameter Type Required Description
query string yes The question to answer. Must be a non-empty string.
sites array of strings no Site domains to restrict the answer's sources to, e.g. ["example.com"]. The Yandex API accepts up to 5; the plugin forwards the list as it is. Omit it to search the whole web.

The search market is not a tool parameter; it comes from YANDEX_SEARCH_TYPE.

The tool returns a JSON string. On success:

{
  "success": true,
  "answer": "Paris is the capital of France.",
  "sources": [{"url": "https://en.wikipedia.org/wiki/Paris", "title": "Paris", "used_text": ""}],
  "search_queries": ["capital of France"],
  "fixed_query": "",
  "is_answer_rejected": false,
  "is_bullet_answer": false
}
  • answer — the synthesised answer text.
  • sources — the sources the answer cites (url, title, used_text; the last two may be empty).
  • search_queries — the queries Yandex actually ran.
  • fixed_query — the typo-corrected query, or an empty string.
  • is_answer_rejected — true when Yandex declined to answer.
  • is_bullet_answer — true when the answer is formatted as a bullet list.

The tool never raises. Any failure — missing credentials, invalid arguments, an HTTP or API error — comes back as {"success": false, "error": "<message>"}.

Configuration reference

Variable Required Default Description
YANDEX_API_KEY yes – Yandex Cloud API key.
YANDEX_FOLDER_ID yes – Yandex Cloud folder ("catalog") id.
YANDEX_SEARCH_TYPE no SEARCH_TYPE_RU Search market/domain enum.
YANDEX_SEARCH_API_URL no https://searchapi.api.cloud.yandex.net API base URL override.

YANDEX_SEARCH_TYPE applies to both web and generative search and is case-sensitive: an unrecognised value makes every call fail with an error naming the accepted values.

The rest of the request options are fixed and cannot be configured. Every request times out after 30 seconds, and on expiry the call fails with an error such as HTTP request to /v2/gen/search failed: .... Web search returns at most the number of results Hermes asks for (clamped to 1–100), one document per site group, first page only. The family filter stays at FAMILY_MODE_MODERATE, Yandex typo correction is always on for both modes, and region and snippet localisation are left at the Yandex defaults for the selected market.

Development

python -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'

ruff check .          # lint
ruff format --check . # code style
pytest                # unit tests (live E2E tests are deselected by default)
radon cc -s -n C hermes_yandex_search  # complexity gate; must print nothing

The package layout separates a Hermes-independent API client from the host integration:

  • hermes_yandex_search/client.py — the YandexSearchClient (pure HTTP + parsing, no Hermes imports).
  • hermes_yandex_search/provider.py — the yandex web-search backend provider.
  • hermes_yandex_search/generative.py — the yandex_generative_search tool.
  • hermes_yandex_search/config.py — builds a client from the settings Hermes resolves.
  • hermes_yandex_search/__init__.py — register(ctx), the plugin entry point.

Running the live E2E tests

The E2E suite (marked e2e, deselected by default) hits the live Yandex Search API and, when hermes-agent is installed, a live Hermes host.

Locally

Store your API key in a file (created with restrictive permissions), and the folder id in a companion file:

umask 077 && printf '%s' 'your-yandex-search-api-key' > ~/.yandex-search-api-key
umask 077 && printf '%s' 'your-yandex-folder-id'      > ~/.yandex-folder-id

(Alternatively, export YANDEX_API_KEY and YANDEX_FOLDER_ID in your shell — environment variables take precedence over the files.) Then:

pytest -m e2e -v

The Hermes-host test (tests/e2e/test_live_hermes.py) skips automatically unless Hermes is importable. The hermes-agent on PyPI is far behind the Hermes users run, so run the suite in the Python of a real Hermes install instead, as the workflow below does: tests/install/hermes_env.py finds that Python in either layout the installer makes, this checkout goes on its path, and pytest goes on it after Hermes' own packages, so Hermes keeps its own versions. Nothing is installed into Hermes' environment, which Hermes builds itself. From the root of this checkout, with the development virtualenv active:

site=$(mktemp -d)
python -m pip install --quiet --target "$site" pytest
PYTHONPATH="$PWD" "$(python tests/install/hermes_env.py)" -c 'import sys; sys.path.append(sys.argv.pop(1)); import pytest; raise SystemExit(pytest.main(sys.argv[1:]))' "$site" tests/e2e -m e2e -v

For a Hermes you run from a checkout of your own rather than the installer's, put the Python that checkout runs on in place of $(python tests/install/hermes_env.py).

On GitHub Actions

The E2E (live) workflow (.github/workflows/e2e.yml) is manual (Actions → E2E (live) → Run workflow). It reads credentials from a GitHub Environment so they are never committed to the repo. It runs the plugin inside a real Hermes, installed by its official installer: the latest Hermes release by default, and its hermes input switches to Hermes main or to no Hermes at all. With Hermes, the tests run in Hermes' own Python, with this checkout on its path rather than installed into Hermes' environment, and the run fails outright if the plugin cannot import Hermes, rather than skipping the Hermes-host test.

If you fork this repository and want to run the live E2E workflow, set up the Environment once:

  1. Open Settings → Environments → New environment and name it yandex-e2e (the name the workflow references).
  2. Under that environment, add two secrets:
    • YANDEX_API_KEY — your Yandex Cloud API key.
    • YANDEX_FOLDER_ID — your Yandex Cloud folder id.
  3. Optionally add an environment variable YANDEX_SEARCH_TYPE (e.g. SEARCH_TYPE_COM) to change the default market. You can also override it per-run via the workflow input.
  4. (Recommended) Add required reviewers to the environment so live runs must be approved — this gates access to the paid API.
  5. Run the workflow from the Actions tab.

Checking the install paths

The install-marked tests in tests/install/ install the built plugin into a real Hermes, set up by its official installer, by each option in Installing the plugin into Hermes — both forms of Option B and the upgrades included, running the README's own commands — and then ask Hermes what it loaded: the plugin must be listed as enabled, load without error, give the agent yandex_generative_search, and, with the backend selected, be the provider web_search calls. They also check that installing from the repository root still looks the way this README describes. A fast unit test keeps the commands in the tests and in this README identical.

The Install check workflow runs them against Hermes v2026.9.24, the last release in the older layout, against the latest Hermes release, and against Hermes main — on every pull request, once a week, and on every release tag before anything is published: the GitHub Release and the PyPI upload both wait for it. They install into a real ~/.hermes, so run them yourself only in a container or VM — see the docstring of tests/install/test_install.py.

Part of a family of Yandex plugins for Hermes Agent:

  • hermes-yandex-disk — browse, read, write, and share files on Yandex Disk (REST API).
  • hermes-yandex-mail — search, read, flag, move, and delete Yandex Mail messages over IMAP, and send over SMTP when sending is switched on.
  • hermes-yandex-calendar — list, create, update, respond to, move, and delete Yandex Calendar events (CalDAV).

Contributing

Contributions are welcome — see CONTRIBUTING.md for the dev setup and checks to run.

License

MIT © Roman Akinfeev

Metadata

Release files for hermes-yandex-search-api 0.1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for hermes-yandex-search-api 0.1.5
File Size Uploaded Provenance
hermes_yandex_search_api-0.1.5.tar.gz 44.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for hermes-yandex-search-api 0.1.5
File Interpreter ABI Platform
hermes_yandex_search_api-0.1.5-py3-none-any.whl Python 3 none any Details

Total release size: 69.7 kB

Release files / hermes_yandex_search_api-0.1.5.tar.gz

Download URL hermes_yandex_search_api-0.1.5.tar.gz
Size 44.0 kB
Tags Source
SHA-256 checksum
How to use checksums
8933e24e2b63c498370aee0db06634ff5ab90168501f44149b7625827228e19c
BLAKE2b-256 checksum
How to use checksums
1583b1dfcfcc9807dd1b381b12c43b7c5b506c260200f238275e50d787b94d63
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / hermes_yandex_search_api-0.1.5-py3-none-any.whl

Download URL hermes_yandex_search_api-0.1.5-py3-none-any.whl
Size 25.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4ee3c6bf58939538b03d54e7f5339b4050d81e468faafb55c6bc70e8c9bb1ed8
BLAKE2b-256 checksum
How to use checksums
133ef8f4e88da19cdb089420e4f9b25dc8446192a201b489cecad85388c56a15
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Security for hermes-yandex-search-api 0.1.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for hermes_yandex_search_api-0.1.5.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page