The first time a large language model invented a statistic in my draft about search APIs, I knew it was time to ground the thing against real web results. A self-hosted SearXNG JSON API now sits between the model and the internet for our blog pipeline, replacing a paid :online markup and costing nothing at the volume we run.
What matters more than free: we designed the integration so a search failure degrades the output gracefully rather than breaking the pipeline. That is the piece most builders skip, and it is the piece that makes self-hosting safe to depend on overnight.
SearXNG JSON API Endpoint
SearXNG exposes a single /search endpoint. You tell it you want structured output with format=json, and it hands back a clean payload instead of a rendered results page. The request below is the exact call our pipeline makes—no middleware, no abstraction.
GET https://searxng.techpotions.app/search?format=json&q={query}&categories={categories}&time_range={time_range}
Parameters that earn their keep
Not every parameter matters for LLM grounding. These are the three we actually use in production.
| Parameter | Purpose | Example |
|---|---|---|
q |
The search query—exactly what you would type into a search box | q=vector databases compared 2025 |
categories |
Scopes the engine set. general hits broad web results; mix with news, science, or files when the query calls for it |
categories=general,news |
time_range |
Narrows results by recency. Critical when grounding the model against what is ranking right now | time_range=month |
format |
Must be json. Without it, you get an HTML page you do not want to parse |
format=json |
time_range accepts day, week, month, or year. For our blog pipeline, month is the default. When a query targets a fast-moving topic, we drop to week. When it is evergreen, year keeps the signal broad.
The raw TypeScript call
async function searchSearXNG(query: string, timeRange = "month"): Promise<SearchResult[]> {
const base = "https://searxng.techpotions.app/search";
const params = new URLSearchParams({
format: "json",
q: query,
categories: "general",
time_range: timeRange,
});
const response = await fetch(`${base}?${params}`);
if (!response.ok) return [];
try {
const data = await response.json();
return (data.results ?? []).slice(0, 10);
} catch {
return [];
}
}
What It Replaced: Paid Search Markup
Before the SearXNG instance, our blog pipeline used OpenRouter's :online search markup. The model would tag a prompt with :online, and OpenRouter would inject web results from a paid search backend. It worked. It also cost money on every query and leaked portions of our content roadmap into a third-party search API.
That privacy piece is not theoretical. Our pipeline surfaces what is currently ranking for a target query so drafts are framed against the real SERP rather than the model's stale training data. The search queries are literally a list of the topics we plan to publish over the next quarter. Keeping those queries inside infrastructure we own matters.
Self-hosting SearXNG makes the grounding layer free at the volume we run, but the real win is that our content roadmap stays ours. For AI services where the prompts themselves are proprietary, this pattern applies just as directly.
The Resilience Pattern: Degrade, Don't Fail
Search is an enhancement to generation, not a precondition for it. A draft written without fresh sources is slightly worse. A draft that never gets written because a cron job exploded at 3am is an editor staring at an empty CMS in the morning.
Our searchSearXNG wrapper never throws. Three failure modes all converge on the same outcome: an empty array.
- Non-OK response — upstream engine timed out, instance is restarting, network blip
- Network error — DNS failure, box is unreachable, TLS expiry
- Malformed JSON — upstream returned an error page, or a reverse proxy injected something unexpected
Every path returns []. The caller receives zero results instead of an exception in the stack. If your grounding call can throw, your nightly generation job now has a hard dependency on a service you self-host on hardware you are not watching at 3am.
This is the decision worth the whole integration. Most AI pipelines wire up a search API and then wrap it in a retry loop, as though a retry will fix an instance that has been down for four hours. Retries are for transient failures. This pattern handles the persistent ones.
// No try/catch at the call site. No retry loop. No alerts.
const sources = await searchSearXNG(query);
// sources is always an array. Could have 10 results. Could have 0.
// Build the prompt either way.
const groundedPrompt = buildPrompt(query, sources);
Two Jobs the SearXNG JSON API Handles
1. SERP-aware framing
The first call in the pipeline searches the target query and returns what is ranking right now. That surface shapes the draft outline: which angles the top results take, what headings they use, what questions they answer. The model sees the real competitive landscape instead of guessing from stale weights.
2. Source grounding
The second call searches for authoritative sources on each factual claim the draft makes. The model receives real URLs and real snippets and weaves them into the prose. This dramatically reduces hallucinated statistics and invented quotes. We wrote about the broader pipeline approach in stopping AI blog pipeline hallucinations.
The Honest Caveat
SearXNG is a metasearch aggregator. It does not crawl the web itself. Each query fans out to upstream engines—Google, Bing, DuckDuckGo, and others depending on configuration—and those engines rate-limit. Some block datacenter IPs. Result quality fluctuates in a way a paid search API with a dedicated crawl index does not.
You own the uptime. You configure the instance, you watch the logs, you handle the inevitable day an upstream engine changes its response format and breaks result parsing. This is the right trade when grounding is a nice-to-have enhancement to generation. It is the wrong trade when search results are the product.
Spend the dollars when every result matters. Self-host when the fallback is a perfectly functional model prompt without web context.
Getting SearXNG Running
SearXNG ships as a Docker image with a single container. Bring a domain, add a Let's Encrypt reverse proxy, and set the SEARXNG_SECRET environment variable to a random value for encryption. The official docs cover the compose file.
Our instance at searxng.techpotions.app runs behind a Traefik reverse proxy with automatic TLS. The configuration file enables JSON output by default and limits the engine set to the ones that reliably return results from the region our queries target.
For builders integrating search into AI products, we offer AI consulting and development services that include grounding-layer architecture. If you are starting from scratch, our project launch framework covers the patterns we reuse across client builds.
FAQ
What is the SearXNG JSON API?
It is the structured output mode of a self-hosted SearXNG metasearch instance. You set format=json on the /search endpoint, and the server returns a JSON object with a results array containing titles, URLs, snippets, and engine metadata—no HTML parsing required.
Is the SearXNG JSON API free?
Yes. The software is open source. You pay only for the infrastructure you run it on. At modest query volumes, a small VPS handles the load without additional search API costs. The trade is that you own the operational burden: uptime, engine configuration, and the occasional upstream breakage.
Why use SearXNG instead of a paid search API for AI agents?
Two reasons make the self-hosted path compelling. First, search queries stay private on your infrastructure—relevant when those queries reveal your content roadmap or proprietary prompts. Second, the resilience pattern of degrading to an empty result set rather than throwing means a search outage does not fail the generation job. Paid APIs solve the uptime problem; they do not automatically solve the architectural one.
Can I use the SearXNG JSON API in production?
Yes, with the right failure design. Treat search results as an optional enhancement, not a required input. When results are available, the output is stronger. When the instance is down or upstream engines block, the pipeline runs anyway. That architecture is what makes self-hosting production-safe for overnight automation.
Top comments (0)