Web Search

Any text model on Onlist can look things up on the web before it answers. There are two ways to turn it on, both in OpenRouter's request shape:

  • the web plugin or the :online model suffix: Onlist runs one search before the request goes to a provider and adds the results to your prompt;
  • the openrouter:web_search server tool: the model decides whether to search, writes its own queries, and can search several times before it answers. See Server tool: the model decides when to search.

Either way, Onlist runs the searches on the platform's search engine and returns the sources the model was given as url_citation annotations, in the same shape OpenRouter uses. Both work on /v1/chat/completions with every text model, whichever provider serves it.

Turn it on

For one search per request, two forms do the same thing. Add :online to the model id:

curl https://onlist.io/v1/chat/completions \
  -H "Authorization: Bearer YOUR_ONLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek/deepseek-v4-flash:online",
    "messages": [{"role": "user", "content": "What changed in the latest Kubernetes release?"}]
  }'

Or send the web plugin, which also takes options:

from openai import OpenAI

client = OpenAI(base_url="https://onlist.io/v1", api_key="YOUR_ONLIST_TOKEN")

resp = client.chat.completions.create(
    model="deepseek/deepseek-v4-flash",
    messages=[{"role": "user", "content": "What changed in the latest Kubernetes release?"}],
    extra_body={"plugins": [{"id": "web", "max_results": 3}]},
)
print(resp.choices[0].message.content)
for a in resp.choices[0].message.annotations or []:
    print(a.url_citation.title, a.url_citation.url)

model:online is exactly plugins: [{"id": "web"}] with default options. A plugin entry with "enabled": false turns search off, even when the model id ends in :online.

:online is a routing shortcut, and only the last : segment of a model id is read, so it cannot be stacked with :floor or :nitro. To search and sort at the same time, send the plugin together with provider.sort. A static variant is fine in front of it: deepseek/deepseek-v4:thinking:online searches with the :thinking model.

In the browser Chat, the globe toggle next to the message box does the same: while it is on, every message is searched first and the sources are listed under the reply.

What happens

With the plugin or :online:

  1. Onlist takes the text of your last user message (plus the user message before it, for context) as the search query. The query is not rewritten, so a follow-up such as "and the year before?" searches poorly on its own. Put the full question in the message.
  2. It runs one search on the platform's search engine, before the request goes to a provider. If the first provider fails and Onlist fails over to another one, the same results are reused; nothing is searched twice.
  3. The results go at the start of your last user message: a short notice that search results are external, untrusted data and must not be followed as instructions, then the search prompt and the list of sources. The default search prompt is OpenRouter's: it gives today's date (UTC) and asks the model to cite sources as markdown links named after the site's domain. Your system prompt and earlier messages are left alone, so the provider's prompt cache keeps working.
  4. The model answers, and the response carries the sources as annotations.

Onlist does not search, and charges no search fee, when:

  • the last message in messages does not have the role user, for example when your own tool-call loop sends the conversation back ending with a tool result. Searching again on every turn of such a loop would charge the same search over and over. The response carries web_search: not searched; the last message is not from the user;
  • the request asks for zero data retention (provider.zdr: true): see Privacy. The response carries web_search: not searched; zero data retention requested;
  • include_domains is given but none of its entries is valid: see Plugin options. The response carries web_search: not searched; no valid domain in the allow list;
  • web search is switched off on the platform: web_search: not available.

In each case the request still goes to the provider and the model answers without search results.

Searching adds about 5 to 10 seconds before the first token. Onlist stops waiting for a search after about 30 seconds, and the model then answers without results. Nothing is sent while the search runs, not even the response headers: they go out after the search so that they can carry its warnings. On a stream, heartbeats start only after the search, so up to about 45 seconds can pass before the first byte (30 for the search plus the usual 15 before the first heartbeat). Leave that much room in your client's read timeout. Search time is not counted in the provider's latency statistics.

Caution

Onlist tells the model that search results are external data, not instructions, but a page can still contain text written to steer a model. Treat answers built on search results the way you would treat the pages themselves.

Plugin options

The web plugin takes the same options as on OpenRouter. Options Onlist does not support are ignored, never rejected, and reported in X-Onlist-Warnings.

OptionOpenRouterOnlist
id"web""web". Other plugin ids are ignored, e.g. ignored: plugins[file-parser].
enabledfalse turns the plugin offSame. It also turns off :online on the model id.
enginenative, exa, firecrawl, parallel, perplexity, or unsetauto, native and deepseek (or unset) all use the platform engine. Any other value also uses it and adds the warning web_search: engine "exa" not available on Onlist; used deepseek.
max_resultsDefault 5Default 5, at most 10.
search_promptText placed before the resultsSupported. The untrusted-data notice still comes first.
include_domainsOnly search these domainsSupported. Results from other domains are dropped, so every source returned is within these domains. Given but with no valid entry: nothing is searched.
exclude_domainsSkip these domainsSupported. Results from these domains are dropped.
modeEngine-specific depth settingIgnored: ignored: plugins[web].mode.
x_searchX (Twitter) search on xAI modelsIgnored: ignored: plugins[web].x_search.

Domain filters are strict. Onlist asks the engine to keep to them and then drops every result that does not match, so each source added to your prompt and returned in the response satisfies them. example.com matches that domain and its subdomains, *.example.com only its subdomains, and example.com/blog also requires the path to be /blog or below it. An internationalized domain and its punycode form are the same domain (例子.cn matches xn--fsqu00a.cn and the other way round), and a scheme, a port or a trailing dot in an entry is ignored (https://example.com:443/ is example.com). When both lists are set, both apply. Each list is read up to its first 20 entries, and entries longer than 253 bytes are skipped; both cases are reported in Warnings. An entry with no domain in it, such as https://, *. or /blog, is skipped too. If include_domains is given but none of its entries is left, nothing is searched and the response carries web_search: not searched; no valid domain in the allow list: Onlist does not fall back to an unfiltered search. Filtering can leave fewer results than max_results, or none; with none, the response carries web_search: no results; answered without results and no search fee is charged.

On OpenRouter, native asks the model's own provider to search. Onlist cannot tell which provider's upstream supports that, so native runs the platform engine like auto. To use a model's own search, send its search tool yourself; see Native search tools.

Declare openrouter:web_search in tools and the model gets a search function it can call whenever it needs to, several times if necessary. Onlist runs each search, gives the results back to the model, and the model searches again or writes its answer. You receive one response: the answer, with the sources as annotations. The search calls themselves never reach you.

curl https://onlist.io/v1/chat/completions \
  -H "Authorization: Bearer YOUR_ONLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek/deepseek-v4-flash",
    "messages": [{"role": "user", "content": "Compare the latest Kubernetes and Nomad releases."}],
    "tools": [{"type": "openrouter:web_search", "parameters": {"max_results": 3}}],
    "stream": true
  }'

Each model call in the loop is a step. A step in which the model asked for searches is a round: Onlist runs the searches, adds the model's call and the results to the conversation, and calls the model again. The loop ends when the model answers without searching or a limit is reached.

Declaring the tool

Both of OpenRouter's shapes work. Options go in a parameters object or directly on the tool entry; when both carry the same option, the one in parameters wins:

{ "type": "openrouter:web_search", "parameters": { "max_results": 3 } }
{ "type": "openrouter:web_search", "max_results": 3 }

On /v1/chat/completions, these tool types are treated as the same tool, as on OpenRouter: web_search, web_search_preview (and its dated versions such as web_search_preview_2025_03_11), and web_search_2025_08_26. A {"type": "web_search", ...} tool in another vendor's chat format is taken over the same way; options Onlist does not know are ignored with a warning. On /v1/responses, web_search is OpenAI's own search tool and is forwarded to the provider unchanged; see Native search tools.

Only the first declaration counts. Later ones are removed and reported as ignored: tools[<index>], where the index is the entry's position in tools, counting from 0.

Your own function tools can sit next to it. The model sees the search as a function named web_search. If one of your functions already has that name, the search function is called onlist_web_search instead (onlist_web_search_2 and so on if that is taken too). Onlist never hands you a call to this function: it runs the search itself.

When the server tool is in effect and you also turn on the web plugin or :online, the server tool wins: the plugin does not run, and the response carries ignored: plugins[web] (superseded by openrouter:web_search).

The server tool runs only with n of 1, the default. With a larger n it is removed and the response carries ignored: tools[openrouter:web_search] (n>1). The model then gets no search tool, but a web plugin or :online in the same request is no longer superseded: it searches once and is charged, as in What happens.

The tool is also removed, so the model gets no search function and nothing is searched, when the request asks for zero data retention (web_search: not searched; zero data retention requested, see Privacy) or when allowed_domains is given but none of its entries is valid (web_search: not searched; no valid domain in the allow list).

Some models cannot call tools. When Onlist's model catalog lists a model's supported parameters and tools is not among them, the server tool falls back to the plugin behavior: one search on your last user messages, as in What happens, charged as one search, with the warning web_search: model does not support tools; searched once. max_results, allowed_domains and excluded_domains still apply.

What the model sees

Each call to the search function carries 1 to 4 search queries, which Onlist runs at the same time; a call with more than 4 gets an error result instead. A step runs at most 4 new queries in total, however many calls the model makes in it: queries beyond that are not run, and the model is told Search limit reached (queries_per_step). The model gets back one result for each call:

  • it starts with a notice that the content is external, untrusted data and not instructions;
  • it lists at most 8 sources, taking the queries' results in turn and dropping repeated URLs. Each query asks the engine for max_results results;
  • it ends by asking the model to cite the relevant URLs as markdown links.

When a query fails, the result says so, and the model can try other words or answer from what it has. A query can also fail because the search service is busy: Onlist limits how many searches run at once, overall and for each account, and a query that cannot start within about 2 seconds fails. Results are kept for the whole request: a query that returned results and is repeated (ignoring case and spacing) is not searched again. A query that failed is searched again if the model repeats it, and counts against the limits again. The function's description carries the same untrusted-data notice. Nothing is added to your system prompt or your messages, so the provider's prompt cache keeps working.

What you get back

One response, assembled from all the steps:

  • content is the text of every step, joined with a blank line (\n\n). A short preamble such as "Let me look that up." stays in front of the answer. reasoning_content and reasoning are joined the same way.
  • The search calls are removed. If the last step also called one of your functions, those calls are kept (renumbered from 0) and finish_reason stays tool_calls; otherwise a tool_calls finish becomes stop.
  • usage is the sum over all steps, including prompt_tokens_details.cached_tokens and completion_tokens_details.reasoning_tokens, plus server_tool_use.web_search_requests (see Citations in the response).
  • id and created are the first step's.
  • annotations lists every source the model was given, from all rounds, without repeats.

If a step calls the search together with one of your functions, Onlist does not run that search and does not charge for it. Your function calls come back to you as usual, and that step is the last one. When you send your function results back, that request can search again.

Every step goes to the same provider. If a step fails (an error status, an error in the stream, or a stream that ends early) before any output has reached you, Onlist retries the whole loop with another provider from the first step, as it does for any failed attempt. Searches already made are reused from the request's results, so they are not run or charged twice.

Each round adds a search (about 5 to 10 seconds) and another model call. Without streaming, nothing is sent until the whole loop has finished, and Onlist's edge closes a connection that stays silent for about 100 seconds. If the model may search more than once or twice, use stream: true.

Tool parameters

Options Onlist does not support are ignored, never rejected, and reported in X-Onlist-Warnings.

OptionOpenRouterOnlist
engineauto, native, exa, firecrawl, parallel, perplexityAs for the plugin: auto, native and deepseek (or unset) use the platform engine; any other value also uses it, with web_search: engine "exa" not available on Onlist; used deepseek.
max_resultsResults per search, default 5, 1 to 25Results per query, default 5, at most 10. One call gives the model at most 8 sources, however many queries it carries.
max_usesSearches per request; later calls get an error resultSupported, counted per query, failed queries included: a call with 3 queries uses 3. Queries over the limit are not run, and the model is told the limit was reached.
max_total_resultsResults across all callsSupported: sources given to the model across the request. Once reached, the model is told the limit was reached. A query none of whose results were given to the model is not charged.
max_charactersCharacters per result, 1 to 100,000Supported: each source's excerpt is cut to this many characters. Excerpts are a few sentences long, so only small values change anything.
allowed_domainsOnly these domains; support varies by engineSupported, as the plugin's include_domains: strict, and usable together with excluded_domains. Given but with no valid entry: the tool is removed and nothing is searched.
excluded_domainsSkip these domains; support varies by engineSupported, as the plugin's exclude_domains.
search_context_sizelow, medium or high result lengthIgnored: ignored: tools[openrouter:web_search].search_context_size.
user_locationApproximate location, for native searchIgnored: ignored: tools[openrouter:web_search].user_location.
modeEngine-specific depth settingIgnored: ignored: tools[openrouter:web_search].mode.
x_searchX (Twitter) search on xAI modelsIgnored: ignored: tools[openrouter:web_search].x_search.

max_uses, max_total_results and max_characters must be whole numbers of at least 1; another value is ignored with the same kind of warning, as is a parameters field that is not an object. max_results above 10 is lowered to 10, and max_characters above 100,000 to 100,000. The domain lists have the plugin's limits (the first 20 entries, 253 bytes per entry) and its matching rules, reported as in Warnings.

When a query is not run because of max_uses or max_total_results, the model receives, for example, Search limit reached (max_uses). Answer using the information already gathered.

Limits and stopping

The model keeps searching until it answers on its own or one of these limits is reached:

  • max_tool_calls: at most this many search queries per request. Default 30, which is also the maximum.
  • stop_server_tools_when: your own stop conditions; see below.
  • 5 rounds per request, set by Onlist.
  • 2 rounds in a row in which every search failed.

Whatever you set, a request runs at most 30 search queries, failed ones included, and at most 4 new queries per round. No stop condition raises these limits.

When a limit is reached, the searches the model asked for in that round still run (those within the limit), and the model is then called once more with tool_choice: "none", so it writes its final answer from what it has. Five rounds therefore means at most six model calls. If the model still tries to search in that last call, the call is dropped and finish_reason is stop.

Your credits are handled differently: before each further step, including that final call, Onlist sets aside what the steps so far, the next step and the round's search fees may cost, beyond what it already holds for the request. If your credits or your API key's spend limits cannot cover it, the request stops before that step; see Running out of credits.

max_tool_calls and stop_server_tools_when are top-level fields of the request body, next to tools, and are removed before the request goes to the provider. max_tool_calls is a whole number from 1 to 30; a larger value is lowered to 30.

stop_server_tools_when is an array of stop conditions. When it holds at least one condition Onlist recognizes, it replaces max_tool_calls entirely, as on OpenRouter. Onlist recognizes two:

ConditionEffect
{"type": "step_count_is", "step_count": 4}Stop after 4 search queries. Onlist counts steps as search queries, the same unit as max_tool_calls. A value above 30 is lowered to 30.
{"type": "max_cost", "max_cost_in_dollars": 0.05}Stop once the steps so far and their search fees cost $0.05 or more. The final answer is charged on top, so the request can end up costing somewhat more than the cap.

With several conditions, the first one reached ends the search. Other condition types are ignored, each reported as ignored: stop_server_tools_when[<type>]. With only a max_cost condition, the request still runs at most 30 queries and 5 rounds.

tool_choiceEffect
Unset or "auto"The model decides whether to search.
{"type": "openrouter:web_search"}The first step must search; later steps are "auto". An alias type works too.
{"type": "function", "function": {"name": "web_search"}}The same, when the name is the search function's.
"required"The first step must call a tool, the search or one of yours; later steps are "auto".
"none"The search is not offered to the model, so the server tool runs no searches. A web plugin or :online in the same request still searches once and is charged, as in What happens.
One of your own functionsSent unchanged with every step.

