Search API fallback for AI agents with Mux
How to set up a search API fallback for AI agents with Mux's models list, so a provider outage, rate limit or timeout never stops a run mid-task.
A search API fallback is an ordered list of search providers that a request walks through until one answers. In Mux you pass that list as models, up to five model ids, and the gateway tries each in order, refunds failed attempts and returns the first success along with an attempts array that shows what happened. An agent wired to a single provider stops when that provider has a bad minute; an agent with a fallback list keeps going and pays only for the model that answered.
Why one search provider is a single point of failure#
An agent that calls a web search tool in a loop depends on that tool in a way a human user never does. A person who sees an error retries in a few seconds. An agent mid-plan either crashes the run, hallucinates around the gap, or burns retries against a provider that is still down. The failure modes are ordinary:
- Provider 5xx. Every search API has outages and partial outages. During one, every request from your agent fails until the provider recovers.
- Rate limits. A burst of parallel tool calls, or a few agents sharing one provider key, can hit the provider's per-second or per-minute cap. Retrying harder makes it worse.
- Timeouts. A slow upstream turns a 1 second tool call into a 15 second one, and an agent framework with its own timeout gives up first.
- Regional problems. A provider can be fine from one region and unreachable from another.
The usual fix is to integrate two or three search APIs, write a retry wrapper, normalize three response shapes, and hold three billing relationships. Mux does that once, behind one endpoint, with one key and one bill. The response shape is identical no matter which provider answered, so your agent's parsing code does not change when the fallback kicks in.
How the models list works#
POST /v1/search accepts either model (one id) or models (an ordered list of up to five). When models is set it takes precedence. The rules:
| Behavior | What Mux does |
|---|---|
| Order | Tries each model in the order given, stops at the first success. |
| Failed attempts | The price was reserved before the call and is refunded when the call fails. You pay for the model that answered. |
| Unaffordable models | A model whose price would exceed your balance or the key's credit limit is skipped, not called and not charged. A cheaper model later in the list can still run. |
| Timeouts | A provider that has not answered after 15 seconds is abandoned and the next model is tried. Because the provider has almost always finished the search by then, that attempt keeps the model's base price, shown as cost_usd on its entry in attempts. |
| All fail | You get 502 all_providers_failed with the full attempts array. Nothing is charged, except any timed-out attempt. |
| Observability | Every response includes attempts, one entry per model tried, with status of ok, error or skipped. |
If you omit both fields, Mux uses auto, which is a built-in fallback list: parallel/fast, then exa/auto, then keenable/pro. That order favors the cheapest model first and still covers all three providers.
The Mux SDKs retry 429 and 5xx responses twice with backoff, but not 502 all_providers_failed, because the gateway has already tried everything you allowed. That error is safe to retry yourself since you were not charged.
Recommended orders for three goals#
Prices below are list prices per 1,000 requests with 10 results each. See the live table on /pricing and the per-model pages such as /models/parallel/fast.
| Model | Per 1,000 requests | Included results | Extra result | Latency |
|---|---|---|---|---|
| exa/instant Exa instant, lowest-latency neural search | $4.00 | 10 | +$1.00 / 1k | ~300 ms |
| exa/fast Exa fast | $7.00 | 10 | +$1.00 / 1k | ~500 ms |
| exa/auto Exa auto (default, best general quality) | $7.00 | 10 | +$1.00 / 1k | ~1 s |
| exa/deep Exa deep multi-query search | $12.00 | 10 | +$1.00 / 1k | ~5 s |
| parallel/turbo Parallel turbo, ~200ms | $1.00 | 10 | +$1.00 / 1k | ~200 ms |
| parallel/fast Parallel fast, <1s | $1.00 | 10 | +$1.00 / 1k | ~800 ms |
| parallel/basic Parallel basic | $5.00 | 10 | +$1.00 / 1k | ~1 s |
| parallel/advanced Parallel advanced, highest quality excerpts | $5.00 | 10 | +$1.00 / 1k | ~3 s |
| keenable/pro Keenable pro index | $4.00 | 10 | none | ~700 ms |
| keenable/realtime Keenable realtime index | $4.00 | 10 | none | ~700 ms |
Cheapest first#
Start with parallel/fast at $1 per 1,000, fall back to keenable/pro at $4, and keep exa/auto at $7 as the last resort. Most requests cost $0.001, and the expensive model only runs when both cheaper ones are down.
curl https://api.getmux.io/v1/search \
-H "Authorization: Bearer $MUX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "postgres connection pooling", "models": ["parallel/fast", "keenable/pro", "exa/auto"]}'Quality first#
Lead with exa/auto, which is Exa's general-purpose mode, then parallel/advanced for its high-quality excerpts, then keenable/pro. Use this for research agents where a weaker result set costs more in model tokens than the search does.
const res = await mux.search({
query: "peer-reviewed studies on intermittent fasting and insulin sensitivity",
models: ["exa/auto", "parallel/advanced", "keenable/pro"],
maxResults: 20,
content: "full",
});Note that content: "full" returns page text on Exa and joined excerpts on Parallel, but content is null on Keenable. If your agent needs page text, put Keenable last or leave it out.
Latency first#
For interactive agents, start with parallel/turbo at about 200 ms typical, then exa/instant at about 300 ms, then keenable/pro at about 700 ms.
const res = await mux.search({
query: "weather api with hourly forecast",
models: ["parallel/turbo", "exa/instant", "keenable/pro"],
maxResults: 5,
});A good rule for any order: put at least two different providers in the list. Two Exa variants protect you against a bad model, not against an Exa outage.
Reading attempts in your logs#
Log res.model and res.attempts on every search. A normal response has a single ok entry. A response that fell back looks like this:
{
"model": "keenable/pro",
"usage": { "cost_usd": 0.004, "results": 10, "latency_ms": 712, "byok": false },
"attempts": [
{ "model": "parallel/fast", "source": "platform", "status": "error", "error": "parallel 503: service unavailable" },
{ "model": "keenable/pro", "source": "platform", "status": "ok" }
]
}Things to watch for:
status: "error"means the provider was called and failed. It was refunded unlesscost_usdis present, which marks a timeout that kept the base price.status: "skipped"means the model was never called. Theerrorfield says why, usually insufficient credits or a key limit. If you see this often, raise the key's limit or move a cheaper model earlier in the list.- The last entry is always the model that served the request, and matches the top-level
modelfield and thex-mux-modelresponse header. - A rising fallback rate for one provider is an early warning that it is degraded. Counting
attemptsentries withstatus: "error"by model over a window gives you a free provider health dashboard.
The same information appears per request on the activity page of the dashboard and from GET /v1/activity, where each failed upstream attempt is logged as its own row with its error.
for (const a of res.attempts) {
if (a.status !== "ok") console.warn("search fallback", a.model, a.status, a.error);
}FAQ#
Does a fallback cost more than a single-provider request?#
No. Each attempt's price is reserved before the call and refunded if it fails, so you pay for the model that succeeded. The one exception is a timed-out attempt, which keeps that model's base price because the provider has normally completed the search by then.
What happens if every model in the list fails?#
You get 502 all_providers_failed with the attempts array in the error body, and your balance is unchanged apart from any timed-out attempt. The SDKs do not retry this status automatically, but it is safe for you to retry.
Can I use models with my own provider keys?#
Yes. With BYOK, a request to a provider you have connected runs on your key, and attempts shows source: "byok" for it. If your key fails, Mux falls back to its own account for that provider at list price unless you set always_use: true.
How many models can I list?#
Up to five. Past three you get diminishing returns, since Mux currently routes to three providers. A list that covers all three providers is the strongest guard against an outage.
Set MUX_API_KEY and send your first fallback request in a minute with the quickstart.
Also available as markdown for your coding assistant.
More guides
Add a web search tool to the Vercel AI SDK with Mux
Build a web search tool for the Vercel AI SDK with the tool() helper and multi-step generateText, using Mux for Exa and Parallel results on one key.
Exa pricing explained: what each Exa search tier costs
Exa pricing through Mux: per-request and extra-result math for exa/instant, fast, auto and deep, worked examples, and when your own Exa contract is cheaper.
Keenable search API through Mux: models and pricing
What the Keenable search API offers through Mux: two models, a flat $4 per 1,000 requests for up to 50 results, native date filters, and no page text.