# How to enable the SearXNG JSON API

By Das Digitale Momentum, published 2026-11-03

SearXNG answers in HTML until JSON output is enabled, and a request with `format=json` returns 403 Forbidden. This guide enables the JSON API in `settings.yml`, tests it with curl and connects the instance to AI agents with searxNcrawl.

## Why format=json returns 403 Forbidden

SearXNG serves a result format only if `settings.yml` lists it under `search.formats`. Its [search API documentation](https://docs.searxng.org/dev/search_api.html) says that a request for a format that is not enabled returns 403 Forbidden, and that many public instances have these formats disabled. The fix therefore needs an instance whose settings you control.

The HTML page of SearXNG is made for people in a browser. Scripts and AI agents need the same results as data: the JSON API returns the merged hits of every engine the instance queries, with their sources and scores, in one answer that a program can parse without scraping. Tools built on SearXNG, from command-line clients to MCP servers such as searxNcrawl, depend on it, which is why a missing `json` entry stops all of them with the same 403.

## Step 1: run SearXNG

If you have no instance yet, start one in a container as described in the [SearXNG Docker guide](https://docs.searxng.org/admin/installation-docker.html). The instance answers on port 8888, and its configuration in `./config/` is mounted as `/etc/searxng/` inside the container.

```
docker run --name searxng -d \
  -p 8888:8080 \
  -v "./config/:/etc/searxng/" \
  -v "./data/:/var/cache/searxng/" \
  docker.io/searxng/searxng:latest
```

The `-d` flag runs the container in the background, and `--name searxng` gives it the name that the restart in step 3 uses. Port 8888 on the host maps to port 8080 inside the container; if 8888 is taken, choose another host port and use it in every address on this page. The `data` directory keeps the cache between restarts.

## Step 2: enable JSON in settings.yml

Open `./config/settings.yml` and list `json` under `search.formats`, next to `html` for the browser interface. Format names are lower case; csv and rss are enabled the same way ([settings reference](https://docs.searxng.org/admin/settings/settings_search.html)).

```
search:
  formats:
    - html
    - json
```

## Step 3: restart and test

Restart the container so SearXNG reads the new settings, then ask for JSON. A JSON object with a `results` array means the API works; 403 Forbidden means the format list was not picked up.

```
docker restart searxng
curl -s 'http://localhost:8888/search?q=searxng&format=json' | head -c 300
```

## What the JSON response contains

The answer is one JSON object. `results` holds the hits: each with `url`, `title`, `content` (the snippet), the `engine` that ranked it first, every engine in `engines` that found it, a merged `score`, the `category` and, where an engine supplies it, `publishedDate`. Alongside the results, `answers`, `corrections`, `infoboxes` and `suggestions` carry what the engines add beyond plain links. Shortened to one result, an answer looks like this:

```
{
  "query": "searxng",
  "results": [
    {
      "url": "https://github.com/searxng/searxng",
      "title": "...",
      "content": "...",
      "engine": "google cse",
      "engines": ["google cse", "duckduckgo"],
      "score": 3.0,
      "category": "general",
      "publishedDate": null
    }
  ],
  "answers": [],
  "corrections": [],
  "infoboxes": [],
  "suggestions": [],
  "unresponsive_engines": [["brave", "Suspended: too many requests"]]
}
```

`unresponsive_engines` names every engine that failed for this query, with the reason. Upstream engines rate-limit or challenge a new instance from time to time; the other engines still answer, so the results stay usable.

Scripts and agents usually need only `url`, `title` and `content`; keep the other fields for ranking and debugging. Results found by several engines carry a higher `score`, a quick signal for where the engines agree.

## Query parameters of the JSON API

Both `/` and `/search` accept these parameters, as URL query parameters with GET or as form data with POST ([search API reference](https://docs.searxng.org/dev/search_api.html)):

- q: The search query; required
- format: json, csv or rss; each must be listed under `search.formats`, or the answer is 403
- categories: Comma-separated list of the categories to search, for example general or news
- language: Language code of the results; the default comes from `settings.yml`
- pageno: Page of the results, starting at 1
- time_range: day, month or year
- safesearch: 0, 1 or 2; the default comes from `settings.yml`

A GET request is enough for most uses: URL-encode the query, add `format=json` and fetch the next page with `pageno=2`. POST sends the same parameters as form data, so the query does not appear in the URL. To serve feed readers or spreadsheets as well, list `rss` or `csv` under `search.formats` too; each format is enabled on its own, as the 403 for csv in our test shows.

## If the test still fails

- 403 Forbidden: `json` is missing under `search.formats`, or the file you edited is not the one mounted as `/etc/searxng/settings.yml`.
- 429 Too Many Requests: the [limiter](https://docs.searxng.org/admin/searx.limiter.html), enabled with `server.limiter`, throttles suspicious clients. Add the address of your agent host to `pass_ip` in `/etc/searxng/limiter.toml`.
- No answer from another container: address SearXNG by its container name on a shared network, not by localhost.
- A public instance: use your own. Public instances often disable JSON and rate-limit automated requests.

## Keep the instance private

On its first start the container writes a short `settings.yml` with `use_default_settings: true` and a random `server.secret_key`; keep that key secret and leave the file in the mounted directory, so it survives updates of the image. SearXNG has no API keys, so anyone who reaches port 8888 can use your instance and the reputation of its address. Bind it to localhost or a private network, or put a reverse proxy with authentication in front of it.

If several agents or people share the instance, consider the limiter. It needs a [Valkey](https://valkey.io/) database configured in `settings.yml` and limits suspicious traffic per client address, so the upstream engines are less likely to block or challenge your instance. Clients you trust, such as the host of your agents, go into `pass_ip` in `limiter.toml` and keep unrestricted access. Behind a reverse proxy, forward the client address in `X-Forwarded-For` and `X-Real-IP`; otherwise every request looks as if it came from the proxy.

## Does SearXNG need an API key?

No. SearXNG has no API keys and no setting for one: whoever reaches the instance can query it. In our test a request without any key and a request with a made-up `X-API-Key` header both answered 200 with results. Guides that mention such a header describe a proxy or a wrapper in front of SearXNG, not SearXNG itself.

To limit who may search, put a reverse proxy with authentication in front of the instance. With [Caddy](https://caddyserver.com/) that takes a shared network, a password hash and a short Caddyfile. Create the network and the hash first:

```
docker network create searxng-net
docker network connect searxng-net searxng
docker run --rm caddy:2 caddy hash-password --plaintext 'choose-a-password'
```

Write the hash into a file named `Caddyfile` next to the `config` directory. The proxy listens on port 8080 in its container and forwards authenticated requests to the SearXNG container by its name:

```
:8080 {
	basic_auth {
		agent <hash from caddy hash-password>
	}
	reverse_proxy searxng:8080
}
```

```
docker run --name searxng-auth -d --network searxng-net \
  -p 8889:8080 -v "./Caddyfile:/etc/caddy/Caddyfile" caddy:2
curl -s -o /dev/null -w '%{http_code}\n' -u agent:choose-a-password \
  'http://localhost:8889/search?q=searxng&format=json'
```

In our run the proxy answered 401 without credentials and with a wrong password, and 200 with the right one. Then publish only the proxy: start SearXNG with `-p 127.0.0.1:8888:8080`, or without `-p` at all, so the port without a password is not reachable from other machines. searxNcrawl sends the credentials as basic authentication:

```
SEARXNG_URL=http://localhost:8889 SEARXNG_USERNAME=agent \
  SEARXNG_PASSWORD=choose-a-password uvx --from searxncrawl search "model context protocol"
```

Without the two variables the same search failed with the message that SearXNG authentication failed and that `SEARXNG_USERNAME` and `SEARXNG_PASSWORD` should be checked; with them it returned results. For a token in a header instead, for example the one of an API gateway, set `SEARXNG_EXTRA_HEADERS`.

## Connect the JSON API to AI agents

searxNcrawl is an open-source MCP server that uses this JSON API: point `SEARXNG_URL` at the instance and your agents get the `search` tool, plus `crawl` and `crawl_site` for page content in Markdown. Try a search from the shell first:

```
SEARXNG_URL=http://localhost:8888 uvx --from searxncrawl search "model context protocol" --max-results 5
```

Then run `uvx searxncrawl setup` to register the server with Claude Code, Codex, Gemini CLI, Cursor, VS Code and Claude Desktop. The [SearXNG MCP server page](https://www.searxncrawl.io/searxng-mcp-server/) shows the setup for each client.

searxNcrawl sends `format=json` together with language, safe search and page with every search, so the only change on the SearXNG side is the format list. If the instance sits behind basic authentication, set `SEARXNG_USERNAME` and `SEARXNG_PASSWORD`; for a token in a header, for example the one of a tunnel or gateway, use `SEARXNG_EXTRA_HEADERS`, which can read the value from a file.

## How we tested this guide

We ran every step on 2026-11-04 with the image `docker.io/searxng/searxng:latest` (SearXNG 2026.9.15) and searxNcrawl 0.33.1. Before step 2, `format=json` answered 403 Forbidden; after the restart it answered 200 with 20 results, while `format=csv`, still not listed, kept answering 403. For the section on API keys we put Caddy 2.11.7 with basic authentication in front of the same instance.

We reviewed these facts on 2026-11-04 against the linked sources. Our [editorial and corrections policy](https://www.searxncrawl.io/about/#editorial) says how we keep them current and fix errors.

## Frequently asked questions

### Is the SearXNG JSON API enabled by default?

No. SearXNG serves HTML until `json` is listed under `search.formats` in `settings.yml`; until then a request with `format=json` answers 403 Forbidden.

### How do I fix 403 Forbidden for format=json in SearXNG?

Add `json` under `search.formats` in the `settings.yml` that your instance actually loads (in the container `/etc/searxng/settings.yml`), restart SearXNG and repeat the request with `format=json`.

### Can I use the JSON API of a public SearXNG instance?

Rarely. The SearXNG documentation warns that many public instances have these formats disabled, and public instances also rate-limit automated requests. Run your own instance for agents and scripts.

### Does the SearXNG JSON API need an API key?

No. SearXNG has no API keys: whoever can reach the instance can query it. Keep it on a private network or behind authentication; searxNcrawl can send `SEARXNG_USERNAME` and `SEARXNG_PASSWORD` or extra headers.

Updated: 2026-11-04

---

Source: <https://www.searxncrawl.io/searxng-json-api/>
