← Blog

Build AI-Powered Search with Claude: A Developer Guide

2026-09-25 · 5 min read · SubToAPI Team

AI-powered search with Claude means using retrieval to find relevant content and Claude to understand the query, reason over the results, and generate a grounded, natural-language answer — instead of returning a list of blue links. It's the architecture behind "ask your docs a question" products, internal knowledge bases, and support search boxes that actually answer questions.

Claude itself is not a search engine. It has no live index of your data and, unless you give it tools, no way to look anything up. What it's excellent at is turning a pile of retrieved text into a coherent, cited answer, deciding when it needs more information, and calling a search function on its own when the first query didn't return enough. This article covers the architecture, the retrieval step, and how to wire Claude into a working search pipeline using tool use and streaming.

The two-layer architecture

Every working "AI search" system has two layers:

  1. Retrieval layer — finds candidate documents or passages. This is usually a vector database (pgvector, Pinecone, Weaviate, Qdrant) storing embeddings of your content, or a traditional keyword/BM25 index, or both combined (hybrid search).
  2. Reasoning layer — Claude. It takes the query plus retrieved passages and produces an answer, decides if results are insufficient and re-queries, and cites sources.

Skipping the retrieval layer and just asking Claude "what does our documentation say about X" only works if the docs fit in the context window and you paste them in directly. For anything beyond a handful of pages, you need retrieval.

Step 1: Index your content

Chunk your documents into passages of a few hundred tokens each, generate embeddings for each chunk, and store them with metadata (source URL, title, timestamp). Chunking strategy matters more than model choice here — split on natural boundaries (headings, paragraphs) rather than fixed character counts, and keep enough overlap that answers don't get cut off mid-sentence.

// Pseudocode: index a document
const chunks = splitIntoChunks(document.text, { maxTokens: 400, overlap: 50 });
for (const chunk of chunks) {
  const embedding = await embed(chunk.text);
  await vectorDB.upsert({
    id: chunk.id,
    vector: embedding,
    metadata: { source: document.url, title: document.title, text: chunk.text }
  });
}

Step 2: Retrieve, then ask Claude

At query time, embed the user's question, pull the top-k matching chunks, and pass them to Claude as context. Ask Claude to answer only from the provided passages and to cite which source each claim came from — this cuts down on hallucinated answers and makes the search feel trustworthy.

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-20250514",
    "max_tokens": 600,
    "system": "Answer using only the provided passages. Cite the source title after each claim in brackets. If the passages do not answer the question, say so.",
    "messages": [{
      "role": "user",
      "content": "Passages:\n1. [Refund Policy] Refunds are processed within 5 business days.\n2. [Billing FAQ] Refund requests go to billing@example.com.\n\nQuestion: How long do refunds take?"
    }]
  }'

This request/response pattern is the core of most retrieval-augmented search products. For the full request shape, see the messages docs.

Step 3: Let Claude drive the search itself

A more capable pattern is to give Claude a search tool and let it decide when and what to query, rather than always retrieving once before every call. This handles multi-hop questions ("compare our refund policy to last year's") where a single retrieval pass isn't enough.

const tools = [{
  name: "search_docs",
  description: "Search the knowledge base for relevant passages",
  input_schema: {
    type: "object",
    properties: { query: { type: "string" } },
    required: ["query"]
  }
}];

const response = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
    "content-type": "application/json"
  },
  body: JSON.stringify({
    model: "claude-sonnet-4-20250514",
    max_tokens: 800,
    tools,
    messages: [{ role: "user", content: "How does our refund policy compare to 2023?" }]
  })
});

When Claude responds with a tool_use block, run your retrieval function with the query it generated, feed the results back as a tool_result, and let Claude continue the conversation. It might call the tool two or three times before answering. This loop is described in detail in the tool use docs.

Step 4: Stream the answer

Search UIs feel much faster when the answer streams token by token instead of appearing all at once after a multi-second wait, especially once you've added a retrieval round-trip and possibly a tool-use loop. Set "stream": true and read the server-sent events as they arrive. See the streaming guide for the event format and a working example.

Production considerations

A few things separate a search demo from something you'd put in front of real users:

This is where SubToAPI fits in: it turns your existing Claude access into an HTTPS API with application-scoped keys (sub_live_...), streaming, tool use, and usage metadata per key, so you can give each customer or environment its own key and see exactly what their search queries cost. Start with the quickstart, check pricing, or sign up for a free trial.

questions

Do I need a vector database to build search with Claude? For anything beyond a small, static set of documents, yes — a vector database (or hybrid keyword + vector index) handles retrieval, and Claude handles reasoning over the results. Pasting your entire knowledge base into every prompt doesn't scale in cost or latency.

Can Claude search the web directly? Not on its own. Claude can call a search tool you provide (a web search API, your internal index, etc.) via tool use, decide what to query, and reason over the results, but it needs that tool wired up — it has no built-in live index.

How do I stop Claude from making up answers in search results? Restrict it explicitly to the retrieved passages, require citations for each claim, and instruct it to say when the passages don't answer the question. Reranking retrieved chunks before passing them to Claude also reduces irrelevant context that leads to guessing.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →