Developer Console AI Chatbot ← Home

Strut AI Grounding Engine — Developer Integration

Strut is a web grounding engine. It discovers sources, extracts their readable content, ranks the passages that answer your query, and returns them with quotes verified against the page — optionally with a synthesized answer on top.

Grounded, with the receipts: /v1/ground fetches live pages, isolates the passages that answer the query, and returns each with a quote taken from the page and a bracketed citation [1]. Retrieval-only responses typically land in ~1.3s; adding a synthesized answer takes about 2s.

Quick cURL Test

bash
curl -X POST "$STRUT_BASE_URL/v1/ground" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $STRUT_API_KEY" \
  -d '{
    "query": "What are topological quantum qubits?",
    "max_sources": 3
  }'

Telemetry & Pipeline Benchmarks

Every grounding response includes microsecond-precision breakdown telemetry:

< 3.5ms
Zero-Copy HTML Extractor

Strips ads, scripts, navbars, and converts raw HTML directly to clean semantic markdown.

< 1.0ms
Passage Chunker

Preserves header hierarchy and extracts contextual 350-word passages.

< 2.0ms
Neural BM25 Reranker

Exact phrase matching and semantic alignment ranking across evaluated chunks.

100%
Citation Verification

Every factual claim is aligned to exact source URLs and quotes with `[1]`, `[2]` tags.

Authentication & Keys

Every request to /v1/ground, /v1/search and /v1/extract must carry an API key. Create one in the developer console.

bash
curl -X POST "https://your-deployment.example.com/v1/search" \
  -H "Authorization: Bearer strut_live_..." \
  -H "Content-Type: application/json" \
  -d '{"query": "who invented the transistor"}'

x-api-key: strut_live_... is accepted as an alternative to the Authorization header.

Scopes

Each key carries a scope, chosen when the key is created and enforced on every request.

ScopeGrants
full/v1/ground, /v1/search, /v1/extract
ground-only/v1/ground
search-only/v1/search
extract-only/v1/extract

Rate limits

Each key has a per-minute request limit, set when the key is created. Every response carries the current state:

HeaderMeaning
X-RateLimit-LimitRequests permitted per minute for this key.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetSeconds until the window resets.
Retry-AfterSent with 429; seconds to wait before retrying.

Rate limit counters are held in a shared store, so every server instance counts a key together. Windows are fixed one-minute windows: a caller can use up to twice the limit across a window boundary.

What is metered

/v1/ground, /v1/search and /v1/extract each count as one query against your usage and your monthly spend cap, at the rate shown by GET /v1/models. (Before October 2026 only /v1/ground was counted.)

Error responses

StatusMeaning
401No key supplied, or the key is unknown or revoked.
403The key is valid but its scope does not cover this endpoint.
429The key's per-minute rate limit is exhausted. Also sent for requests with no key when the shared no-key allowance is used up, and for the Nova model over its per-caller allowance.
400The request body is not valid JSON, or a field has the wrong type or is out of range.
402The account's monthly spend cap has been reached. Months are calendar months in UTC; the cap resets on the 1st.

Every error body is JSON with an error field and, where useful, a hint.

Degraded results

When the search provider was rate limited or failed while your request ran, the response is still returned with whatever was found and telemetry.degraded is true (telemetry.discovery_sources names the cause). Degraded results are never cached, so retrying gets a fresh attempt.

POST /v1/ground

Generates an end-to-end factually grounded answer from live web sources with verified quotes and bracketed citations.

Request Parameters

FieldTypeDefaultDescription
querystringRequiredThe question or prompt to ground against the live web.
max_sourcesnumber5Maximum number of candidate web pages to evaluate (1–10; larger values are clamped to 10).
prefer_domainsarray—Domains to rank higher (at most 20), for any question. Unlike include_domains it does not exclude anything: it only moves passages from those sites up. For questions about company figures (revenue, net income, EPS) passages from the SEC, regulators, newswire services and investor-relations sites are also ranked higher automatically.
urlsarray—Pages to ground on instead of searching; at most 10 are used. Only public http(s) pages are fetched: private, loopback and link-local addresses are refused. query may be up to 2,000 characters.
modelstringstrut/web-groundingModel identifier (strut/web-grounding, strut-ground-v1, groq/openai/gpt-oss-20b).
include_answerbooleanfalseWhen false (default) the response returns extracted, reranked passages with verified quotes and no synthesized prose — roughly 1s (advanced) and zero token cost. Recommended when the consumer is an LLM. Set true for a written answer (adds the model call).
search_depthstringadvancedbasic grounds on excerpts from the search index with no page fetches — the fastest mode, comparable to Tavily basic. advanced fetches and extracts each page for verified quotes. Every result carries published_date (ISO 8601) when it is known. Pages that cannot be fetched in time are filled from their search-index excerpts; each result carries content_source (page or index_excerpt), and only page results can have a verified quote. fast and deep are accepted as aliases.
topicstringgeneralgeneral, news or finance. news (and finance, served the same way) searches news indexes only, so every result carries a publication date.
daysinteger—Only results published in the last N days (1–3650).
time_rangestring—day, week, month or year. An alternative to days.
start_date / end_datestring—YYYY-MM-DD. An explicit window, inclusive; end_date defaults to today. Takes precedence over days. With any recency window, results with a known date outside it are removed, and undated results are kept only when the search index applied the window itself. The response echoes the applied window in filters.
streambooleanfalseWhether to stream response tokens via Server-Sent Events (SSE).

Response Payload

json
{
  "query": "What are topological qubits?",
  "grounded_answer": "Topological qubits store quantum information in non-local topological properties of matter [1]...",
  "citations": [
    {
      "source_index": 1,
      "title": "Topological quantum computer - Wikipedia",
      "url": "https://en.wikipedia.org/wiki/Topological_quantum_computer",
      "verified_quote": "Topological qubits are quantum bits that store information in non-local...",
      "relevance_score": 0.8842
    }
  ],
  "model": "strut/web-grounding",
  "telemetry": {
    "fetchMs": 450,
    "extractMs": 18,
    "chunkMs": 2,
    "rerankMs": 6,
    "synthesis_ms": 980,
    "total_end_to_end_ms": 1456
  }
}

POST /v1/extract

Fetch a list of URLs and return their readable content as clean Markdown, with scripts, styles and navigation removed. Useful when you already know which pages you want.

FieldTypeDefaultDescription
urlsstring[]—Required. URLs to fetch and extract.

GET /v1/models

Engine capabilities, the models available for synthesis, and the metered rate. Unauthenticated callers get the capabilities, the rate and one coarse flag (providers.web_search_index.independent_index_configured); send an API key to also see which providers are configured and the account store. The developer console reads the rate from here so the price it displays cannot drift from the rate the spend cap is enforced against.

json
{
  "engine": "Strut Grounding Engine",
  "pricing": { "cost_per_query": 0.007, "currency": "USD" },
  "providers": { "web_search_index": { "mojeek_configured": false } }
}

SSE Token Streaming

Set stream: true on /v1/ground to receive the answer as Server-Sent Events. Citations are resolved before the first token, so the source list is available immediately and the prose arrives incrementally. Streaming implies include_answer; a retrieval-only request has nothing to stream.

bash
curl -N -X POST "$STRUT_BASE_URL/v1/ground" \
  -H "Authorization: Bearer $STRUT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "...", "stream": true, "include_answer": true}'

LangChain & LangGraph Integration

Use Strut as a verified live grounding tool in your autonomous AI agents:

python
from langchain.tools import tool
import requests

@tool
def strut_grounding_tool(query: str) -> str:
    """Search the live web and obtain factually grounded synthesis with verified citations."""
    res = requests.post(
        "$STRUT_BASE_URL/v1/ground",
        json={"query": query, "max_sources": 4},
        headers={"Content-Type": "application/json"}
    )
    if not res.ok:
        return f"Lookup error: {res.text}"
    data = res.json()
    return data.get("grounded_answer", "")

# Attach to LangChain Agent
from langchain_openai import ChatOpenAI
from langchain.agents import create_openai_tools_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

tools = [strut_grounding_tool]
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
prompt = ChatPromptTemplate.from_messages([
    ("system", "You are an autonomous research agent using Strut AI web grounding."),
    MessagesPlaceholder(variable_name="chat_history", optional=True),
    ("user", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad"),
])

agent = create_openai_tools_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# Run Query
response = executor.invoke({"input": "What are the latest developments in quantum qubits in 2026?"})
print(response["output"])

Vercel AI SDK Integration

Wire Strut grounding seamlessly with the ai package in Next.js App Router:

typescript
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai('gpt-4o-mini'),
    messages,
    tools: {
      strutGround: tool({
        description: 'Ground live web queries with sub-second verified factual citations',
        parameters: z.object({
          query: z.string().describe('The web search query to ground factually')
        }),
        execute: async ({ query }) => {
          const res = await fetch('$STRUT_BASE_URL/v1/ground', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ query, max_sources: 4 })
          });
          return await res.json();
        }
      })
    }
  });

  return result.toDataStreamResponse();
}

Strut Python SDK Client

Lightweight drop-in client for Python backends:

python
from sdk.python.strut import StrutClient

client = StrutClient(base_url="https://your-deployment.example.com", api_key=os.environ["STRUT_API_KEY"])

# 1. Factual Grounding
grounded = client.ground("What is topological quantum computing?")
print("Answer:", grounded["grounded_answer"])

# 2. Fast AI Search
search_res = client.search("Latest AI breakthroughs 2026", search_depth="fast")
for item in search_res["results"]:
    print(f"• {item['title']} ({item['domain']}): {item['snippet'][:100]}...")

# 3. Batch Markdown Extraction
extracted = client.extract(["https://en.wikipedia.org/wiki/Quantum_computing"])
print("Markdown preview:", extracted["results"][0]["markdown"][:300])