← All resources

How to Enable Web Search on a FreeRouter Chat Key

Chat models don't know what happened last night. Ask for a score, the weather in Paris, or where NVDA closed, and you get a hedge or a made-up number. Most teams then add a search client to the app, stuff results into the prompt, and do it again for every other data source.

On FreeRouter you turn the tools on in the dashboard. Your app still calls POST /v1/chat/completions. We offer the tools to the model, run the ones it picks, and put the results in the reply. You don't add a request field or change your SDK. Change gateways later and the tools stay on the key.

Web search in the catalog today is Parallel, which gives the model two tools: web_search and web_fetch. Weather and finance servers work the same way. Some need you to bring your own key — Settings will say so. The catalog changes as we add and retire servers, so Settings → MCP is the list your workspace actually has. See MCP Tools if you want the screens spelled out.

What happens on a chat call

Send a normal chat request. Don't put a tools array in the body. If web search is on the key, we offer Parallel's tools to the model — streamed or not. If it returns tool_calls, we run them and send the results back on the same routing rule. After a few rounds we stop offering tools so it has to answer.

That's last week's Longhorns score, the temperature in Paris, or a closing price — in the assistant message. Search runs on the key. You don't add an endpoint in your app. If search is down, you still get a chat reply. We don't turn a good model call into a 5xx because a tool failed.

flowchart LR
    App["Your app"] --> Key["FreeRouter key"]
    Key --> Offer["offer web search"]
    Offer --> Model["upstream model"]
    Model --> Call["tool_calls"]
    Call --> Search["Parallel: web_search"]
    Call --> Fetch["Parallel: web_fetch"]
    Search --> Fold["results back"]
    Fetch --> Fold
    Fold --> Model
    Model --> Out["assistant message"]

Tools sit on the key. You still send a normal chat completion.

What you need

  • A FreeRouter account, a provider key stored in the dashboard, and a FreeRouter key routed at that provider. Getting Started if you don't have that yet.
  • Parallel. In Settings the switch is labeled Parallel. Nothing is on until you flip it.
  • Your own vendor key, if you want the spend on your account. Parallel is optional BYOK: leave it blank and a FreeRouter-held key handles it. Monid is required BYOK and won't run until you paste one. See Bring your own key.
  • A model that actually calls tools. The snippets below use openai/gpt-4o-mini.
  • stream: true is fine. The tool loop runs on streamed requests on the OpenAI shapes, merged into one response. The google shape can't stream at all yet.

How you enable web search

Nothing is on by default. Turn the tool on for the workspace, then on the key. Or check the box that copies it onto every key when you save.

  1. Settings → MCP. Open Settings and scroll to MCP. Turn on Parallel. Paste your own key if you want the spend on your account; leave it blank and ours handles it. A server marked required BYOK won't run at all until you paste one.
  2. Optional: apply to every key. Check Also turn these on for every FreeRouter API key in this workspace if you only have one key, or you want production and playground on the same tools. It's a one-time copy. Keys don't stay in sync after that.
  3. Save MCP. Until you do, the key page will tell you to set this up in Settings first.
  4. API Keys → Enable MCPs. Skip this if you used apply-to-all. Under the key's Extensions, click Enable MCPs, turn on the same search tools, Save MCP. Each key is separate, so staging can stay plain while production searches.
  5. Test in Playground. Pick that key at Playground. Ask something current. Chat mode shows each tool call inline as a chip, so you can see whether the model actually searched. Playground uses the same tool loop as live traffic for that key. Those runs don't show up in Logs.
  6. Call from the app. Same base URL, same key, same body as before. Don't add a tools field — tools you send yourself stay yours to execute.

Weather, finance, and Monid use the same two switches. Some of them need you to bring your own key in Settings. New tools show up under Settings → MCP and stay off until you turn them on. See Set it up and Monid.

Ask with curl

Export your FreeRouter key and paste this. It's a normal completion. If web search is on the key, the model can search before it answers:

export FREEROUTER_API_KEY=fr_…
curl https://api.freerouter.com/v1/chat/completions \
  -H "Authorization: Bearer $FREEROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o-mini",
    "messages": [
      { "role": "user", "content": "What was the Texas Longhorns football score last week? Name the opponent." }
    ]
  }'

You get a normal chat response: { id, model, choices, usage }. The score is in choices[0].message.content. There's no extra search object on the wire — we already mixed that into the message. X-FreeRouter-Provider names who served the model, same as any other request.

Want weather or a price instead? Change the prompt. Keep the rest of the curl. "What's the weather in Paris right now?" and "What did NVDA close at yesterday?" work the same way. Later you can turn on weather, finance, or Monid if you want something more specific. Web search already covers all three.

Same thing in Node

Save this as fr-web-search.js and run it with FREEROUTER_API_KEY in the environment. Standard library only.

// fr-web-search.js — chat completion with web search on the key.
// Run: FREEROUTER_API_KEY=fr_… node fr-web-search.js
async function main() {
  const key = process.env.FREEROUTER_API_KEY;
  if (!key) throw new Error('Set FREEROUTER_API_KEY.');

  const res = await fetch('https://api.freerouter.com/v1/chat/completions', {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${key}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      model: 'openai/gpt-4o-mini',
      messages: [
        { role: 'user', content: "What's the weather in Paris right now? Give the temperature." },
      ],
    }),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
  const body = await res.json();
  console.log('provider', res.headers.get('x-freerouter-provider'));
  console.log(body.choices[0].message.content);
}

main().catch((err) => { console.error(err.message); process.exit(1); });

Once the tools are on the key, that's it for the client. The model can look the fact up before it writes the answer.

Putting it together

Turn Parallel on in Settings, attach it to the key, then send the same chat call you already send. The model searches if it needs to. The answer comes back as ordinary chat.

The tools live on the FreeRouter key, not on OpenRouter or Vercel. Fail over or remap later and search is still there. Attaching them to the key rather than to one model host is what makes that true.

flowchart TD
    S1["Settings → MCP: turn on Parallel"] --> S2["Save MCP"]
    S2 --> S3["API Keys → Enable MCPs"]
    S3 --> S4["POST /v1/chat/completions"]
    S4 --> S5{"model calls search?"}
    S5 -->|yes| S6["FreeRouter runs the search"]
    S6 --> S7["results back to the same model"]
    S7 --> S8["ordinary chat reply"]
    S5 -->|no| S8

Workspace switch, key switch, then the chat call you already ship.

Weather, finance, and the rest

Once search works, weather, finance, and Monid use the same switches. With more than one server on, a steering message is how you tell the model which to reach for on a given question. Set it under Settings → Steering message, or on the key. It gets appended to the system prompt. Your app still sends the same body.

Monid needs you to bring your own key. Spend hits your Monid wallet, and we won't offer those tools until the key is saved. Pick enable all, enable none, or a subset. The model only sees the few that match the prompt, not the whole list.

This is tools on a chat call. Don't mix it up with the MCP Server at https://api.freerouter.com/mcp — that's for managing keys with a management key. Chat keys don't go there.

Limits and failure modes

  • Streaming runs the loop too. Search works on /v1/chat/completions, /v1/responses, Anthropic /v1/messages, and Google generateContent — streamed or buffered. On a stream the turns are merged into one response: only the final turn carries a finish_reason, and there is exactly one data: [DONE]. The google shape has no streaming at all yet.
  • Settings on, key off is plain chat. Turning a tool on in Settings isn't enough. If Enable MCPs tells you to configure Settings first, you haven't hit Save MCP yet.
  • The model has to call the tool. A weak tool-user, or a prompt that doesn't need the open web, will answer from memory. Playground is the fastest check. Use a model that actually calls tools, and ask something search can answer.
  • Some MCP servers require you to bring your own key. If a required server has no workspace key, we leave it off that request. Other tools still run. Parallel is optional BYOK, so it runs either way. Monid always needs yours.
  • Only tools we list. You can't paste an arbitrary MCP URL from the workspace. Command / npx (stdio) servers aren't supported. New servers appear under Settings → MCP as we add them, and stay off until you opt in.
  • Search is as of this request, not a live feed. Scores, weather, and prices are whatever the tool returned on this call. Don't treat that as a streaming quote or an official box score.
  • A broken tool doesn't fail the chat. Timeout, 401, or a down server becomes a skip or a tool-message. You still get a model reply. Check Settings for a saved key, and Logs for a normal model path.

Next steps

Get a FreeRouter key, turn on Parallel, and run the curl above.

Route your first request today.

Bring your own keys and start routing in minutes.

Get your key