Skip to content
Built-in Web Search

Built-in Web Search

Set web_search: true and the model grounds its answer with live results.

POST https://api.shannon-ai.com/v1/chat/completions

The reply is an ordinary chat completion with two additions. The answer points to the search results with numbers in square brackets, sources says what each number stands for, and annotations marks where each one is cited:

200 JSON
{
  "id": "chatcmpl-3e5a7c9b1d2f4a6c8e0b2d4f6a8c1e3b",
  "object": "chat.completion",
  "created": 1791590400,
  "model": "shannon-3",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Saturday in Lisbon is forecast to be sunny with a high near 24 °C [1]. Sunday turns cloudy, with a chance of light rain in the afternoon [2].",
        "reasoning_content": null,
        "annotations": [
          {
            "type": "url_citation",
            "url_citation": {
              "start_index": 66,
              "end_index": 69,
              "url": "https://weather.example/lisbon/weekend",
              "title": "Lisbon weather: weekend outlook"
            }
          },
          {
            "type": "url_citation",
            "url_citation": {
              "start_index": 137,
              "end_index": 140,
              "url": "https://forecast.example/pt/lisbon/sunday",
              "title": "Sunday forecast for Lisbon"
            }
          }
        ]
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 2874,
    "completion_tokens": 41,
    "total_tokens": 2915
  },
  "sources": [
    {
      "index": 1,
      "title": "Lisbon weather: weekend outlook",
      "url": "https://weather.example/lisbon/weekend"
    },
    {
      "index": 2,
      "title": "Sunday forecast for Lisbon",
      "url": "https://forecast.example/pt/lisbon/sunday"
    },
    {
      "index": 3,
      "title": "Portugal: the week ahead",
      "url": "https://news.example/portugal/weather-week"
    }
  ]
}

Switching search on

Search is a field of the request, off by default. Nothing is searched unless a request sends it.

Field Type Default Description Applied by
web_search boolean false true runs a web search for this request and gives the results to the model before it answers. Every Shannon model except shannon-coder-1
Endpoint Web search How
/v1/chat/completions Yes "web_search": true in the request body.
/v1/messages Yes "web_search": true in the request body.
/v1/responses — This endpoint answers from the model alone. Use one of the two above for a searched answer.

On /v1/messages the field sits at the top level of the body, next to model and messages:

Request body
{
  "model": "shannon-3",
  "web_search": true,
  "messages": [
    {
      "role": "user",
      "content": "What is the weather forecast for Lisbon this weekend?"
    }
  ]
}

What a searched request does

  • The API searches the web for the last user message. Earlier turns of the conversation give the search its context.
  • The search runs on every request that sends web_search: true. The model does not choose whether to search.
  • The results are put in front of the model as numbered sources, and the model writes its answer from them.
  • The reply starts when the search is done. A streamed request sends nothing, neither headers nor events, until then, so allow for a longer wait before the first chunk.

What the reply contains

  • The reply has the usual shape of its endpoint. The answer is in choices[0].message.content (a text block on /v1/messages).
  • The text can carry markers such as [1] and [2] after the statements they support. Each number stands for one of the results the model read.
  • sources names those results: one entry for each result the model was given, with index, title and url. The marker [1] is the entry with index 1. Results the answer does not cite are listed too.
  • On /v1/chat/completions the message also has annotations, in the OpenAI shape: one url_citation for each source a marker names, with url, title, and start_index and end_index, the position of the marker in content counted in characters (the end is not included).
  • In a stream sources comes with the last chunk, and on /v1/messages with the message_delta event. The annotations come in one chunk just before the last one, as delta.annotations.
  • sources is there only when the search of this request found something. A reply without it was written without search results.

How searches are counted

Every plan includes a number of searches per day:

Plan Searches per day
Free 3
Plus 30
Standard 50
Pro 60
  • A request whose search found something uses one search of the day's allowance. A search that found nothing uses none.
  • Chat and API share the allowance of the account that owns the key.
  • The count starts again every day at 00:00 UTC.
  • The search results are input tokens of the request and are billed with the rest of the prompt.

Compare the plans

When the day's searches are used up

A request that sends web_search: true after the last search of the day is not refused. It is answered without a search: the status is 200 and the reply has no sources.

Search as a tool of your own

web_search: true is the only switch of the search described here. A function that you define in tools and name web_search is one of your own tools: the model returns a call, and your code runs the search and sends the result back.

Search tool types of other platforms, such as web_search_20250305 on /v1/messages or {"type": "web_search"} on /v1/responses, do not start a search here.

Errors

Web search adds no status code and no error type. A search that finds nothing, or a day without searches left, gives a normal answer without search results. Every other error is the same as for a request without search. Error Handling