Web search

⚠️
Preview — this feature is being built; the design below is the spec.

Add the built-in syntic_web_search tool and the model can search the web while it answers. Syntic runs the searches, feeds the results to the model and returns an answer with citations to the pages it used. You do not run anything yourself and there is no extra round trip: one request in, one cited answer out.

When to use it

  • Questions about recent events, prices, releases or anything after the model’s training.
  • Research summaries that must link their sources.
  • Checking facts before an agent acts on them.

For a page you already know the address of, use web fetch instead.

Endpoint and auth

POST https://app.syntic.ai/v1/chat/completions (and Messages), Authorization: Bearer sk_live_.... The model must list syntic_web_search in capabilities.server_tools. Organisation admins can turn web search off in the Console; requests then return 403 tool_disabled.

Tool definition

Add it to tools next to your own functions:

FieldTypeRequiredDetails
typestringYes"syntic_web_search"
max_usesintegerNoSearches allowed in this request, 1 to 20. Default 5.
allowed_domainsarray of stringsNoOnly return results from these domains (and their subdomains).
blocked_domainsarray of stringsNoNever return results from these. Cannot be combined with allowed_domains.
user_locationobjectNo{ "country": "GB", "region": "England", "city": "Leeds", "timezone": "Europe/London" } to localise results. All keys optional.
freshnessstringNoday, week, month, year or any (default).

How it works

  1. The model decides a search would help and writes a query.
  2. Syntic runs the search and passes the top results (title, address, snippet, page text extract) to the model.
  3. The model may search again, up to max_uses, then writes its answer with citations.

If the loop reaches its step limit before the model finishes, the reply ends with finish_reason: "pause"; send the conversation back unchanged to let it continue (see Stop reasons).

Response

{
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "The council confirmed on 3 October that garden waste collections move to fortnightly from 1 November.",
      "annotations": [{
        "type": "citation",
        "start_index": 0,
        "end_index": 98,
        "source": { "url": "https://www.example.gov.uk/news/garden-waste", "title": "Garden waste changes" },
        "location": { "type": "char", "start_char": 120, "end_char": 260 },
        "quote": "From 1 November, garden waste will be collected every two weeks."
      }],
      "server_tool_calls": [
        { "id": "srv_01", "type": "syntic_web_search", "query": "Leeds garden waste collection change November 2026", "results": 8, "status": "ok" }
      ]
    },
    "finish_reason": "stop"
  }],
  "usage": {
    "prompt_tokens": 5120, "completion_tokens": 64, "total_tokens": 5184,
    "server_tool_use": { "web_search_requests": 1 }
  }
}
FieldDescription
message.server_tool_calls[]What Syntic ran: query, number of results, and status (ok, no_results, failed). For your logs; do not send it back.
message.annotations[]Citations to the pages used. Show them to users.
usage.server_tool_use.web_search_requestsSearches billed.

Streaming

While a search runs, the stream sends a progress delta so your UI can show it:

data: {"choices":[{"index":0,"delta":{"server_tool_call":{"id":"srv_01","type":"syntic_web_search","query":"Leeds garden waste collection change November 2026","status":"running"}}}]}

followed by the same object with "status": "ok" and then the answer text. Keep-alive comments are sent during long searches.

Examples

curl https://app.syntic.ai/v1/chat/completions \
  -H "Authorization: Bearer $SYNTIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Amara S1.2",
    "stream": false,
    "tools": [{
      "type": "syntic_web_search",
      "max_uses": 3,
      "user_location": { "country": "GB", "city": "Leeds" },
      "freshness": "month"
    }],
    "messages": [{ "role": "user", "content": "Are Leeds garden waste collections changing this winter?" }]
  }'

Web search can be combined with your own function tools in the same tools array. Function calls are returned to you as usual; searches are run by Syntic.

Errors

Unified envelope. A failed individual search does not fail the request; it shows status: "failed" and the model carries on.

StatuscodeparamCause
400server_tool_not_supportedtools[i].typeThe model cannot use this tool.
400invalid_domain_filtertools[i].allowed_domainsBoth lists sent, a malformed domain, or more than 100 entries.
400invalid_max_usestools[i].max_usesOutside 1 to 20.
403tool_disabledtools[i].typeTurned off for your organisation.
402quota_exhaustedNot enough allowance or credit for the search fees.

Pricing and limits

Search fee$10 for every 1,000 searches, plus the tokens of the results the model reads (billed as input)
Searches per request20
Zero data retentionAvailable. Queries are sent to the search provider without your organisation id and are not stored by Syntic.

Search fees appear as their own line on the cost report.

Build notes for the platform team

  • Engine exists (web-search function, used by chat-proxy). Needs a server-side tool loop inside api-proxy: run model, execute server tools, loop, then return.
  • Never pass the provider name of the search backend to the client.
  • Bill searches as separate usage lines (web_search) so they never mix with token counts.
Was this page helpful?