A chat model answers from what it learned. Ask it for something that changed last week and you get a confident guess. The usual fix is retrieval: search the web, fetch the page, feed the text back into the model. That works, and it also means a second vendor, a second key, a second bill, and tool-calling glue you now own.

If your inference already speaks the OpenAI API, there is a shorter path: the search and read tools can run on the completion call itself.

What ships

WebWright exposes two tools on POST /v1/chat/completions:

  • web_search searches the live web. Send one query and get ranked results, each with a title, URL, and snippet for grounding.
  • web_read fetches a URL and returns clean, readable text suitable for grounding an answer. Set render: true to run the page in a sandboxed browser first, which reaches content built by JavaScript. Reach for it after a search when you need the whole page, not just the snippet.

Both run server-side, inside the same request path as your model call. There is no separate search endpoint for your client to configure.

The gate, stated plainly

These tools are Premium. A Premium pay-as-you-go account — the payg_tier on your key, not a field in the request — has WebWright run the whole model-to-tool loop for them. On Standard the request is still accepted — the model emits the call and the response hands it back to your client — because what Premium buys is the execution, not permission to send a tools array.

The gate covers our tools, not yours. If your request defines its own function tools — an MCP server in your editor, a file reader, a shell — those tools are forwarded untouched on any plan: the model's calls come back to your client, and your client executes them. Bring your own tools and you never touch the Premium gate.

The working call

Non-streaming, with the tools enabled:

curl https://webwright.ai/v1/chat/completions \
  -H "Authorization: Bearer $WREN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "wren-r1",
    "stream": false,
    "messages": [
      {"role": "user", "content": "What changed in the OpenAI-compatible API space this week?"}
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "web_search",
          "description": "Search the live web and return ranked results.",
          "parameters": {
            "type": "object",
            "properties": {"query": {"type": "string"}},
            "required": ["query"]
          }
        }
      }
    ]
  }'

The same shape from Python with the OpenAI SDK:

from openai import OpenAI

client = OpenAI(base_url="https://webwright.ai/v1", api_key=WREN_API_KEY)

resp = client.chat.completions.create(
    model="wren-r1",
    stream=False,
    messages=[{"role": "user", "content": "What changed in the OpenAI-compatible API space this week?"}],
    tools=[
        {
            "type": "function",
            "function": {
                "name": "web_search",
                "description": "Search the live web and return ranked results.",
                "parameters": {
                    "type": "object",
                    "properties": {"query": {"type": "string"}},
                    "required": ["query"],
                },
            },
        }
    ],
)
print(resp.choices[0].message.content)

Those are ordinary OpenAI function tools, sent the ordinary way. Two things worth knowing before you copy them:

  • Premium callers can leave the tools array out entirely. WebWright injects web_search and web_read and runs them server-side, which is the point of the feature: no tool definitions in your request.
  • Tier comes from your API key, not the request body. payg_tier is set on your account in the dashboard; sending it in the payload changes nothing.

WebWright runs the model-to-tool loop server-side, up to six rounds, and sums the usage across rounds on the same meter as any other completion. You do not write the tool-calling loop yourself.

Streaming, and what it looks like

Set "stream": true like you would against any OpenAI-compatible endpoint. The tool rounds run behind the scenes; once the model has its tool results, the finished answer arrives as a normal text/event-stream — role chunk, content, a terminal finish_reason, the usage chunk, then [DONE]. Your client parses it with the same code path it already has.

What you will not see is deltas arriving during the tool rounds. Those rounds belong to us, and the model has to finish one before its tool calls can be executed, so the content cannot be incremental before the retrieval is done. If time-to-first-token matters more than retrieval, call without tools.

Client-side tools behave differently and better: they stream token by token, because the call never leaves your app and the tool runs in it.

Search, read, or just prompt

  • Prompt when the answer is stable knowledge the model already holds. Do not pay for retrieval you do not need.
  • Search when the question depends on facts that change: recent releases, current pricing pages, news, docs.
  • Read when a search snippet is not enough and you need the page body, for example to quote a specific section or follow a table.

A small rule of thumb: reach for a tool only when you would otherwise be guessing.

When to book a consult

If you would rather not choose between self-serve retrieval, your own vendor, and a hybrid, that is what the consult is for. One working session maps where you are, what you have tried, and what good would look like, and you leave with a prioritized first use case, an architecture, a rough cost, and the honest risks.

The WebWright Team