Streaming

With stream: true, the text and reasoning of every step stream to you as they are generated, so a preamble such as "Let me look that up." arrives right away. What you do not receive:

  • the search calls;
  • the finish_reason, usage chunk and data: [DONE] of the steps that searched;
  • a new role chunk for each step. Later steps continue the same message, with the first step's id and created.

If the text sent so far does not end with a line break, Onlist sends \n\n before the next step's first text, so text from different steps does not run together. Calls to your own functions stream as usual, renumbered if the step also searched. The annotations chunk and the usage chunk come once, at the end, as in Citations in the response; the usage chunk carries the sum over all steps.

Nothing is generated while a search runs between steps. When the stream has been quiet for about 15 seconds, Onlist sends an SSE comment line to keep the connection open (see Streaming); SDKs skip those.

Once text or reasoning has reached you, Onlist can no longer switch to another provider. If a later step then fails, the stream ends with an error frame, a data: payload with an error key and no data: [DONE] (see Streaming). You are charged only for the steps that completed; the searches whose results went to the failed step are not charged.

Citations in the response

Every search result comes back as an annotation on the assistant message, whether or not the model cited it. They follow any annotations the provider itself returned. With the server tool, that is every source the model was given in any round.

{
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The latest release removes the in-tree cloud providers ([kubernetes.io](https://kubernetes.io/releases/)).",
        "annotations": [
          {
            "type": "url_citation",
            "url_citation": {
              "url": "https://kubernetes.io/releases/",
              "title": "Releases | Kubernetes",
              "content": "Kubernetes ships three minor releases a year ...",
              "start_index": 56,
              "end_index": 104
            }
          }
        ]
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 1830,
    "completion_tokens": 412,
    "total_tokens": 2242,
    "server_tool_use": { "web_search_requests": 1 }
  }
}
FieldMeaning
url_citation.url, titleThe page, as the search returned it. title is the site's host name when the page has none.
url_citation.contentA short excerpt of what the page says, when the search provided one.
start_index, end_indexWhere the reply's content first cites the page: the whole markdown link [label](url), or just the URL when it appears bare. end_index is exclusive. Both 0 when the reply does not mention the page.
usage.server_tool_use.web_search_requestsThe number of searches charged. With the plugin or :online, 1 when search results were added to the request. With the server tool, the queries with at least one result given to the model.

With the server tool, web_search_requests leaves out queries that failed, found nothing, were not run, or had all their results cut by max_total_results; OpenRouter counts every query made.

Whenever no search fee is charged, the response carries neither usage.server_tool_use nor the search annotations, with or without streaming. That includes a request that produces no output and is therefore not charged at all. When streaming, the usage chunk gets server_tool_use by the same rule: with the plugin or :online, only when it reports output tokens; with the server tool, only when text, reasoning or one of your function calls has reached you. A provider that puts a running usage in every chunk has it only in the chunks from that point on, so read the count from the last usage chunk.

start_index and end_index count Unicode code points, so in Python content[start_index:end_index] is the cited text. JavaScript strings count UTF-16 units, so positions there drift after characters outside the Basic Multilingual Plane, such as emoji.

When streaming, the annotations arrive in one extra chunk after the last content chunk and before the usage chunk, so the usage chunk is still the last frame before data: [DONE]:

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1767225600,"model":"deepseek-v4-flash","choices":[{"index":0,"delta":{"content":")."},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1767225600,"model":"deepseek-v4-flash","choices":[{"index":0,"delta":{"annotations":[{"type":"url_citation","url_citation":{"url":"https://kubernetes.io/releases/","title":"Releases | Kubernetes","content":"Kubernetes ships three minor releases a year ...","start_index":56,"end_index":104}}]},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1767225600,"model":"deepseek-v4-flash","choices":[],"usage":{"prompt_tokens":1830,"completion_tokens":412,"total_tokens":2242,"server_tool_use":{"web_search_requests":1}}}

data: [DONE]

The annotations chunk carries no content and "finish_reason": null; its id, created and model are copied from the provider's chunks. If the provider's stream has no usage chunk, the annotations chunk is the last data frame: it comes right before data: [DONE], or ends the stream when there is no [DONE] either. A stream that stops before the model finishes its reply, or whose reply has no output at all, gets no annotations chunk.

Pricing

ItemPlugin and :onlineServer tool
Search feeCurrently $0.005 per search, one search per request.Currently $0.005 per search query. One call can carry up to 4 queries.
Model callsOne, at the model's price.One per step, each billed in full at the model's price.
Search resultsInput tokens at the model's price.Input tokens of every later step, at the model's price.

The search fee is charged by Onlist, which pays the search service. It is not part of the provider's price and the provider does not share in it. It is included in the request's charge in your usage log, and the credit check before the request already accounts for it.

You are not charged the search fee when:

  • the search fails, times out or cannot start because the search service is busy, or your last user message has no text to search for (for example, it holds only an image). The model answers without results and the response carries web_search: search failed; answered without results;
  • the search finds nothing. Nothing is added to your prompt and the response carries web_search: no results; answered without results;
  • the request produces no output, for example when every provider attempt fails. As with any request, no output means no charge;
  • nothing was searched, for one of the reasons in What happens.

If you disconnect while the search is still running, Onlist does not call the model and the request costs nothing.

Server tool billing

Every step is a full model call to the same provider, billed at its price:

  • Each step is priced on its own. A long-context price applies to a step when that step's own prompt is long enough: two steps of 80K tokens are two 80K-token calls, not one call of 160K.
  • Each later step sends the whole conversation so far again, earlier results included, so input tokens grow with every round. Providers with prompt caching usually serve the repeated part from the cache.
  • Models priced per request are charged once per model call. Models with dynamic pricing are priced per step and summed.

usage in the response and the entry in your usage log carry the totals over all steps, and the entry's details show how many model calls the request made.

Each search query is charged at most once per request: only when at least one of its results was given to the model in the attempt that produced your answer. Not charged:

  • queries that failed, found nothing, or were not run because a limit was reached;
  • queries whose results were all cut by max_total_results, so none of them reached the model;
  • a query the model repeats in the same request (compared ignoring case and spacing);
  • searches for a provider attempt that failed. The next attempt reuses the results, and you pay for the queries given to the model in the attempt that answered.

If nothing from the whole request reaches you (no text, no reasoning and none of your function calls, for example because every step only searched and the final call tried to search again), the request costs nothing: neither the model calls nor the searches are charged. Once any output has reached you, the completed steps and the queries given to the model are charged, even if the final step adds nothing. The one exception to "no output, no charge" is a request that stops because your credits or your API key's spend limits cannot cover the next step: the completed steps are charged even if nothing reached you; see Running out of credits.

Running out of credits

The credit check before the request holds the first step and one round of searching (4 queries). Before each further step, Onlist sets aside from your credits what the completed steps, the next step and that round's search fees may cost, beyond what it already holds, and the same amount against your API key's spend limits. Several requests running at once cannot spend the same credits twice. Whatever is set aside but not used is returned when the request ends.

When that is not possible, the request stops before the next step. The steps already completed are charged as usual (their cost was set aside before they ran), and so are the search queries already given to the model. The queries searched in the last round, whose results never reached the model, are not charged. This holds whether or not anything has reached you:

  • Nothing has reached you yet (always the case without streaming): you get the same error as a request refused up front, 402 with insufficient_balance, or quota_exhausted (402) / rate_limited (429, with Retry-After) when your API key's spend limit is reached. A stream that has only sent heartbeat comments so far gets it as an error frame instead.
  • Text or reasoning has already streamed to you: the stream ends with an error frame carrying the same code and no data: [DONE].

If the request stops for a reason on Onlist's side instead (500 with internal_error), nothing is charged as long as nothing has reached you. If text or reasoning has already streamed to you, the stream ends with an error frame carrying internal_error and no data: [DONE], and the steps completed so far and the search queries given to the model are charged as usual.

Warnings

Web search reports problems in X-Onlist-Warnings and still answers the request:

WarningMeaning
web_search: search failed; answered without resultsThe search failed or timed out, or the last user message had no text to search for (for example, only an image). The answer has no search results, and no search fee is charged.
web_search: no results; answered without resultsThe search found nothing, so nothing was added to the prompt. No search fee is charged.
web_search: engine "<x>" not available on Onlist; used deepseekYou asked for an engine Onlist does not run; the platform engine was used.
web_search: not availableWeb search is switched off on the platform right now. The request ran without it.
web_search: not searched; the last message is not from the userThe last message in messages does not have the role user, for example a tool result in your tool-call loop. Nothing was searched or charged.
web_search: not searched; zero data retention requestedThe request asks for zero data retention (provider.zdr: true), so its content is not sent to the search service. Nothing was searched or charged. See Privacy.
web_search: not searched; no valid domain in the allow listinclude_domains was given but none of its entries is valid. Nothing was searched or charged; Onlist does not search without the filter instead.
ignored: plugins[<id>]A plugin other than web. Onlist does not run it.
ignored: plugins[web].<option>An option of the web plugin that Onlist does not support, such as mode or x_search. The search still runs.
ignored: plugins[web] (more unsupported options)The web plugin has more than 4 unsupported options. The first 4 are listed one by one and this entry stands for the rest.
ignored: plugins[web].include_domains[20+]include_domains has more than 20 entries; only the first 20 were read. exclude_domains gets the same entry with its own name.
ignored: plugins[web].include_domains (entries over 253 bytes)Some include_domains entries are longer than 253 bytes and were skipped. exclude_domains gets the same entry with its own name.
ignored: plugins[<index>]The plugins entry at that position (counting from 0) has no id or is not an object, so it was skipped.
ignored: plugins[8+]plugins has more than 8 entries. Only the first 8 were read, so a web plugin further down is not used.
ignored: pluginsplugins is not an array, so it was skipped.
ignored: plugins (web search is only available on /v1/chat/completions)You sent plugins or :online to another endpoint; see below.

Every entry except the last appears only on /v1/chat/completions. On other endpoints, a non-empty plugins field or the :online suffix gets that last entry instead. Plugin ids, option names and engine names are echoed with every character other than ASCII letters, digits and -_.:/ removed, and cut to 64 characters.

Server tool warnings

The server tool can add these, as well as the engine, not available, zero data retention requested and no valid domain in the allow list entries above (the last for allowed_domains; in both cases the tool is removed and the model gets no search function). With the server tool, web_search: search failed; answered without results means Onlist could not offer the search to the model for this request.

WarningMeaning
ignored: tools[openrouter:web_search] (only available on /v1/chat/completions)You declared openrouter:web_search on another endpoint. It was removed from tools and the request ran without it.
ignored: tools[openrouter:web_search] (n>1)The request asked for more than one choice (n above 1). The tool was removed, so the model ran no searches. A web plugin or :online in the same request still searches once.
ignored: tools[<index>]Another web search declaration after the first one, at that position in tools (counting from 0). It was removed.
ignored: tools (more duplicate web_search entries)More than 4 extra declarations. The first 4 are listed one by one and this entry stands for the rest.
ignored: tools[openrouter:web_search].<option>An option Onlist does not support, such as search_context_size or user_location, an invalid value for max_uses, max_total_results or max_characters, or a non-object parameters.
ignored: tools[openrouter:web_search] (more unsupported options)More than 4 unsupported options. The first 4 are listed one by one and this entry stands for the rest.
ignored: tools[openrouter:web_search].allowed_domains[20+]allowed_domains has more than 20 entries; only the first 20 were read. excluded_domains gets the same entry with its own name.
ignored: tools[openrouter:web_search].allowed_domains (entries over 253 bytes)Some allowed_domains entries are longer than 253 bytes and were skipped. excluded_domains gets the same entry with its own name.
ignored: plugins[web] (superseded by openrouter:web_search)The request also turned on the web plugin or :online. The server tool ran and the plugin did not.
web_search: model does not support tools; searched onceThe model cannot call tools, so Onlist searched once on your last user messages instead, as the plugin does.
ignored: max_tool_callsmax_tool_calls is not a whole number of at least 1. The default of 30 applies.
ignored: stop_server_tools_whenstop_server_tools_when is not an array, so it was skipped.
ignored: stop_server_tools_when[<type>]A condition type Onlist does not support, or a step_count_is or max_cost condition without a valid number. That condition was skipped.
ignored: stop_server_tools_when[<index>]The condition at that position (counting from 0) is not an object or has no type.
ignored: stop_server_tools_when[8+]More than 8 conditions. Only the first 8 were read.
ignored: stop_server_tools_when (more unsupported conditions)More than 4 conditions were skipped. The first 4 are listed one by one and this entry stands for the rest.

Option names and condition types are echoed the same way as plugin ids.

Endpoints

Platform web search, with the plugin or the server tool, runs on /v1/chat/completions only. On any other endpoint, including /v1/responses and /v1/messages:

  • the web plugin and the :online suffix are ignored (the suffix is still removed from the model id), the request runs without search, and the response carries ignored: plugins (web search is only available on /v1/chat/completions);
  • an openrouter:web_search tool is removed from tools, the request runs without it, and the response carries ignored: tools[openrouter:web_search] (only available on /v1/chat/completions). Other tools, including web_search on /v1/responses, are left alone.

Clients on those endpoints, such as Codex and Claude Code, usually bring their own search tool instead.

Native search tools

If your request carries a provider's own search tool, Onlist forwards it to the provider unchanged and the provider's upstream runs the search:

  • the web_search tool on /v1/responses (what Codex sends);
  • Anthropic's web_search_* server tool on /v1/messages (what Claude Code sends);
  • web_search_options on /v1/chat/completions, for the search models that accept it.

Onlist does not add a search fee for these. The results the upstream adds to the conversation are billed as tokens at the provider's price, and the number of searches is recorded in your usage log. Whether a tool works depends on the provider's upstream: one that does not support it will reject the request or ignore the tool.

Privacy

To run a search, Onlist sends the query to the search service the platform uses, currently DeepSeek's web search. With the plugin, the query is built from your last user messages (see above). With the server tool, the model writes the queries from the whole conversation, including the system prompt, earlier turns and tool results, so any of it can end up in a query. This also applies to web_search and the other alias tool types on /v1/chat/completions: Onlist runs them as its own search rather than passing them to the provider. If you have turned on the privacy filter in your account preferences, queries go through the same filter before they are sent to the search service.

The search engine is itself a call to a third-party model, so a request that asks for zero data retention (provider.zdr: true) is not searched: the plugin and :online do not run, the openrouter:web_search tool is removed so the model gets no search tool, and the response carries web_search: not searched; zero data retention requested. The request is otherwise served as usual. Requests that do not turn search on send nothing to a search service. See Privacy & data.

Limitations

  • The plugin and :online search once per request, on your last user messages, and only when the last message is from the user. The browser Chat uses the plugin too: one search per message.
  • No platform search under zero data retention (provider.zdr: true).
  • The server tool searches at most 5 rounds and 30 queries per request, with up to 4 queries per round, needs n of 1, and supports only the step_count_is and max_cost stop conditions.
  • Chat Completions only. On OpenRouter, the server tool also works on /v1/responses.
  • Codex's standalone search endpoint (/v1/alpha/search) is not available.

Next steps

  • Models: Model ids and the variant suffixes, including :online.
  • Provider routing: Combine search with provider.sort and the other routing controls.
  • Limits & headers: Every entry X-Onlist-Warnings can carry.
  • Usage & cost: The usage object and where each request's charge is recorded.