← Back to daily report

build-with-claude/search-results.md

Changed on 2026-07-21 15:11:52 EST

+488 lines added
-270 lines removed
Visual Diff
# Search results¶

Enable natural citations for RAG applications by providing search results with source attribution¶

---¶

<Note>¶
For how zero data retention (ZDR) applies to this feature, see [API and data retention](/docs/en/manage-claude/api-and-data-retention).¶
</Note>¶

Search result content blocks
enable natural citations with proper source attribution, bringing web search-quality citations to your custom applications. This feature is particularly powerful for RAG (Retrieval-Augmented Generation) applications where you need Claude to cite sources accurately.¶

The search results feature is available on the following models:¶

* Claude Opus 4.8 (claude-opus-4-8)¶
* Claude Opus 4.7 (`claude-opus-4-7`)¶
* Claude Opus 4.6 (`claude-opus-4-6`)¶
* Claude Sonnet 5 (`claude-sonnet-5`)¶
* Claude Sonnet 4.6 (`claude-sonnet-4-6`)¶
* Claude Sonnet 4.5 (`claude-sonnet-4-5-20250929`)¶
* Claude Opus 4.5 (`claude-opus-4-5-20251101`)¶
* Claude Opus 4.1 ([deprecated](/docs/en/about-claude/model-deprecations)) (`claude-opus-4-1-20250805`)¶
* Claude Opus 4 ([retired, except on Google Cloud](/docs/en/about-claude/model-deprecations)) (`claude-opus-4-20250514`)¶
* Claude Sonnet 4 ([retired, except on Bedrock and Google Cloud](/docs/en/about-claude/model-deprecations)) (`claude-sonnet-4-20250514`)¶
* Claude Haiku 4.5 (`claude-haiku-4-5-20251001`)¶
* Claude Haiku 3.5 ([retired,
let Claude cite your own content the same way it cites web search results: each citation carries the source and title you provided. Use them in RAG (Retrieval-Augmented Generation) applications where Claude needs to attribute answers to your documents.¶

All [active models](/docs/en/about-claude/models/overview) support search results with citations, with the
except ion Bedrock and Google Cloud](/docs/en/about-claude/model-deprecations)) (`claude-3-5-haiku-20241022`)¶

## Key benefits¶

* **Natural citations:** Achieve the same citation quality as web search for any content¶
* **Flexible integration:** Use in tool returns for dynamic RAG or as top-level content for pre-fetched data¶
* **Proper source attribution:** Each result includes source and title information for clear attribution¶
* **No document workarounds needed:** Eliminates the need for document-based workarounds¶
* **Consistent citation format:** Matches the citation quality and format of Claude's web search functionality
of Claude Haiku 3. No beta header is required: search results are part of the standard Messages API.

## How it works¶

Search results can be provided in two ways:¶

1. **From tool calls:** Your custom tools return search results, enabling dynamic RAG applications¶
2. **As top-level content:** You provide search results directly in user messages for pre-fetched or cached content¶

In both cases, Claude c
an automatically cite information from the search results with proper source attributionites the search results automatically when citations are enabled. No special prompting is needed: ask your question, and citations appear on the text blocks that draw on your content.¶

### Search result schema¶

Search results use the following structure:¶

```json¶
{¶
"type": "search_result",¶
"source": "https://example.com/article", // Required: Source URL or identifier¶
"title": "Article Title", // Required: Title of the result¶
"content": [¶
// Required: Array of text blocks¶
{¶
"type": "text",¶
"text": "The actual content of the search result..."¶
}¶
],¶
"citations": {¶
// Optional: Citation configuration¶
"enabled": true // Enable/disable citations for this result¶
}¶
}¶
```¶

### Required fields¶

| Field | Type | Description
|¶
| --------- | ------ | ----------------------------------------------------- |¶
| `type` | string | Must be `"search_result"` |¶
| `source` | string | The source URL or identifier for the content |¶
| `title` | string | A descriptive title for the search result |¶
| `content` | array | An array of text blocks containing the actual content |¶

### Optional fields¶

| Field | Type | Description |¶
| --------------- | ------ | ------------------------------------------------------------- |¶
| `citations` | object | Citation configuration with `enabled` Boolean field |¶
| `cache_control` | object | Cache control settings (for example, `{"type": "ephemeral"}`) |¶

Each item in the `content` array must be a text block with:¶

* `type`: Must be `"text"`¶
* `text`: The actual text content (non-empty string)¶

## Method 1: Search results from tool calls¶

The most powerful use case is returning search results from your custom tools. This enables dynamic RAG applications where tools fetch and return relevant content with automatic citations.¶

### Example: Knowledge base tool¶

<CodeGroup exclude="shell">
|¶
| --------- | ------ | ---------------------------------------------------------------------------------------------------------------- |¶
| `type` | string | Must be `"search_result"` |¶
| `source` | string | The source of the content. Any stable string works: a URL, or an internal identifier such as `kb://article-1234` |¶
| `title` | string | A descriptive title for the search result |¶
| `content` | array | An array of text blocks containing the actual content |¶

### Optional fields¶

| Field | Type | Description |¶
| --------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |¶
| `citations` | object | Citation configuration with `enabled` Boolean field. Citations are disabled by default; every example on this page sets `"enabled": true` explicitly. All search results in a request must use the same setting (see [Citation control](#citation-control)) |¶
| `cache_control` | object | Cache control settings (for example, `{"type": "ephemeral"}`) |¶

Each item in the `content` array must be a text block with:¶

* `type`: Must be `"text"`¶
* `text`: The actual text content (non-empty string)¶

Search results hold text only. Images and other media are not supported inside the `content` array.¶

## Method 1: Search results from tool calls¶

Returning search results from your custom tools enables dynamic RAG applications: tools fetch content at runtime, and Claude cites it in the response. The following example forces the tool call with [`tool_choice`](/docs/en/agents-and-tools/tool-use/define-tools#forcing-tool-use), so the retrieval step runs every time.¶

### Example: Knowledge base tool¶

<CodeGroup>¶
```bash cURL¶
# The tool-calling flow needs application-side search logic that doesn't¶
# translate to a one-off shell command. See the SDK tabs for the full flow.¶
# The raw shape of a tool conversation with search results is shown in the¶
# Combining both methods cURL tab; Method 2 shows the top-level shape.¶
```¶

```bash CLI¶
# The tool-calling flow needs application-side search logic that doesn't¶
# translate to a one-off shell command. See the SDK tabs for the full flow.¶
# The raw shape of a tool conversation with search results is shown in the¶
# Combining both methods cURL tab; Method 2 shows the top-level shape.¶
```¶

```python Python¶
from anthropic.types import (¶
MessageParam,¶
TextBlockParam,¶
SearchResultBlockParam,¶
ToolResultBlockParam,¶
)¶

client = Anthropic()¶

# Define a knowledge base search tool¶
knowledge_base_tool = {¶
"name": "search_knowledge_base",¶
"description": "Search the company knowledge base for information",¶
"input_schema": {¶
"type": "object",¶
"properties": {"query": {"type": "string", "description": "The search query"}},¶
"required": ["query"],¶
},¶
}¶


# Function to handle the tool call¶
def search_knowledge_base(query):¶
# Your search logic here¶
# Returns search results in the correct format¶
return [¶
SearchResultBlockParam(¶
type="search_result",¶
source="https://docs.company.com/product-guide",¶
title="Product Configuration Guide",¶
content=[¶
TextBlockParam(¶
type="text",¶
text="To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs.",¶
)¶
],¶
citations={"enabled": True},¶
),¶
SearchResultBlockParam(¶
type="search_result",¶
source="https://docs.company.com/troubleshooting",¶
title="Troubleshooting Guide",¶
content=[¶
TextBlockParam(¶
type="text",¶
text="If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values.",¶
)¶
],¶
citations={"enabled": True},¶
),¶
]¶


#
Create a message with the tool¶
response = client.messages.create(¶
model="claude-opus-4-8", # Works with all supported models¶
max_tokens=1024,¶
tools=[knowledge_base_tool],¶
messages=[¶
MessageParam(role="user", content="How do I configure the timeout settings?")¶
],¶
)¶

# When Claude calls the tool, provide the search results¶
if response.content[0].type == "tool_use":¶
tool_result = search_knowledge_base(response.content[0].input["query"])¶

# Send the tool result back¶
final_response = client.messages.create(¶
model="claude-opus-4-8", # Works with all supported models¶
max_tokens=1024,¶
messages=[¶
MessageParam(¶
role="user", content="How do I configure the timeout settings?"¶
),¶
MessageParam(role="assistant", content=response.content),¶
MessageParam(¶
role="user",¶
content=[¶
ToolResultBlockParam(¶
type="tool_result",¶
tool_use_id=response.content[0].id,¶
content=tool_result, # Search results go here¶
)¶
],¶
),¶
],¶
Build up the conversation in a list, starting with the user's question¶
messages = [¶
MessageParam(role="user", content="How do I configure the timeout settings?")¶
]¶

# Create a message with the tool¶
response = client.messages.create(¶
model="claude-opus-4-8",¶
max_tokens=1024,¶
tools=[knowledge_base_tool],¶
tool_choice={"type": "tool", "name": "search_knowledge_base"},¶
messages=messages,¶
)¶

# When Claude calls the tool, provide the search results.¶
# The tool_use block is not always first: iterate to find it.¶
tool_use = next((block for block in response.content if block.type == "tool_use"), None)¶
if tool_use is not None:¶
tool_result = search_knowledge_base(tool_use.input["query"])¶

# Append Claude's turn, then the tool result, to the running conversation¶
messages.append(MessageParam(role="assistant", content=response.content))¶
messages.append(¶
MessageParam(¶
role="user",¶
content=[¶
ToolResultBlockParam(¶
type="tool_result",¶
tool_use_id=tool_use.id,¶
content=tool_result, # Search results go here¶
)¶
],¶
)¶
)¶

# Send the tool result back¶
final_response = client.messages.create(¶
model="claude-opus-4-8",¶
max_tokens=1024,¶
messages=messages,¶
)¶
print(final_response
)¶
```¶

```typescript TypeScript¶
const anthropic = new Anthropic();¶

// Define a knowledge base search tool¶
const knowledgeBaseTool: Anthropic.
Messages.Tool = {¶
name: "search_knowledge_base",¶
description: "Search the company knowledge base for information",¶
input_schema: {¶
type: "object" as const,¶
properties: {¶
query: {¶
type: "string",¶
description: "The search query"¶
}¶
},¶
required: ["query"]¶
}¶
};¶

// Function to handle the tool call¶
function searchKnowledgeBase(query: string) {¶
// Your search logic here¶
// Returns search results in the correct format¶
return [¶
{¶
type: "search_result" as const,¶
source: "https://docs.company.com/product-guide",¶
title: "Product Configuration Guide",¶
content: [¶
{¶
type: "text" as const,¶
text: "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs."¶
}¶
],¶
citations: { enabled: true }¶
},¶
{¶
type: "search_result" as const,¶
source: "https://docs.company.com/troubleshooting",¶
title: "Troubleshooting Guide",¶
content: [¶
{¶
type: "text" as const,¶
text: "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values."¶
}¶
],¶
citations: { enabled: true }¶
}¶
];¶
}¶

//
Create a message with the tool¶
const response = await anthropic.messages.create({¶
model: "claude-opus-4-8", // Works with all supported models¶
max_tokens: 1024,¶
tools: [knowledgeBaseTool],¶
messages: [¶
{¶
role: "user",¶
content: "How do I configure the timeout settings?"¶
}¶
]¶
});¶

// Handle tool use and provide results¶
if (response.content[0].type === "tool_use") {¶
const input = response.content[0].input as { query: string };¶
const toolResult = searchKnowledgeBase(input.query);¶

const finalResponse = await anthropic.messages.create({¶
model: "claude-opus-4-8", // Works with all supported models¶
max_tokens: 1024,¶
messages: [¶
{ role: "user", content: "How do I configure the timeout settings?" },¶
{ role: "assistant", content: response.content },¶
{¶
role: "user",¶
content: [¶
{¶
type: "tool_result" as const,¶
tool_use_id: response.content[0].id,¶
content: toolResult // Search results go here¶
}¶
]¶
}¶
]¶
});¶
}¶
```¶

```csharp C#¶
using System;¶
using System.Collections.Generic;¶
using System.Threading.Tasks;¶
using Anthropic;¶
using Anthropic.Models.Messages;¶

public class Program¶
{¶
public static async Task Main(string[] args)¶
{¶
AnthropicClient client = new();¶

var knowledgeBaseTool = new Tool¶
{¶
Name = "search_knowledge_base",¶
Description = "Search the company knowledge base for information",¶
InputSchema = new¶
{¶
type = "object",¶
properties = new¶
{¶
query = new¶
{¶
type = "string",¶
description = "The search query"¶
}¶
},¶
required = new[] { "query" }¶
}¶
};¶

var parameters = new MessageCreateParams¶
{¶
Model = Model.ClaudeOpus4_8,¶
MaxTokens = 1024,¶
Tools = new[] { knowledgeBaseTool },¶
Messages = new[]¶
{¶
new MessageParam¶
{¶
Role = Role.User,¶
Content = "How do I configure the timeout settings?"¶
}¶
}¶
};¶

var response = await client.Messages.Create(parameters);¶

if (response.Content[0] is ToolUseBlock toolUse)¶
{¶
var toolResult = SearchKnowledgeBase(toolUse.Input["query"].ToString());¶

var finalParameters = new MessageCreateParams¶
{¶
Model = Model.ClaudeOpus4_8,¶
MaxTokens = 1024,¶
Messages = new[]¶
{¶
new MessageParam { Role = Role.User, Content = "How do I configure the timeout settings?" },¶
new MessageParam { Role = Role.Assistant, Content = response.Content },¶
new MessageParam¶
{¶
Role = Role.User,¶
Content = new[]¶
{¶
new ToolResultBlockParam¶
{¶
ToolUseID = toolUse.Id,¶
Content = toolResult¶
}¶
}¶
}¶
}¶
};¶

var finalResponse = await client.Messages.Create(finalParameters);¶
Console.WriteLine(finalResponse);¶
}¶
}¶

private static List<SearchResultBlockParam> SearchKnowledgeBase(string query)¶
{¶
return new List<SearchResultBlockParam>¶
{¶
new SearchResultBlockParam¶
{¶
Source = "https://docs.company.com/product-guide",¶
Title = "Product Configuration Guide",¶
Content = new[]¶
{¶
new TextBlockParam¶
{¶
Text = "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs."¶
}¶
},¶
Citations = new CitationsConfigParam { Enabled = true }¶
},¶
new SearchResultBlockParam¶
{¶
Source = "https://docs.company.com/troubleshooting",¶
Title = "Troubleshooting Guide",¶
Content = new[]¶
{¶
new TextBlockParam¶
{¶
Text = "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values."¶
}¶
},¶
Citations = new CitationsConfigParam { Enabled = true }¶
}¶
};¶
}¶
}¶
```¶

```go Go¶
client := anthropic.NewClient()¶

knowledgeBaseTool := anthropic.ToolUnionParam{¶
OfTool: &anthropic.ToolParam{¶
Name: "search_knowledge_base",¶
Description: anthropic.String("Search the company knowledge base for information"),¶
InputSchema: anthropic.ToolInputSchemaParam{¶
Properties: map[string]any{¶
"query": map[string]any{¶
"type": "string",¶
"description": "The search query",¶
},¶
},¶
Required: []string{"query"},¶
},¶
},¶
}¶

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_8,¶
MaxTokens: 1024,¶
Tools: []anthropic.ToolUnionParam{knowledgeBaseTool},¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(anthropic.NewTextBlock("How do I configure the timeout settings?")),¶
},¶
})¶
if err != nil {¶
log.Fatal(err)¶
}¶

for _, block := range response.Content {¶
switch variant := block.AsAny().(type) {¶
case anthropic.ToolUseBlock:¶
var input struct {¶
Query string `json:"query"`¶
}¶
if err := json.Unmarshal(variant.Input, &input); err != nil {¶
log.Fatal(err)¶
}¶
toolResults := searchKnowledgeBase(input.Query)¶

// Build assistant message from the response¶
assistantParam := response.ToParam()¶

finalResponse, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_8,¶
MaxTokens: 1024,¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(anthropic.NewTextBlock("How do I configure the timeout settings?")),¶
assistantParam,¶
anthropic.NewUserMessage(anthropic.ContentBlockParamUnion{¶
OfToolResult: &anthropic.ToolResultBlockParam{¶
ToolUseID: variant.ID,¶
Content: toolResults,¶
},¶
}),¶
},¶
})¶
if err != nil {¶
log.Fatal(err)¶
}¶
fmt.Println(finalResponse)¶
}
Build up the conversation in a list, starting with the user's question¶
const messages: Anthropic.MessageParam[] = [¶
{ role: "user", content: "How do I configure the timeout settings?" }¶
];¶

// Create a message with the tool¶
const response = await anthropic.messages.create({¶
model: "claude-opus-4-8",¶
max_tokens: 1024,¶
tools: [knowledgeBaseTool],¶
tool_choice: { type: "tool", name: "search_knowledge_base" },¶
messages¶
});¶

// Handle tool use and provide results.¶
// The tool_use block is not always first: find it in the content array.¶
const toolUse = response.content.find(¶
(block): block is Anthropic.ToolUseBlock => block.type === "tool_use"¶
);¶
if (toolUse) {¶
const input = toolUse.input as { query: string };¶
const toolResult = searchKnowledgeBase(input.query);¶

// Append Claude's turn, then the tool result, to the running conversation¶
messages.push({ role: "assistant", content: response.content });¶
messages.push({¶
role: "user",¶
content: [¶
{¶
type: "tool_result" as const,¶
tool_use_id: toolUse.id,¶
content: toolResult // Search results go here¶
}¶
]¶
});¶

// Send the tool result back¶
const finalResponse = await anthropic.messages.create({¶
model: "claude-opus-4-8",¶
max_tokens: 1024,¶
messages¶
});¶
console.log(finalResponse);¶
}¶
```¶

```csharp C#¶
AnthropicClient client = new();¶

var tools = new List<ToolUnion>¶
{¶
new ToolUnion(new Tool()¶
{¶
Name = "search_knowledge_base",¶
Description = "Search the company knowledge base for information",¶
InputSchema = new InputSchema()¶
{¶
Properties = new Dictionary<string, JsonElement>¶
{¶
["query"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The search query" }),¶
},¶
Required = ["query"],¶
},¶
}),¶
};¶

// Function to handle the tool call¶
static List<Block> SearchKnowledgeBase(string query)¶
{¶
// Your search logic here¶
// Returns search results in the correct format¶
return¶
[¶
new SearchResultBlockParam¶
{¶
Source = "https://docs.company.com/product-guide",¶
Title = "Product Configuration Guide",¶
Content = [new() { Text = "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs." }],¶
Citations = new() { Enabled = true },¶
},¶
new SearchResultBlockParam¶
{¶
Source = "https://docs.company.com/troubleshooting",¶
Title = "Troubleshooting Guide",¶
Content = [new() { Text = "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values." }],¶
Citations = new() { Enabled = true },¶
},¶
];¶
}¶

// Build up the conversation in a list, starting with the user's question¶
List<MessageParam> messages = [new() { Role = Role.User, Content = "How do I configure the timeout settings?" }];¶

// Create a message with the tool¶
var response = await client.Messages.Create(new()¶
{¶
Model = Model.ClaudeOpus4_8,¶
MaxTokens = 1024,¶
Tools = tools,¶
ToolChoice = new ToolChoiceTool { Name = "search_knowledge_base" },¶
Messages = messages,¶
});¶

// When Claude calls the tool, provide the search results.¶
// The tool_use block is not always first: find the first one.¶
foreach (var block in response.Content)¶
{¶
if (block.TryPickToolUse(out var toolUse))¶
{¶
var query = toolUse.Input["query"].GetString() ?? "";¶
var toolResults = SearchKnowledgeBase(query);¶

// Append Claude's turn, then the tool result, to the running conversation¶
messages.Add(new() { Role = Role.Assistant, Content = response.Content.Select(contentBlock => new ContentBlockParam(contentBlock.Json)).ToList() });¶
messages.Add(new()¶
{¶
Role = Role.User,¶
Content = new MessageParamContent(¶
[new ContentBlockParam(new ToolResultBlockParam() { ToolUseID = toolUse.ID, Content = new ToolResultBlockParamContent(toolResults) })]¶
),¶
});¶

// Send the tool result back¶
var finalResponse = await client.Messages.Create(new()¶
{¶
Model = Model.ClaudeOpus4_8,¶
MaxTokens = 1024,¶
Messages = messages,¶
});¶
Console.WriteLine(finalResponse);¶
break;¶
}¶
}¶
```¶

```go Go¶
client := anthropic.NewClient()¶

knowledgeBaseTool := anthropic.ToolUnionParam{¶
OfTool: &anthropic.ToolParam{¶
Name: "search_knowledge_base",¶
Description: anthropic.String("Search the company knowledge base for information"),¶
InputSchema: anthropic.ToolInputSchemaParam{¶
Properties: map[string]any{¶
"query": map[string]any{¶
"type": "string",¶
"description": "The search query",¶
},¶
},¶
Required: []string{"query"},¶
},¶
},¶
}¶

// Build up the conversation in a slice, starting with the user's question¶
messages := []anthropic.MessageParam{¶
anthropic.NewUserMessage(anthropic.NewTextBlock("How do I configure the timeout settings?")),¶
}¶

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_8,¶
MaxTokens: 1024,¶
Tools: []anthropic.ToolUnionParam{knowledgeBaseTool},¶
ToolChoice: anthropic.ToolChoiceUnionParam{OfTool: &anthropic.ToolChoiceToolParam{Name: "search_knowledge_base"}},¶
Messages: messages,¶
})¶
if err != nil {¶
log.Fatal(err)¶
}¶

// The tool_use block is not always first: find it in the content list¶
var toolUse *anthropic.ToolUseBlock¶
for _, block := range response.Content {¶
if variant, ok := block.AsAny().(anthropic.ToolUseBlock); ok {¶
toolUse = &variant¶
break¶
}¶
}¶

if toolUse != nil {¶
var input struct {¶
Query string `json:"query"`¶
}¶
if err := json.Unmarshal(toolUse.Input, &input); err != nil {¶
log.Fatal(err)¶
}¶
toolResults := searchKnowledgeBase(input.Query)¶

// Append Claude's turn, then the tool result, to the running conversation¶
messages = append(messages, response.ToParam())¶
messages = append(messages, anthropic.NewUserMessage(anthropic.ContentBlockParamUnion{¶
OfToolResult: &anthropic.ToolResultBlockParam{¶
ToolUseID: toolUse.ID,¶
Content: toolResults,¶
},¶
}))¶

// Send the tool result back¶
finalResponse, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_8,¶
MaxTokens: 1024,¶
Messages: messages,¶
})¶
if err != nil {¶
log.Fatal(err)¶
}¶
fmt.Println(finalResponse)

}¶
// ...¶
func searchKnowledgeBase(query string) []anthropic.ToolResultBlockParamContentUnion {¶
return []anthropic.ToolResultBlockParamContentUnion{¶
{OfSearchResult: &anthropic.SearchResultBlockParam{¶
Content: []anthropic.TextBlockParam{¶
{Text: "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs."},¶
},¶
Source: "https://docs.company.com/product-guide",¶
Title: "Product Configuration Guide",¶
Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},¶
}},¶
{OfSearchResult: &anthropic.SearchResultBlockParam{¶
Content: []anthropic.TextBlockParam{¶
{Text: "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values."},¶
},¶
Source: "https://docs.company.com/troubleshooting",¶
Title: "Troubleshooting Guide",¶
Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},¶
}},¶
}¶
}¶
```¶

```java Java¶
import com.anthropic.models.messages.ContentBlockParam;¶
import com.anthropic.models.messages.CitationsConfigParam;¶
// ...¶
import com.anthropic.models.messages.
SearchResultBlockParamMessageParam;¶
import com.anthropic.models.messages.Model;¶
import com.anthropic.models.messages.SearchResultBlockParam;¶
import com.anthropic.models.messages.TextBlockParam;¶
import com.anthropic.models.messages.Tool;¶
import com.anthropic.models.messages.ToolChoice;¶
import com.anthropic.models.messages.ToolChoiceTool;¶
import com.anthropic.models.messages.ToolResultBlockParam;¶
import com.anthropic.models.messages.ToolUseBlockParam;¶
import com.anthropic.core.JsonValue
;¶
// ...¶

public class SearchKnowledgeBaseExample {¶
public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

Tool knowledgeBaseTool = Tool.builder()¶
.name("search_knowledge_base")¶
.description("Search the company knowledge base for information")¶
.inputSchema(Tool.InputSchema.builder()¶
.properties(JsonValue.from(Map.of(¶
"query", Map.of(¶
"type", "string",¶
"description", "The search query"¶
)¶
)))¶
.putAdditionalProperty("required", JsonValue.from(List.of("query")))¶
.build())¶
.build();¶

MessageCreateParams params = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_8)¶
.maxTokens(1024L)¶
.addTool(knowledgeBaseTool)¶
.addUserMessage("How do I configure the timeout settings?")¶
.build();¶

Message response = client.messages().create(params);¶

response.content().get(0).toolUse().ifPresent(toolUse -> {¶
List<ContentBlockParam> toolResult = searchKnowledgeBase(¶
(String) ((Map<?, ?>) toolUse._input()).get("query")¶
);¶

MessageCreateParams finalParams = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_8)¶
.maxTokens(1024L)¶
.addUserMessage("How do I configure the timeout settings?")¶
.addAssistantMessageOfBlockParams(List.of(¶
ContentBlockParam.ofToolUse(ToolUseBlockParam.builder()¶
.id(toolUse.id())¶
.name(toolUse.name())¶
.input(toolUse._input())¶
.build())¶
))¶
.addUserMessageOfBlockParams(List.of(¶
ContentBlockParam.ofToolResult(¶
ToolResultBlockParam.builder()¶
.toolUseId(toolUse.id())¶
.contentOfBlockParams(toolResult)¶
.build()¶
)¶
))¶
.build();¶

Message finalResponse = client.messages().create(finalParams);¶
System.out.println(finalResponse);¶
});¶
}¶

private static List<ContentBlockParam> searchKnowledgeBase(String query) {¶
return List.of(¶
ContentBlockParam
// Build up the conversation in a list, starting with the user's question¶
List<MessageParam> messages = new ArrayList<>();¶
messages.add(MessageParam.builder()¶
.role(MessageParam.Role.USER)¶
.content("How do I configure the timeout settings?")¶
.build());¶

MessageCreateParams params = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_8)¶
.maxTokens(1024L)¶
.addTool(knowledgeBaseTool)¶
.toolChoice(ToolChoice.ofTool(ToolChoiceTool.builder()¶
.name("search_knowledge_base")¶
.build()))¶
.messages(messages)¶
.build();¶

Message response = client.messages().create(params);¶

// The tool_use block is not always first: find it in the content list¶
response.content().stream()¶
.flatMap(contentBlock -> contentBlock.toolUse().stream())¶
.findFirst()¶
.ifPresent(toolUse -> {¶
Map<String, JsonValue> input =¶
(Map<String, JsonValue>) toolUse._input().asObject().get();¶
List<ToolResultBlockParam.Content.Block> toolResult = searchKnowledgeBase(¶
input.get("query").asStringOrThrow()¶
);¶

// Append Claude's turn, then the tool result, to the running conversation¶
messages.add(MessageParam.builder()¶
.role(MessageParam.Role.ASSISTANT)¶
.contentOfBlockParams(List.of(¶
ContentBlockParam.ofToolUse(ToolUseBlockParam.builder()¶
.id(toolUse.id())¶
.name(toolUse.name())¶
.input(toolUse._input())¶
.build())¶
))¶
.build());¶
messages.add(MessageParam.builder()¶
.role(MessageParam.Role.USER)¶
.contentOfBlockParams(List.of(¶
ContentBlockParam.ofToolResult(¶
ToolResultBlockParam.builder()¶
.toolUseId(toolUse.id())¶
.contentOfBlocks(toolResult)¶
.build()¶
)¶
))¶
.build());¶

// Send the tool result back¶
MessageCreateParams finalParams = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_8)¶
.maxTokens(1024L)¶
.messages(messages)¶
.build();¶

Message finalResponse = client.messages().create(finalParams);¶
System.out.println(finalResponse);¶
});¶
}¶

private static List<ToolResultBlockParam.Content.Block> searchKnowledgeBase(String query) {¶
return List.of(¶
ToolResultBlockParam.Content.Block
.ofSearchResult(¶
SearchResultBlockParam.builder()¶
.source("https://docs.company.com/product-guide")¶
.title("Product Configuration Guide")¶
.content(List.of(¶
TextBlockParam.builder()¶
.text("To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs.")¶
.build()¶
))¶
.citations(CitationsConfigParam.builder().enabled(true).build())¶
.build()¶
),¶
ContentBlockParamToolResultBlockParam.Content.Block.ofSearchResult(¶
SearchResultBlockParam.builder()¶
.source("https://docs.company.com/troubleshooting")¶
.title("Troubleshooting Guide")¶
.content(List.of(¶
TextBlockParam.builder()¶
.text("If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values.")¶
.build()¶
))¶
.citations(CitationsConfigParam.builder().enabled(true).build())¶
.build()¶
)¶
);¶
}¶
}¶
```¶

```php PHP¶
$client = new Client();¶

$knowledgeBaseTool = [¶
'name' => 'search_knowledge_base',¶
'description' => 'Search the company knowledge base for information',¶
'input_schema' => [¶
'type' => 'object',¶
'properties' => [¶
'query' => [¶
'type' => 'string',¶
'description' => 'The search query'¶
]¶
],¶
'required' => ['query']¶
]¶
];¶

function searchKnowledgeBase($query) {¶
return [¶
[¶
'type' => 'search_result',¶
'source' => 'https://docs.company.com/product-guide',¶
'title' => 'Product Configuration Guide',¶
'content' => [¶
[¶
'type' => 'text',¶
'text' => 'To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs.'¶
]¶
],¶
'citations' => ['enabled' => true]¶
],¶
[¶
'type' => 'search_result',¶
'source' => 'https://docs.company.com/troubleshooting',¶
'title' => 'Troubleshooting Guide',¶
'content' => [¶
[¶
'type' => 'text',¶
'text' => 'If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values.'¶
]¶
],¶
'citations' => ['enabled' => true]¶
]¶
];¶
}¶

$response = $client->messages->create(¶
maxTokens: 1024,¶
messages: [¶
['role' => 'user', 'content' => 'How do I configure the timeout settings?']¶
],¶
model: 'claude-opus-4-8',¶
tools: [$knowledgeBaseTool],¶
);¶

$toolUseBlock = null;¶
foreach ($response->content as $block) {¶
if ($block->type === 'tool_use') {¶
$toolUseBlock = $block;¶
break;¶
}¶
}¶

if ($toolUseBlock !== null) {¶
$toolResult = searchKnowledgeBase($toolUseBlock->input['query']);¶

$finalResponse = $client->messages->create(¶
maxTokens: 1024,¶
messages: [¶
['role' => 'user', 'content' => 'How do I configure the timeout settings?'],¶
['role' => 'assistant', 'content' => $response->content],¶
[¶
'role' => 'user',¶
'content' => [¶
[¶
'type' => 'tool_result',¶
'tool_use_id' => $toolUseBlock->id,¶
'content' => $toolResult¶
]¶
]¶
]¶
]
// Build up the conversation in a list, starting with the user's question¶
$messages = [¶
['role' => 'user', 'content' => 'How do I configure the timeout settings?']¶
];¶

$response = $client->messages->create(¶
maxTokens: 1024,¶
messages: $messages,¶
model: 'claude-opus-4-8',¶
toolChoice: ['type' => 'tool', 'name' => 'search_knowledge_base'],¶
tools: [$knowledgeBaseTool],¶
);¶

$toolUseBlock = null;¶
foreach ($response->content as $block) {¶
if ($block->type === 'tool_use') {¶
$toolUseBlock = $block;¶
break;¶
}¶
}¶

if ($toolUseBlock !== null) {¶
$toolResult = searchKnowledgeBase($toolUseBlock->input['query']);¶

// Append Claude's turn, then the tool result, to the running conversation¶
$messages[] = ['role' => 'assistant', 'content' => $response->content];¶
$messages[] = [¶
'role' => 'user',¶
'content' => [¶
[¶
'type' => 'tool_result',¶
'tool_use_id' => $toolUseBlock->id,¶
'content' => $toolResult¶
]¶
]¶
];¶

// Send the tool result back¶
$finalResponse = $client->messages->create(¶
maxTokens: 1024,¶
messages: $messages
,¶
model: 'claude-opus-4-8',¶
);¶
echo $finalResponse;¶
} else {¶
echo $response;¶
}¶
```¶

```ruby Ruby¶
client = Anthropic::Client.new¶

knowledge_base_tool = {¶
name: "search_knowledge_base",¶
description: "Search the company knowledge base for information",¶
input_schema: {¶
type: "object",¶
properties: {¶
query: { type: "string", description: "The search query" }¶
},¶
required: ["query"]¶
}¶
}¶

def search_knowledge_base(query)¶
[¶
{¶
type: "search_result",¶
source: "https://docs.company.com/product-guide",¶
title: "Product Configuration Guide",¶
content: [¶
{¶
type: "text",¶
text: "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs."¶
}¶
],¶
citations: { enabled: true }¶
},¶
{¶
type: "search_result",¶
source: "https://docs.company.com/troubleshooting",¶
title: "Troubleshooting Guide",¶
content: [¶
{¶
type: "text",¶
text: "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values."¶
}¶
],¶
citations: { enabled: true }¶
}¶
]¶
end¶

response = client.messages.create(¶
model: "claude-opus-4-8",¶
max_tokens: 1024,¶
tools: [knowledge_base_tool],¶
messages: [¶
{ role: "user", content: "How do I configure the timeout settings?" }¶
]¶
)¶

if response.content.first.type == :tool_use¶
tool_result = search_knowledge_base(response.content.first.input["query"])¶

final_response = client.messages.create(¶
model: "claude-opus-4-8",¶
max_tokens: 1024,¶
messages: [¶
{ role: "user", content: "How do I configure the timeout settings?" },¶
{ role: "assistant", content: response.content },¶
{¶
role: "user",¶
content: [¶
{¶
type: "tool_result",¶
tool_use_id: response.content.first.id,¶
content: tool_result¶
}¶
]¶
}¶
]
# Build up the conversation in a list, starting with the user's question¶
messages = [¶
{ role: "user", content: "How do I configure the timeout settings?" }¶
]¶

response = client.messages.create(¶
model: "claude-opus-4-8",¶
max_tokens: 1024,¶
tools: [knowledge_base_tool],¶
tool_choice: { type: "tool", name: "search_knowledge_base" },¶
messages: messages¶
)¶

# The tool_use block is not always first: find it in the content array¶
tool_use = response.content.find { |block| block.type == :tool_use }¶

if tool_use¶
tool_result = search_knowledge_base(tool_use.input[:query])¶

# Append Claude's turn, then the tool result, to the running conversation¶
messages << { role: "assistant", content: response.content }¶
messages << {¶
role: "user",¶
content: [¶
{¶
type: "tool_result",¶
tool_use_id: tool_use.id,¶
content: tool_result¶
}¶
]¶
}¶

# Send the tool result back¶
final_response = client.messages.create(¶
model: "claude-opus-4-8",¶
max_tokens: 1024,¶
messages: messages

)¶
puts final_response¶
end¶
```¶
</CodeGroup>¶

## Method 2: Search results as top-level content¶

You can also provide search results directly in user messages. This is useful for:¶

* Pre-fetched content from your search infrastructure¶
* Cached search results from previous queries¶
* Content from external search services¶
* Testing and development¶

### Example: Direct search results¶

<CodeGroup>¶
```bash cURL¶
curl https://api.anthropic.com/v1/messages \¶
-H "x-api-key: $ANTHROPIC_API_KEY" \¶
-H "anthropic-version: 2023-06-01" \¶
-H "content-type: application/json" \¶
-d '{¶
"model": "claude-opus-4-8",¶
"max_tokens": 1024,¶
"messages": [¶
{¶
"role": "user",¶
"content": [¶
{¶
"type": "search_result",¶
"source": "https://docs.company.com/api-reference",¶
"title": "API Reference - Authentication",¶
"content": [¶
{¶
"type": "text",¶
"text": "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium."¶
}¶
],¶
"citations": {¶
"enabled": true¶
}¶
},¶
{¶
"type": "search_result",¶
"source": "https://docs.company.com/quickstart",¶
"title": "Getting Started Guide",¶
"content": [¶
{¶
"type": "text",¶
"text": "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key."¶
}¶
],¶
"citations": {¶
"enabled": true¶
}¶
},¶
{¶
"type": "text",¶
"text": "Based on these search results, how do I authenticate API requests and what are the rate limits?"¶
}¶
]¶
}¶
]¶
}'¶
```¶

```bash CLI¶
ant messages create <<'YAML'¶
model: claude-opus-4-8¶
max_tokens: 1024¶
messages:¶
- role: user¶
content:¶
- type: search_result¶
source: https://docs.company.com/api-reference¶
title: API Reference - Authentication¶
content:¶
- type: text¶
text: >-¶
All API requests must include an API key in the Authorization¶
header. Keys can be generated from the dashboard. Rate limits:¶
1000 requests per hour for standard tier, 10000 for premium.¶
citations:¶
enabled: true¶
- type: search_result¶
source: https://docs.company.com/quickstart¶
title: Getting Started Guide¶
content:¶
- type: text¶
text: >-¶
To get started: 1) Sign up for an account, 2) Generate an API¶
key from the dashboard, 3) Install our SDK using pip install¶
company-sdk, 4) Initialize the client with your API key.¶
citations:¶
enabled: true¶
- type: text¶
text: >-¶
Based on these search results, how do I authenticate API requests¶
and what are the rate limits?¶
YAML¶
```¶

```python Python¶
from anthropic.types import MessageParam, TextBlockParam, SearchResultBlockParam¶

client = Anthropic()¶

# Provide search results directly in the user message¶
response = client.messages.create(¶
model="claude-opus-4-8",¶
max_tokens=1024,¶
messages=[¶
MessageParam(¶
role="user",¶
content=[¶
SearchResultBlockParam(¶
type="search_result",¶
source="https://docs.company.com/api-reference",¶
title="API Reference - Authentication",¶
content=[¶
TextBlockParam(¶
type="text",¶
text="All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium.",¶
)¶
],¶
citations={"enabled": True},¶
),¶
SearchResultBlockParam(¶
type="search_result",¶
source="https://docs.company.com/quickstart",¶
title="Getting Started Guide",¶
content=[¶
TextBlockParam(¶
type="text",¶
text="To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key.",¶
)¶
],¶
citations={"enabled": True},¶
),¶
TextBlockParam(¶
type="text",¶
text="Based on these search results, how do I authenticate API requests and what are the rate limits?",¶
),¶
],¶
)¶
],¶
)¶

print(response)¶
```¶

```typescript TypeScript¶
const anthropic = new Anthropic();¶

// Provide search results directly in the user message¶
const response = await anthropic.messages.create({¶
model: "claude-opus-4-8",¶
max_tokens: 1024,¶
messages: [¶
{¶
role: "user",¶
content: [¶
{¶
type: "search_result" as const,¶
source: "https://docs.company.com/api-reference",¶
title: "API Reference - Authentication",¶
content: [¶
{¶
type: "text" as const,¶
text: "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium."¶
}¶
],¶
citations: { enabled: true }¶
},¶
{¶
type: "search_result" as const,¶
source: "https://docs.company.com/quickstart",¶
title: "Getting Started Guide",¶
content: [¶
{¶
type: "text" as const,¶
text: "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key."¶
}¶
],¶
citations: { enabled: true }¶
},¶
{¶
type: "text" as const,¶
text: "Based on these search results, how do I authenticate API requests and what are the rate limits?"¶
}¶
]¶
}¶
]¶
});¶

console.log(response);¶
```¶

```csharp C#¶
AnthropicClient client = new();¶

var parameters = new MessageCreateParams// Provide search results directly in the user message¶
var response = await client.Messages.Create(new()

{¶
Model = Model.ClaudeOpus4_8,¶
MaxTokens = 1024,¶
Messages =¶
[¶
new()¶
{¶
Role = Role.User,¶
Content =
new MessageParamContent(
[¶
new
ContentBlockParam(new SearchResultBlockParam¶
{¶
Source = "https://docs.company.com/api-reference",¶
Title = "API Reference - Authentication",¶
Content =

[¶
new TextBlockParam¶
{¶
Text = "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium."¶
}¶
],¶
Citations = new CitationsConfigParam { Enabled = true }¶
},¶
new SearchResultBlockParam¶
{¶
Source = "https://docs.company.com/quickstart",¶
Title = "Getting Started Guide",¶
Content =¶
[¶
new TextBlockParam¶
{¶
Text = "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key."¶
}¶
],¶
Citations = new CitationsConfigParam { Enabled = true }¶
},¶
new TextBlockParam¶
{¶
Text = "Based on these search results, how do I authenticate API requests and what are the rate limits?"¶
}¶
]¶
}¶
]¶
};¶

var message = await client.Messages.Create(parameters);¶
Console.WriteLine(messag
[new() { Text = "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium." }],¶
Citations = new() { Enabled = true },¶
}),¶
new ContentBlockParam(new SearchResultBlockParam¶
{¶
Source = "https://docs.company.com/quickstart",¶
Title = "Getting Started Guide",¶
Content = [new() { Text = "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key." }],¶
Citations = new() { Enabled = true },¶
}),¶
new ContentBlockParam(new TextBlockParam { Text = "Based on these search results, how do I authenticate API requests and what are the rate limits?" }),¶
]),¶
},¶
],¶
});¶

Console.WriteLine(respons
e);¶
```¶

```go Go¶
client := anthropic.NewClient()¶

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_8,¶
MaxTokens: 1024,¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(¶
anthropic.ContentBlockParamUnion{OfSearchResult: &anthropic.SearchResultBlockParam{¶
Content: []anthropic.TextBlockParam{¶
{Text: "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium."},¶
},¶
Source: "https://docs.company.com/api-reference",¶
Title: "API Reference - Authentication",¶
Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},¶
}},¶
anthropic.ContentBlockParamUnion{OfSearchResult: &anthropic.SearchResultBlockParam{¶
Content: []anthropic.TextBlockParam{¶
{Text: "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key."},¶
},¶
Source: "https://docs.company.com/quickstart",¶
Title: "Getting Started Guide",¶
Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},¶
}},¶
anthropic.NewTextBlock("Based on these search results, how do I authenticate API requests and what are the rate limits?"),¶
),¶
},¶
})¶
if err != nil {¶
log.Fatal(err)¶
}¶
fmt.Println(response)¶
```¶

```java Java¶
import com.anthropic.models.messages.ContentBlockParam;¶
import com.anthropic.models.messages.CitationsConfigParam;¶
// ...¶
import com.anthropic.models.messages.SearchResultBlockParam;¶
import com.anthropic.models.messages.TextBlockParam;¶
// ...¶

public class SearchResultExample {¶
public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

MessageCreateParams params = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_8)¶
.maxTokens(1024L)¶
.addUserMessageOfBlockParams(List.of(¶
ContentBlockParam.ofSearchResult(¶
SearchResultBlockParam.builder()¶
.source("https://docs.company.com/api-reference")¶
.title("API Reference - Authentication")¶
.content(List.of(¶
TextBlockParam.builder()¶
.text("All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium.")¶
.build()¶
))¶
.citations(CitationsConfigParam.builder().enabled(true).build())¶
.build()¶
),¶
ContentBlockParam.ofSearchResult(¶
SearchResultBlockParam.builder()¶
.source("https://docs.company.com/quickstart")¶
.title("Getting Started Guide")¶
.content(List.of(¶
TextBlockParam.builder()¶
.text("To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key.")¶
.build()¶
))¶
.citations(CitationsConfigParam.builder().enabled(true).build())¶
.build()¶
),¶
ContentBlockParam.ofText(¶
TextBlockParam.builder()¶
.text("Based on these search results, how do I authenticate API requests and what are the rate limits?")¶
.build()¶
)¶
))¶
.build();¶

Message response = client.messages().create(params);¶
System.out.println(response);¶
}¶
}¶
```¶

```php PHP¶
$client = new Client();¶

$message = $client->messages->create(¶
maxTokens: 1024,¶
messages: [¶
[¶
'role' => 'user',¶
'content' => [¶
[¶
'type' => 'search_result',¶
'source' => 'https://docs.company.com/api-reference',¶
'title' => 'API Reference - Authentication',¶
'content' => [¶
[¶
'type' => 'text',¶
'text' => 'All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium.'¶
]¶
],¶
'citations' => ['enabled' => true]¶
],¶
[¶
'type' => 'search_result',¶
'source' => 'https://docs.company.com/quickstart',¶
'title' => 'Getting Started Guide',¶
'content' => [¶
[¶
'type' => 'text',¶
'text' => 'To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key.'¶
]¶
],¶
'citations' => ['enabled' => true]¶
],¶
[¶
'type' => 'text',¶
'text' => 'Based on these search results, how do I authenticate API requests and what are the rate limits?'¶
]¶
]¶
]¶
],¶
model: 'claude-opus-4-8',¶
);¶

echo json_encode($message, JSON_PRETTY_PRINT);¶
```¶

```ruby Ruby¶
client = Anthropic::Client.new¶

message = client.messages.create(¶
model: "claude-opus-4-8",¶
max_tokens: 1024,¶
messages: [¶
{¶
role: "user",¶
content: [¶
{¶
type: "search_result",¶
source: "https://docs.company.com/api-reference",¶
title: "API Reference - Authentication",¶
content: [¶
{¶
type: "text",¶
text: "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium."¶
}¶
],¶
citations: { enabled: true }¶
},¶
{¶
type: "search_result",¶
source: "https://docs.company.com/quickstart",¶
title: "Getting Started Guide",¶
content: [¶
{¶
type: "text",¶
text: "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key."¶
}¶
],¶
citations: { enabled: true }¶
},¶
{¶
type: "text",¶
text: "Based on these search results, how do I authenticate API requests and what are the rate limits?"¶
}¶
]¶
}¶
]¶
)¶

puts message¶
```¶
</CodeGroup>¶

## Claude's response with citations¶

Regardless of how search results are provided, Claude automatically includes citations when using information from them:¶

```json¶
{¶
"role": "assistant",¶
"content": [¶
{¶
"type": "text",¶
"text": "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard.",¶
"citations": [¶
{¶
"type": "search_result_location",¶
"cited_text": "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium.",¶
"source": "https://docs.company.com/api-reference",¶
"title": "API Reference - Authentication",¶
"search_result_index": 0,¶
"start_block_index": 0,¶
"end_block_index": 1¶
}¶
]¶
},¶
{¶
"type": "text",¶
"text": "\n\nTo set this up from scratch, you'll need to "¶
},¶
{¶
"type": "text",¶
"text": "sign up for an account, generate an API key from the dashboard, install the SDK using `pip install company-sdk`, and initialize the client with your API key.",¶
"citations": [¶
{¶
"type": "search_result_location",¶
"cited_text": "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key.",¶
"source": "https://docs.company.com/quickstart",¶
"title": "Getting Started Guide",¶
"search_result_index": 1,¶
"start_block_index": 0,¶
"end_block_index": 1¶
}¶
]¶
}¶
]¶
}¶
```¶

### Citation fields¶

Each citation includes:¶

| Field | Type | Description |¶
| --------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |¶
| `type` | string | Always `"search_result_location"` for search result citations |¶
| `source` | string | The source from the original search result |¶
| `title` | string or null | The title from the original search result |¶
| `cited_text` | string | The full text of the cited block(s), concatenated. Equals the contents of `content[start_block_index:end_block_index]` joined together. Not counted toward output tokens. |¶
| `search_result_index` | integer | 0-based index of the cited search result among all `search_result` blocks in the request, in the order they appear (across all messages and tool results). |¶
| `start_block_index` | integer | 0-based index of the first cited block in the search result's `content` array. |¶
| `end_block_index` | integer | Exclusive end index of the cited block range in the search result's `content` array. Always greater than `start_block_index`. |¶

The block indices identify a slice of the search result's `content` array, and `cited_text` is the full text of that slice. The text block is the minimal citable unit: Claude cites whole blocks, not substrings within a block. To get finer-grained citations, split your search result content into smaller blocks (see [Multiple content blocks](#multiple-content-blocks)).¶

## Multiple content blocks¶

Search results can contain multiple text blocks in the `content` array:¶

```json¶
{¶
"type": "search_result",¶
"source": "https://docs.company.com/api-guide",¶
"title": "API Documentation",¶
"content": [¶
{¶
"type": "text",¶
"text": "Authentication: All API requests require an API key."¶
},¶
{¶
"type": "text",¶
"text": "Rate Limits: The API allows 1000 requests per hour per key."¶
},¶
{¶
"type": "text",¶
"text": "Error Handling: The API returns standard HTTP status codes."¶
}¶
]
,¶
"citations": { "enabled": true }

}¶
```¶

A citation referencing the rate limits block looks like:¶

```json¶
{¶
"type": "search_result_location",¶
"cited_text": "Rate Limits: The API allows 1000 requests per hour per key.",¶
"source": "https://docs.company.com/api-guide",¶
"title": "API Documentation",¶
"search_result_index": 0,¶
"start_block_index": 1,¶
"end_block_index": 2¶
}¶
```¶

When this search result is cited, `start_block_index` and `end_block_index` identify which of these blocks the citation covers, and `cited_text` contains exactly those blocks' text. Splitting content into smaller, focused blocks gives Claude finer citation boundaries; combining content into one block means every citation returns the full text. This is the same model used by [custom content documents](/docs/en/build-with-claude/citations#custom-content-documents) in the Citations feature.¶

## Advanced usage¶

### Combining both methods¶

You can
use both tool-based and top-level search results in the same conversation:¶

```python¶
from anthropic.types import MessageParam, SearchResultBlockParam, TextBlockParam¶

# First message with top-level search results¶
messages = [¶
MessageParam(¶
role="user",¶
content=[¶
SearchResultBlockParam(¶
type="search_result",¶
source="https://docs.company.com/overview",¶
title="Product Overview",¶
content=[¶
TextBlockParam(¶
type="text", text="Our product helps teams collaborate..."¶
)¶
],¶
citations={"enabled": True},¶
),¶
TextBlockParam(¶
type="text",¶
text="Tell me about this product and search for pricing information",¶
),¶
],¶
)¶
]¶

# Claude might respond and call a tool to search for pricing¶
# Then you provide tool results with more search results¶
```¶

### Combining with other content types¶

Both methods support mixing search results with other content:¶

```python¶
from anthropic.types import SearchResultBlockParam, TextBlockParam¶

# In tool results¶
tool_result = [¶
SearchResultBlockParam(¶
type="search_result",¶
source="https://docs.company.com/guide",¶
title="User Guide",¶
content=[TextBlockParam(type="text", text="Configuration details...")],¶
citations={"enabled": True},¶
),¶
TextBlockParam(¶
type="text", text="Additional context: This applies to version 2.0 and later."¶
),¶
]¶

# In top-level content¶
user_content = [¶
SearchResultBlockParam(¶
type="search_result",¶
source="https://research.com/paper",¶
title="Research Paper",¶
content=[TextBlockParam(type="text", text="Key findings...")],¶
citations={"enabled": True},¶
),¶
{¶
"type": "image",¶
"source": {"type": "url", "url": "https://example.com/chart.png"},¶
},¶
TextBlockParam(¶
type="text", text="How does the chart relate to the research findings?"¶
),¶
]¶
```¶

### Cache control¶

Add cache control for better performance:¶

```json¶
{¶
"type": "search_result",¶
"source": "https://docs.company.com/guide",¶
"title": "User Guide",¶
"content": [{ "type": "text", "text": "..." }],¶
"cache_control": {¶
"type": "ephemeral"¶
}¶
}¶
```
mix both methods in the same conversation. Claude cites from either source, and `search_result_index` counts all `search_result` blocks in request order, regardless of source.¶

The following example replays a complete conversation. The first user message carries a pre-fetched search result, the assistant turn calls a knowledge base tool, and the tool result returns a second search result. Claude's answer cites both sources:¶

<CodeGroup>¶
```bash cURL¶
curl https://api.anthropic.com/v1/messages \¶
-H "x-api-key: $ANTHROPIC_API_KEY" \¶
-H "anthropic-version: 2023-06-01" \¶
-H "content-type: application/json" \¶
-d '{¶
"model": "claude-opus-4-8",¶
"max_tokens": 1024,¶
"tools": [¶
{¶
"name": "search_knowledge_base",¶
"description": "Search the company knowledge base for information",¶
"input_schema": {¶
"type": "object",¶
"properties": {¶
"query": {"type": "string", "description": "The search query"}¶
},¶
"required": ["query"]¶
}¶
}¶
],¶
"messages": [¶
{¶
"role": "user",¶
"content": [¶
{¶
"type": "search_result",¶
"source": "https://docs.company.com/overview",¶
"title": "Product Overview",¶
"content": [¶
{¶
"type": "text",¶
"text": "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."¶
}¶
],¶
"citations": {"enabled": true}¶
},¶
{¶
"type": "text",¶
"text": "What does Acme Dashboard do, and what plans is it available on?"¶
}¶
]¶
},¶
{¶
"role": "assistant",¶
"content": [¶
{¶
"type": "text",¶
"text": "Let me check the pricing information."¶
},¶
{¶
"type": "tool_use",¶
"id": "toolu_01A09q90qw90lq917835lq9",¶
"name": "search_knowledge_base",¶
"input": {"query": "Acme Dashboard pricing plans"}¶
}¶
]¶
},¶
{¶
"role": "user",¶
"content": [¶
{¶
"type": "tool_result",¶
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",¶
"content": [¶
{¶
"type": "search_result",¶
"source": "https://docs.company.com/pricing",¶
"title": "Pricing Plans",¶
"content": [¶
{¶
"type": "text",¶
"text": "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."¶
}¶
],¶
"citations": {"enabled": true}¶
}¶
]¶
}¶
]¶
}¶
]¶
}'¶
```¶

```bash CLI¶
ant messages create <<'YAML'¶
model: claude-opus-4-8¶
max_tokens: 1024¶
tools:¶
- name: search_knowledge_base¶
description: Search the company knowledge base for information¶
input_schema:¶
type: object¶
properties:¶
query:¶
type: string¶
description: The search query¶
required: [query]¶
messages:¶
- role: user¶
content:¶
- type: search_result¶
source: https://docs.company.com/overview¶
title: Product Overview¶
content:¶
- type: text¶
text: >-¶
Acme Dashboard is a monitoring tool for distributed systems.¶
It supports real-time alerting and custom metric dashboards.¶
citations:¶
enabled: true¶
- type: text¶
text: What does Acme Dashboard do, and what plans is it available on?¶
- role: assistant¶
content:¶
- type: text¶
text: Let me check the pricing information.¶
- type: tool_use¶
id: toolu_01A09q90qw90lq917835lq9¶
name: search_knowledge_base¶
input:¶
query: Acme Dashboard pricing plans¶
- role: user¶
content:¶
- type: tool_result¶
tool_use_id: toolu_01A09q90qw90lq917835lq9¶
content:¶
- type: search_result¶
source: https://docs.company.com/pricing¶
title: Pricing Plans¶
content:¶
- type: text¶
text: >-¶
Acme Dashboard is available on the Starter plan at $10 per¶
user per month and the Enterprise plan with custom pricing.¶
citations:¶
enabled: true¶
YAML¶
```¶

```python Python¶
from anthropic.types import (¶
MessageParam,¶
SearchResultBlockParam,¶
TextBlockParam,¶
ToolResultBlockParam,¶
ToolUseBlockParam,¶
)¶

client = Anthropic()¶

knowledge_base_tool = {¶
"name": "search_knowledge_base",¶
"description": "Search the company knowledge base for information",¶
"input_schema": {¶
"type": "object",¶
"properties": {"query": {"type": "string", "description": "The search query"}},¶
"required": ["query"],¶
},¶
}¶

# Replay a conversation that provides search results both ways: the first¶
# user message carries a pre-fetched result, the tool result returns another¶
response = client.messages.create(¶
model="claude-opus-4-8",¶
max_tokens=1024,¶
tools=[knowledge_base_tool],¶
messages=[¶
MessageParam(¶
role="user",¶
content=[¶
SearchResultBlockParam(¶
type="search_result",¶
source="https://docs.company.com/overview",¶
title="Product Overview",¶
content=[¶
TextBlockParam(¶
type="text",¶
text="Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.",¶
)¶
],¶
citations={"enabled": True},¶
),¶
TextBlockParam(¶
type="text",¶
text="What does Acme Dashboard do, and what plans is it available on?",¶
),¶
],¶
),¶
MessageParam(¶
role="assistant",¶
content=[¶
TextBlockParam(¶
type="text", text="Let me check the pricing information."¶
),¶
ToolUseBlockParam(¶
type="tool_use",¶
id="toolu_01A09q90qw90lq917835lq9",¶
name="search_knowledge_base",¶
input={"query": "Acme Dashboard pricing plans"},¶
),¶
],¶
),¶
MessageParam(¶
role="user",¶
content=[¶
ToolResultBlockParam(¶
type="tool_result",¶
tool_use_id="toolu_01A09q90qw90lq917835lq9",¶
content=[¶
SearchResultBlockParam(¶
type="search_result",¶
source="https://docs.company.com/pricing",¶
title="Pricing Plans",¶
content=[¶
TextBlockParam(¶
type="text",¶
text="Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.",¶
)¶
],¶
citations={"enabled": True},¶
)¶
],¶
)¶
],¶
),¶
],¶
)¶

print(response)¶
```¶

```typescript TypeScript¶
const anthropic = new Anthropic();¶

const knowledgeBaseTool: Anthropic.Tool = {¶
name: "search_knowledge_base",¶
description: "Search the company knowledge base for information",¶
input_schema: {¶
type: "object" as const,¶
properties: {¶
query: { type: "string", description: "The search query" }¶
},¶
required: ["query"]¶
}¶
};¶

// Replay a conversation that provides search results both ways: the first¶
// user message carries a pre-fetched result, the tool result returns another¶
const response = await anthropic.messages.create({¶
model: "claude-opus-4-8",¶
max_tokens: 1024,¶
tools: [knowledgeBaseTool],¶
messages: [¶
{¶
role: "user",¶
content: [¶
{¶
type: "search_result" as const,¶
source: "https://docs.company.com/overview",¶
title: "Product Overview",¶
content: [¶
{¶
type: "text" as const,¶
text: "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."¶
}¶
],¶
citations: { enabled: true }¶
},¶
{¶
type: "text" as const,¶
text: "What does Acme Dashboard do, and what plans is it available on?"¶
}¶
]¶
},¶
{¶
role: "assistant",¶
content: [¶
{ type: "text" as const, text: "Let me check the pricing information." },¶
{¶
type: "tool_use" as const,¶
id: "toolu_01A09q90qw90lq917835lq9",¶
name: "search_knowledge_base",¶
input: { query: "Acme Dashboard pricing plans" }¶
}¶
]¶
},¶
{¶
role: "user",¶
content: [¶
{¶
type: "tool_result" as const,¶
tool_use_id: "toolu_01A09q90qw90lq917835lq9",¶
content: [¶
{¶
type: "search_result" as const,¶
source: "https://docs.company.com/pricing",¶
title: "Pricing Plans",¶
content: [¶
{¶
type: "text" as const,¶
text: "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."¶
}¶
],¶
citations: { enabled: true }¶
}¶
]¶
}¶
]¶
}¶
]¶
});¶

console.log(response);¶
```¶

```csharp C#¶
AnthropicClient client = new();¶

// Replay a conversation that provides search results both ways: the first¶
// user message carries a pre-fetched result, the tool result returns another¶
var response = await client.Messages.Create(new()¶
{¶
Model = Model.ClaudeOpus4_8,¶
MaxTokens = 1024,¶
Tools =¶
[¶
new ToolUnion(new Tool()¶
{¶
Name = "search_knowledge_base",¶
Description = "Search the company knowledge base for information",¶
InputSchema = new InputSchema()¶
{¶
Properties = new Dictionary<string, JsonElement>¶
{¶
["query"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The search query" }),¶
},¶
Required = ["query"],¶
},¶
}),¶
],¶
Messages =¶
[¶
new()¶
{¶
Role = Role.User,¶
Content = new MessageParamContent(¶
[¶
new ContentBlockParam(new SearchResultBlockParam¶
{¶
Source = "https://docs.company.com/overview",¶
Title = "Product Overview",¶
Content = [new() { Text = "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards." }],¶
Citations = new() { Enabled = true },¶
}),¶
new ContentBlockParam(new TextBlockParam { Text = "What does Acme Dashboard do, and what plans is it available on?" }),¶
]),¶
},¶
new()¶
{¶
Role = Role.Assistant,¶
Content = new MessageParamContent(¶
[¶
new ContentBlockParam(new TextBlockParam { Text = "Let me check the pricing information." }),¶
new ContentBlockParam(new ToolUseBlockParam¶
{¶
ID = "toolu_01A09q90qw90lq917835lq9",¶
Name = "search_knowledge_base",¶
Input = new Dictionary<string, JsonElement>¶
{¶
["query"] = JsonSerializer.SerializeToElement("Acme Dashboard pricing plans"),¶
},¶
}),¶
]),¶
},¶
new()¶
{¶
Role = Role.User,¶
Content = new MessageParamContent(¶
[¶
new ContentBlockParam(new ToolResultBlockParam()¶
{¶
ToolUseID = "toolu_01A09q90qw90lq917835lq9",¶
Content = new ToolResultBlockParamContent(¶
[¶
new SearchResultBlockParam¶
{¶
Source = "https://docs.company.com/pricing",¶
Title = "Pricing Plans",¶
Content = [new() { Text = "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing." }],¶
Citations = new() { Enabled = true },¶
},¶
]),¶
}),¶
]),¶
},¶
],¶
});¶

Console.WriteLine(response);¶
```¶

```go Go¶
client := anthropic.NewClient()¶

knowledgeBaseTool := anthropic.ToolUnionParam{¶
OfTool: &anthropic.ToolParam{¶
Name: "search_knowledge_base",¶
Description: anthropic.String("Search the company knowledge base for information"),¶
InputSchema: anthropic.ToolInputSchemaParam{¶
Properties: map[string]any{¶
"query": map[string]any{"type": "string", "description": "The search query"},¶
},¶
Required: []string{"query"},¶
},¶
},¶
}¶

// Replay a conversation that provides search results both ways: the first¶
// user message carries a pre-fetched result, the tool result returns another¶
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_8,¶
MaxTokens: 1024,¶
Tools: []anthropic.ToolUnionParam{knowledgeBaseTool},¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(¶
anthropic.ContentBlockParamUnion{OfSearchResult: &anthropic.SearchResultBlockParam{¶
Content: []anthropic.TextBlockParam{¶
{Text: "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."},¶
},¶
Source: "https://docs.company.com/overview",¶
Title: "Product Overview",¶
Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},¶
}},¶
anthropic.NewTextBlock("What does Acme Dashboard do, and what plans is it available on?"),¶
),¶
anthropic.NewAssistantMessage(¶
anthropic.NewTextBlock("Let me check the pricing information."),¶
anthropic.ContentBlockParamUnion{OfToolUse: &anthropic.ToolUseBlockParam{¶
ID: "toolu_01A09q90qw90lq917835lq9",¶
Name: "search_knowledge_base",¶
Input: map[string]any{"query": "Acme Dashboard pricing plans"},¶
}},¶
),¶
anthropic.NewUserMessage(¶
anthropic.ContentBlockParamUnion{OfToolResult: &anthropic.ToolResultBlockParam{¶
ToolUseID: "toolu_01A09q90qw90lq917835lq9",¶
Content: []anthropic.ToolResultBlockParamContentUnion{¶
{OfSearchResult: &anthropic.SearchResultBlockParam{¶
Content: []anthropic.TextBlockParam{¶
{Text: "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."},¶
},¶
Source: "https://docs.company.com/pricing",¶
Title: "Pricing Plans",¶
Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},¶
}},¶
},¶
}},¶
),¶
},¶
})¶
if err != nil {¶
log.Fatal(err)¶
}¶
fmt.Println(response)¶
```¶

```java Java¶
import com.anthropic.core.JsonValue;¶
import com.anthropic.models.messages.CitationsConfigParam;¶
import com.anthropic.models.messages.ContentBlockParam;¶
// ...¶
import com.anthropic.models.messages.SearchResultBlockParam;¶
import com.anthropic.models.messages.TextBlockParam;¶
import com.anthropic.models.messages.Tool;¶
import com.anthropic.models.messages.ToolResultBlockParam;¶
import com.anthropic.models.messages.ToolUseBlockParam;¶
// ...¶

public class CombinedSearchResultsExample {¶
public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

Tool knowledgeBaseTool = Tool.builder()¶
.name("search_knowledge_base")¶
.description("Search the company knowledge base for information")¶
.inputSchema(Tool.InputSchema.builder()¶
.properties(JsonValue.from(Map.of(¶
"query", Map.of("type", "string", "description", "The search query")¶
)))¶
.putAdditionalProperty("required", JsonValue.from(List.of("query")))¶
.build())¶
.build();¶

// Replay a conversation that provides search results both ways: the first¶
// user message carries a pre-fetched result, the tool result returns another¶
MessageCreateParams params = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_8)¶
.maxTokens(1024L)¶
.addTool(knowledgeBaseTool)¶
.addUserMessageOfBlockParams(List.of(¶
ContentBlockParam.ofSearchResult(SearchResultBlockParam.builder()¶
.source("https://docs.company.com/overview")¶
.title("Product Overview")¶
.content(List.of(TextBlockParam.builder()¶
.text("Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.")¶
.build()))¶
.citations(CitationsConfigParam.builder().enabled(true).build())¶
.build()),¶
ContentBlockParam.ofText(TextBlockParam.builder()¶
.text("What does Acme Dashboard do, and what plans is it available on?")¶
.build())¶
))¶
.addAssistantMessageOfBlockParams(List.of(¶
ContentBlockParam.ofText(TextBlockParam.builder()¶
.text("Let me check the pricing information.")¶
.build()),¶
ContentBlockParam.ofToolUse(ToolUseBlockParam.builder()¶
.id("toolu_01A09q90qw90lq917835lq9")¶
.name("search_knowledge_base")¶
.input(JsonValue.from(Map.of("query", "Acme Dashboard pricing plans")))¶
.build())¶
))¶
.addUserMessageOfBlockParams(List.of(¶
ContentBlockParam.ofToolResult(ToolResultBlockParam.builder()¶
.toolUseId("toolu_01A09q90qw90lq917835lq9")¶
.contentOfBlocks(List.of(¶
ToolResultBlockParam.Content.Block.ofSearchResult(SearchResultBlockParam.builder()¶
.source("https://docs.company.com/pricing")¶
.title("Pricing Plans")¶
.content(List.of(TextBlockParam.builder()¶
.text("Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.")¶
.build()))¶
.citations(CitationsConfigParam.builder().enabled(true).build())¶
.build())¶
))¶
.build())¶
))¶
.build();¶

Message response = client.messages().create(params);¶
System.out.println(response);¶
}¶
}¶
```¶

```php PHP¶
$client = new Client();¶

$knowledgeBaseTool = [¶
'name' => 'search_knowledge_base',¶
'description' => 'Search the company knowledge base for information',¶
'input_schema' => [¶
'type' => 'object',¶
'properties' => [¶
'query' => ['type' => 'string', 'description' => 'The search query']¶
],¶
'required' => ['query']¶
]¶
];¶

// Replay a conversation that provides search results both ways: the first¶
// user message carries a pre-fetched result, the tool result returns another¶
$response = $client->messages->create(¶
maxTokens: 1024,¶
tools: [$knowledgeBaseTool],¶
messages: [¶
[¶
'role' => 'user',¶
'content' => [¶
[¶
'type' => 'search_result',¶
'source' => 'https://docs.company.com/overview',¶
'title' => 'Product Overview',¶
'content' => [¶
[¶
'type' => 'text',¶
'text' => 'Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.'¶
]¶
],¶
'citations' => ['enabled' => true]¶
],¶
[¶
'type' => 'text',¶
'text' => 'What does Acme Dashboard do, and what plans is it available on?'¶
]¶
]¶
],¶
[¶
'role' => 'assistant',¶
'content' => [¶
['type' => 'text', 'text' => 'Let me check the pricing information.'],¶
[¶
'type' => 'tool_use',¶
'id' => 'toolu_01A09q90qw90lq917835lq9',¶
'name' => 'search_knowledge_base',¶
'input' => ['query' => 'Acme Dashboard pricing plans']¶
]¶
]¶
],¶
[¶
'role' => 'user',¶
'content' => [¶
[¶
'type' => 'tool_result',¶
'tool_use_id' => 'toolu_01A09q90qw90lq917835lq9',¶
'content' => [¶
[¶
'type' => 'search_result',¶
'source' => 'https://docs.company.com/pricing',¶
'title' => 'Pricing Plans',¶
'content' => [¶
[¶
'type' => 'text',¶
'text' => 'Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.'¶
]¶
],¶
'citations' => ['enabled' => true]¶
]¶
]¶
]¶
]¶
]¶
],¶
model: 'claude-opus-4-8',¶
);¶

echo json_encode($response, JSON_PRETTY_PRINT);¶
```¶

```ruby Ruby¶
client = Anthropic::Client.new¶

knowledge_base_tool = {¶
name: "search_knowledge_base",¶
description: "Search the company knowledge base for information",¶
input_schema: {¶
type: "object",¶
properties: {¶
query: { type: "string", description: "The search query" }¶
},¶
required: ["query"]¶
}¶
}¶

# Replay a conversation that provides search results both ways: the first¶
# user message carries a pre-fetched result, the tool result returns another¶
response = client.messages.create(¶
model: "claude-opus-4-8",¶
max_tokens: 1024,¶
tools: [knowledge_base_tool],¶
messages: [¶
{¶
role: "user",¶
content: [¶
{¶
type: "search_result",¶
source: "https://docs.company.com/overview",¶
title: "Product Overview",¶
content: [¶
{¶
type: "text",¶
text: "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."¶
}¶
],¶
citations: { enabled: true }¶
},¶
{¶
type: "text",¶
text: "What does Acme Dashboard do, and what plans is it available on?"¶
}¶
]¶
},¶
{¶
role: "assistant",¶
content: [¶
{ type: "text", text: "Let me check the pricing information." },¶
{¶
type: "tool_use",¶
id: "toolu_01A09q90qw90lq917835lq9",¶
name: "search_knowledge_base",¶
input: { query: "Acme Dashboard pricing plans" }¶
}¶
]¶
},¶
{¶
role: "user",¶
content: [¶
{¶
type: "tool_result",¶
tool_use_id: "toolu_01A09q90qw90lq917835lq9",¶
content: [¶
{¶
type: "search_result",¶
source: "https://docs.company.com/pricing",¶
title: "Pricing Plans",¶
content: [¶
{¶
type: "text",¶
text: "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."¶
}¶
],¶
citations: { enabled: true }¶
}¶
]¶
}¶
]¶
}¶
]¶
)¶

puts response¶
```¶
</CodeGroup>¶

The response cites both sources. The pre-fetched result is `search_result_index: 0` and the tool-returned result is `search_result_index: 1`, matching the order the `search_result` blocks appear in the conversation:¶

```json¶
{¶
"role": "assistant",¶
"content": [¶
{¶
"type": "text",¶
"text": "Here's what I found about Acme Dashboard:\n\n**What it does:** "¶
},¶
{¶
"type": "text",¶
"text": "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.",¶
"citations": [¶
{¶
"type": "search_result_location",¶
"cited_text": "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.",¶
"source": "https://docs.company.com/overview",¶
"title": "Product Overview",¶
"search_result_index": 0,¶
"start_block_index": 0,¶
"end_block_index": 1¶
}¶
]¶
},¶
{¶
"type": "text",¶
"text": "\n\n**Available plans:** "¶
},¶
{¶
"type": "text",¶
"text": "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.",¶
"citations": [¶
{¶
"type": "search_result_location",¶
"cited_text": "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.",¶
"source": "https://docs.company.com/pricing",¶
"title": "Pricing Plans",¶
"search_result_index": 1,¶
"start_block_index": 0,¶
"end_block_index": 1¶
}¶
]¶
}¶
]¶
}¶
```¶

### Mixing with other content types¶

In user messages, `search_result` blocks can sit alongside any other content block. The Method 2 example pairs search results with a `text` question, and image or document blocks can join them the same way.¶

Tool results are stricter: if any block in a `tool_result` content array is a `search_result`, all of its blocks must be `search_result`. Mixing search results with other block types in the same tool result returns a validation error. To return supporting text alongside tool-sourced search results, include it as a text block inside one of the search results' `content` arrays, where it also becomes citable.¶

### Cache control¶

Add `cache_control` on the search result block to cache it for reuse across requests. It sits alongside `citations` on the same block:¶

```json¶
{¶
"type": "search_result",¶
"source": "https://docs.company.com/guide",¶
"title": "User Guide",¶
"content": [{ "type": "text", "text": "..." }],¶
"citations": { "enabled": true },¶
"cache_control": { "type": "ephemeral" }¶
}¶
```¶

See [Prompt caching](/docs/en/build-with-claude/prompt-caching) for minimum cacheable lengths and other requirements.


### Citation control¶

By default, citations are disabled for search results. You can enable citations by explicitly setting the `citations` configuration:¶

```json¶
{¶
"type": "search_result",¶
"source": "https://docs.company.com/guide",¶
"title": "User Guide",¶
"content": [{ "type": "text", "text": "Important documentation..." }],¶
"citations": {¶
"enabled": true // Enable citations for this result¶
}¶
}¶
```¶

When `citations.enabled` is set to `true`, Claude
includes citation references when using information from the search result. This enables:¶

* Natural citations for your custom RAG applications¶
* Source attribution when interfacing with proprietary knowledge bases¶
* Web search-quality citations for any custom tool that returns search results
attaches citation references to the text blocks that draw on the search result.

<Warning>¶
Citations are all-or-nothing: either all search results in a request must have citations enabled, or all must have them disabled. Mixing search results with different citation settings results in an error.¶
</Warning>¶

## Best practices¶

### For tool-based search (Method 1)¶

* **Dynamic content:** Use for real-time searches and dynamic RAG applications¶
* **Error handling:** Return appropriate messages when searches fail¶
* **Result limits:** Return only the most relevant results to avoid context overflow¶

### For top-level search (Method 2)¶

* **Pre-fetched content:** Use when you already have search results¶
* **Batch processing:** Ideal for processing multiple search results at once¶
* **Testing:** Great for testing citation behavior with known content¶

### General best practices¶

1. **Structure results effectively:**¶

* Use clear, permanent source URLs¶
* Provide descriptive titles¶
* Break long content into logical text blocks to give Claude finer citation boundaries¶

2. **Maintain consistency:**¶

* Use consistent source formats across your application¶
* Ensure titles accurately reflect content¶
* Keep formatting consistent¶

3. **Handle errors gracefully:**


```python¶
def search_with_fallback(query):¶
try:¶
results = perform_search(query)¶
if not results:¶
return {"type": "text", "text": "No results found."}¶
return format_as_search_results(results)¶
except Exception as e:¶
return {"type": "text", "text": f"Search error: {str(e)}"}¶
```¶

## Limitations¶

* Search result content blocks are available on Claude API, Amazon Bedrock, and Google Cloud.¶
* Only text content is supported within search results (no images or other media).¶
* The `content` array must contain at least one text block.¶

## Next steps¶

<CardGroup cols={2}>¶
<Card title="Citations" icon="book" href="/docs/en/build-with-claude/citations">¶
Learn how citations work across documents, custom content, and search results.¶
</Card>¶

<Card title="Web search tool" icon="magnifying-glass" href="/docs/en/agents-and-tools/tool-use/web-search-tool">¶
Let Claude search the web and cite sources automatically using a server tool
when a search fails or returns nothing, return a plain text block describing the outcome (for example, `{"type": "text", "text": "No results found."}`) instead of raising an error: Claude explains the empty result to the user, and the conversation continues.¶

## Limitations¶

* Search result content blocks are available on Claude API, Amazon Bedrock, and Google Cloud.¶
* Only text content is supported within search results (no images or other media).¶
* `search_result` blocks can only appear in user messages (including inside tool results). Assistant messages with search results are rejected.¶
* When the [web search tool](/docs/en/agents-and-tools/tool-use/web-search-tool) is enabled in the same request, citations must be enabled on all `search_result` blocks.¶

## Next steps¶

<CardGroup cols={2}>¶
<Card title="Streaming refusals" icon="lock" href="/docs/en/test-and-evaluate/strengthen-guardrails/handle-streaming-refusals">¶
Detect and handle refusal stop reasons in streaming responses, and retry refused requests on a fallback model.¶
</Card>¶

<Card title="Citations" icon="book" href="/docs/en/build-with-claude/citations">¶
Ground Claude's responses in your source documents. Citations return the exact passages that support each claim, so you can verify answers and surface sources to your users.¶
</Card>¶

<Card title="Web search tool" icon="browser" href="/docs/en/agents-and-tools/tool-use/web-search-tool">¶
Give Claude access to current web content with cited sources, optional dynamic filtering, and domain controls
.¶
</Card>¶

<Card title="Messages API reference" icon="code" href="/docs/en/api/messages/create">¶
See the complete Messages API documentation, including content block types.¶
</Card>¶

<Card title="Prompt caching" icon="database" href="/docs/en/build-with-claude/prompt-caching">¶
Cache search results with `cache_control` to reduce cost and latency on repeated requests.¶
</Card>¶
</CardGroup>¶

Unified Diff

--- a/build-with-claude/search-results.md
+++ b/build-with-claude/search-results.md
@@ -8,30 +8,9 @@
   For how zero data retention (ZDR) applies to this feature, see [API and data retention](/docs/en/manage-claude/api-and-data-retention).
 </Note>
 
-Search result content blocks enable natural citations with proper source attribution, bringing web search-quality citations to your custom applications. This feature is particularly powerful for RAG (Retrieval-Augmented Generation) applications where you need Claude to cite sources accurately.
-
-The search results feature is available on the following models:
-
-* Claude Opus 4.8 (claude-opus-4-8)
-* Claude Opus 4.7 (`claude-opus-4-7`)
-* Claude Opus 4.6 (`claude-opus-4-6`)
-* Claude Sonnet 5 (`claude-sonnet-5`)
-* Claude Sonnet 4.6 (`claude-sonnet-4-6`)
-* Claude Sonnet 4.5 (`claude-sonnet-4-5-20250929`)
-* Claude Opus 4.5 (`claude-opus-4-5-20251101`)
-* Claude Opus 4.1 ([deprecated](/docs/en/about-claude/model-deprecations)) (`claude-opus-4-1-20250805`)
-* Claude Opus 4 ([retired, except on Google Cloud](/docs/en/about-claude/model-deprecations)) (`claude-opus-4-20250514`)
-* Claude Sonnet 4 ([retired, except on Bedrock and Google Cloud](/docs/en/about-claude/model-deprecations)) (`claude-sonnet-4-20250514`)
-* Claude Haiku 4.5 (`claude-haiku-4-5-20251001`)
-* Claude Haiku 3.5 ([retired, except on Bedrock and Google Cloud](/docs/en/about-claude/model-deprecations)) (`claude-3-5-haiku-20241022`)
-
-## Key benefits
-
-* **Natural citations:** Achieve the same citation quality as web search for any content
-* **Flexible integration:** Use in tool returns for dynamic RAG or as top-level content for pre-fetched data
-* **Proper source attribution:** Each result includes source and title information for clear attribution
-* **No document workarounds needed:** Eliminates the need for document-based workarounds
-* **Consistent citation format:** Matches the citation quality and format of Claude's web search functionality
+Search result content blocks let Claude cite your own content the same way it cites web search results: each citation carries the source and title you provided. Use them in RAG (Retrieval-Augmented Generation) applications where Claude needs to attribute answers to your documents.
+
+All [active models](/docs/en/about-claude/models/overview) support search results with citations, with the exception of Claude Haiku 3. No beta header is required: search results are part of the standard Messages API.
 
 ## How it works
 
@@ -40,7 +19,7 @@
 1. **From tool calls:** Your custom tools return search results, enabling dynamic RAG applications
 2. **As top-level content:** You provide search results directly in user messages for pre-fetched or cached content
 
-In both cases, Claude can automatically cite information from the search results with proper source attribution.
+In both cases, Claude cites the search results automatically when citations are enabled. No special prompting is needed: ask your question, and citations appear on the text blocks that draw on your content.
 
 ### Search result schema
 
@@ -67,32 +46,48 @@
 
 ### Required fields
 
-| Field     | Type   | Description                                           |
-| --------- | ------ | ----------------------------------------------------- |
-| `type`    | string | Must be `"search_result"`                             |
-| `source`  | string | The source URL or identifier for the content          |
-| `title`   | string | A descriptive title for the search result             |
-| `content` | array  | An array of text blocks containing the actual content |
+| Field     | Type   | Description                                                                                                      |
+| --------- | ------ | ---------------------------------------------------------------------------------------------------------------- |
+| `type`    | string | Must be `"search_result"`                                                                                        |
+| `source`  | string | The source of the content. Any stable string works: a URL, or an internal identifier such as `kb://article-1234` |
+| `title`   | string | A descriptive title for the search result                                                                        |
+| `content` | array  | An array of text blocks containing the actual content                                                            |
 
 ### Optional fields
 
-| Field           | Type   | Description                                                   |
-| --------------- | ------ | ------------------------------------------------------------- |
-| `citations`     | object | Citation configuration with `enabled` Boolean field           |
-| `cache_control` | object | Cache control settings (for example, `{"type": "ephemeral"}`) |
+| Field           | Type   | Description                                                                                                                                                                                                                                                 |
+| --------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `citations`     | object | Citation configuration with `enabled` Boolean field. Citations are disabled by default; every example on this page sets `"enabled": true` explicitly. All search results in a request must use the same setting (see [Citation control](#citation-control)) |
+| `cache_control` | object | Cache control settings (for example, `{"type": "ephemeral"}`)                                                                                                                                                                                               |
 
 Each item in the `content` array must be a text block with:
 
 * `type`: Must be `"text"`
 * `text`: The actual text content (non-empty string)
 
+Search results hold text only. Images and other media are not supported inside the `content` array.
+
 ## Method 1: Search results from tool calls
 
-The most powerful use case is returning search results from your custom tools. This enables dynamic RAG applications where tools fetch and return relevant content with automatic citations.
+Returning search results from your custom tools enables dynamic RAG applications: tools fetch content at runtime, and Claude cites it in the response. The following example forces the tool call with [`tool_choice`](/docs/en/agents-and-tools/tool-use/define-tools#forcing-tool-use), so the retrieval step runs every time.
 
 ### Example: Knowledge base tool
 
-<CodeGroup exclude="shell">
+<CodeGroup>
+  ```bash cURL
+  # The tool-calling flow needs application-side search logic that doesn't
+  # translate to a one-off shell command. See the SDK tabs for the full flow.
+  # The raw shape of a tool conversation with search results is shown in the
+  # Combining both methods cURL tab; Method 2 shows the top-level shape.
+  ```
+
+  ```bash CLI
+  # The tool-calling flow needs application-side search logic that doesn't
+  # translate to a one-off shell command. See the SDK tabs for the full flow.
+  # The raw shape of a tool conversation with search results is shown in the
+  # Combining both methods cURL tab; Method 2 shows the top-level shape.
+  ```
+
   ```python Python
   from anthropic.types import (
       MessageParam,
@@ -147,48 +142,55 @@
       ]
 
 
+  # Build up the conversation in a list, starting with the user's question
+  messages = [
+      MessageParam(role="user", content="How do I configure the timeout settings?")
+  ]
+
   # Create a message with the tool
   response = client.messages.create(
-      model="claude-opus-4-8",  # Works with all supported models
+      model="claude-opus-4-8",
       max_tokens=1024,
       tools=[knowledge_base_tool],
-      messages=[
-          MessageParam(role="user", content="How do I configure the timeout settings?")
-      ],
+      tool_choice={"type": "tool", "name": "search_knowledge_base"},
+      messages=messages,
   )
 
-  # When Claude calls the tool, provide the search results
-  if response.content[0].type == "tool_use":
-      tool_result = search_knowledge_base(response.content[0].input["query"])
+  # When Claude calls the tool, provide the search results.
+  # The tool_use block is not always first: iterate to find it.
+  tool_use = next((block for block in response.content if block.type == "tool_use"), None)
+  if tool_use is not None:
+      tool_result = search_knowledge_base(tool_use.input["query"])
+
+      # Append Claude's turn, then the tool result, to the running conversation
+      messages.append(MessageParam(role="assistant", content=response.content))
+      messages.append(
+          MessageParam(
+              role="user",
+              content=[
+                  ToolResultBlockParam(
+                      type="tool_result",
+                      tool_use_id=tool_use.id,
+                      content=tool_result,  # Search results go here
+                  )
+              ],
+          )
+      )
 
       # Send the tool result back
       final_response = client.messages.create(
-          model="claude-opus-4-8",  # Works with all supported models
+          model="claude-opus-4-8",
           max_tokens=1024,
-          messages=[
-              MessageParam(
-                  role="user", content="How do I configure the timeout settings?"
-              ),
-              MessageParam(role="assistant", content=response.content),
-              MessageParam(
-                  role="user",
-                  content=[
-                      ToolResultBlockParam(
-                          type="tool_result",
-                          tool_use_id=response.content[0].id,
-                          content=tool_result,  # Search results go here
-                      )
-                  ],
-              ),
-          ],
+          messages=messages,
       )
+      print(final_response)
   ```
 
   ```typescript TypeScript
   const anthropic = new Anthropic();
 
   // Define a knowledge base search tool
-  const knowledgeBaseTool: Anthropic.Messages.Tool = {
+  const knowledgeBaseTool: Anthropic.Tool = {
     name: "search_knowledge_base",
     description: "Search the company knowledge base for information",
     input_schema: {
@@ -235,157 +237,137 @@
     ];
   }
 
+  // Build up the conversation in a list, starting with the user's question
+  const messages: Anthropic.MessageParam[] = [
+    { role: "user", content: "How do I configure the timeout settings?" }
+  ];
+
   // Create a message with the tool
   const response = await anthropic.messages.create({
-    model: "claude-opus-4-8", // Works with all supported models
+    model: "claude-opus-4-8",
     max_tokens: 1024,
     tools: [knowledgeBaseTool],
-    messages: [
-      {
-        role: "user",
-        content: "How do I configure the timeout settings?"
-      }
-    ]
+    tool_choice: { type: "tool", name: "search_knowledge_base" },
+    messages
   });
 
-  // Handle tool use and provide results
-  if (response.content[0].type === "tool_use") {
-    const input = response.content[0].input as { query: string };
+  // Handle tool use and provide results.
+  // The tool_use block is not always first: find it in the content array.
+  const toolUse = response.content.find(
+    (block): block is Anthropic.ToolUseBlock => block.type === "tool_use"
+  );
+  if (toolUse) {
+    const input = toolUse.input as { query: string };
     const toolResult = searchKnowledgeBase(input.query);
 
-    const finalResponse = await anthropic.messages.create({
-      model: "claude-opus-4-8", // Works with all supported models
-      max_tokens: 1024,
-      messages: [
-        { role: "user", content: "How do I configure the timeout settings?" },
-        { role: "assistant", content: response.content },
+    // Append Claude's turn, then the tool result, to the running conversation
+    messages.push({ role: "assistant", content: response.content });
+    messages.push({
+      role: "user",
+      content: [
         {
-          role: "user",
-          content: [
-            {
-              type: "tool_result" as const,
-              tool_use_id: response.content[0].id,
-              content: toolResult // Search results go here
-            }
-          ]
+          type: "tool_result" as const,
+          tool_use_id: toolUse.id,
+          content: toolResult // Search results go here
         }
       ]
     });
+
+    // Send the tool result back
+    const finalResponse = await anthropic.messages.create({
+      model: "claude-opus-4-8",
+      max_tokens: 1024,
+      messages
+    });
+    console.log(finalResponse);
   }
   ```
 
   ```csharp C#
-  using System;
-  using System.Collections.Generic;
-  using System.Threading.Tasks;
-  using Anthropic;
-  using Anthropic.Models.Messages;
-
-  public class Program
+  AnthropicClient client = new();
+
+  var tools = new List<ToolUnion>
   {
-      public static async Task Main(string[] args)
+      new ToolUnion(new Tool()
       {
-          AnthropicClient client = new();
-
-          var knowledgeBaseTool = new Tool
-          {
-              Name = "search_knowledge_base",
-              Description = "Search the company knowledge base for information",
-              InputSchema = new
+          Name = "search_knowledge_base",
+          Description = "Search the company knowledge base for information",
+          InputSchema = new InputSchema()
+          {
+              Properties = new Dictionary<string, JsonElement>
               {
-                  type = "object",
-                  properties = new
-                  {
-                      query = new
-                      {
-                          type = "string",
-                          description = "The search query"
-                      }
-                  },
-                  required = new[] { "query" }
-              }
-          };
-
-          var parameters = new MessageCreateParams
+                  ["query"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The search query" }),
+              },
+              Required = ["query"],
+          },
+      }),
+  };
+
+  // Function to handle the tool call
+  static List<Block> SearchKnowledgeBase(string query)
+  {
+      // Your search logic here
+      // Returns search results in the correct format
+      return
+      [
+          new SearchResultBlockParam
+          {
+              Source = "https://docs.company.com/product-guide",
+              Title = "Product Configuration Guide",
+              Content = [new() { Text = "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs." }],
+              Citations = new() { Enabled = true },
+          },
+          new SearchResultBlockParam
+          {
+              Source = "https://docs.company.com/troubleshooting",
+              Title = "Troubleshooting Guide",
+              Content = [new() { Text = "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values." }],
+              Citations = new() { Enabled = true },
+          },
+      ];
+  }
+
+  // Build up the conversation in a list, starting with the user's question
+  List<MessageParam> messages = [new() { Role = Role.User, Content = "How do I configure the timeout settings?" }];
+
+  // Create a message with the tool
+  var response = await client.Messages.Create(new()
+  {
+      Model = Model.ClaudeOpus4_8,
+      MaxTokens = 1024,
+      Tools = tools,
+      ToolChoice = new ToolChoiceTool { Name = "search_knowledge_base" },
+      Messages = messages,
+  });
+
+  // When Claude calls the tool, provide the search results.
+  // The tool_use block is not always first: find the first one.
+  foreach (var block in response.Content)
+  {
+      if (block.TryPickToolUse(out var toolUse))
+      {
+          var query = toolUse.Input["query"].GetString() ?? "";
+          var toolResults = SearchKnowledgeBase(query);
+
+          // Append Claude's turn, then the tool result, to the running conversation
+          messages.Add(new() { Role = Role.Assistant, Content = response.Content.Select(contentBlock => new ContentBlockParam(contentBlock.Json)).ToList() });
+          messages.Add(new()
+          {
+              Role = Role.User,
+              Content = new MessageParamContent(
+                  [new ContentBlockParam(new ToolResultBlockParam() { ToolUseID = toolUse.ID, Content = new ToolResultBlockParamContent(toolResults) })]
+              ),
+          });
+
+          // Send the tool result back
+          var finalResponse = await client.Messages.Create(new()
           {
               Model = Model.ClaudeOpus4_8,
               MaxTokens = 1024,
-              Tools = new[] { knowledgeBaseTool },
-              Messages = new[]
-              {
-                  new MessageParam
-                  {
-                      Role = Role.User,
-                      Content = "How do I configure the timeout settings?"
-                  }
-              }
-          };
-
-          var response = await client.Messages.Create(parameters);
-
-          if (response.Content[0] is ToolUseBlock toolUse)
-          {
-              var toolResult = SearchKnowledgeBase(toolUse.Input["query"].ToString());
-
-              var finalParameters = new MessageCreateParams
-              {
-                  Model = Model.ClaudeOpus4_8,
-                  MaxTokens = 1024,
-                  Messages = new[]
-                  {
-                      new MessageParam { Role = Role.User, Content = "How do I configure the timeout settings?" },
-                      new MessageParam { Role = Role.Assistant, Content = response.Content },
-                      new MessageParam
-                      {
-                          Role = Role.User,
-                          Content = new[]
-                          {
-                              new ToolResultBlockParam
-                              {
-                                  ToolUseID = toolUse.Id,
-                                  Content = toolResult
-                              }
-                          }
-                      }
-                  }
-              };
-
-              var finalResponse = await client.Messages.Create(finalParameters);
-              Console.WriteLine(finalResponse);
-          }
-      }
-
-      private static List<SearchResultBlockParam> SearchKnowledgeBase(string query)
-      {
-          return new List<SearchResultBlockParam>
-          {
-              new SearchResultBlockParam
-              {
-                  Source = "https://docs.company.com/product-guide",
-                  Title = "Product Configuration Guide",
-                  Content = new[]
-                  {
-                      new TextBlockParam
-                      {
-                          Text = "To configure the product, navigate to Settings > Configuration. The default timeout is 30 seconds, but can be adjusted between 10-120 seconds based on your needs."
-                      }
-                  },
-                  Citations = new CitationsConfigParam { Enabled = true }
-              },
-              new SearchResultBlockParam
-              {
-                  Source = "https://docs.company.com/troubleshooting",
-                  Title = "Troubleshooting Guide",
-                  Content = new[]
-                  {
-                      new TextBlockParam
-                      {
-                          Text = "If you encounter timeout errors, first check the configuration settings. Common causes include network latency and incorrect timeout values."
-                      }
-                  },
-                  Citations = new CitationsConfigParam { Enabled = true }
-              }
-          };
+              Messages = messages,
+          });
+          Console.WriteLine(finalResponse);
+          break;
       }
   }
   ```
@@ -409,51 +391,59 @@
   		},
   	}
 
+  	// Build up the conversation in a slice, starting with the user's question
+  	messages := []anthropic.MessageParam{
+  		anthropic.NewUserMessage(anthropic.NewTextBlock("How do I configure the timeout settings?")),
+  	}
+
   	response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
-  		Model:     anthropic.ModelClaudeOpus4_8,
-  		MaxTokens: 1024,
-  		Tools:     []anthropic.ToolUnionParam{knowledgeBaseTool},
-  		Messages: []anthropic.MessageParam{
-  			anthropic.NewUserMessage(anthropic.NewTextBlock("How do I configure the timeout settings?")),
-  		},
+  		Model:      anthropic.ModelClaudeOpus4_8,
+  		MaxTokens:  1024,
+  		Tools:      []anthropic.ToolUnionParam{knowledgeBaseTool},
+  		ToolChoice: anthropic.ToolChoiceUnionParam{OfTool: &anthropic.ToolChoiceToolParam{Name: "search_knowledge_base"}},
+  		Messages:   messages,
   	})
   	if err != nil {
   		log.Fatal(err)
   	}
 
+  	// The tool_use block is not always first: find it in the content list
+  	var toolUse *anthropic.ToolUseBlock
   	for _, block := range response.Content {
-  		switch variant := block.AsAny().(type) {
-  		case anthropic.ToolUseBlock:
-  			var input struct {
-  				Query string `json:"query"`
-  			}
-  			if err := json.Unmarshal(variant.Input, &input); err != nil {
-  				log.Fatal(err)
-  			}
-  			toolResults := searchKnowledgeBase(input.Query)
-
-  			// Build assistant message from the response
-  			assistantParam := response.ToParam()
-
-  			finalResponse, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
-  				Model:     anthropic.ModelClaudeOpus4_8,
-  				MaxTokens: 1024,
-  				Messages: []anthropic.MessageParam{
-  					anthropic.NewUserMessage(anthropic.NewTextBlock("How do I configure the timeout settings?")),
-  					assistantParam,
-  					anthropic.NewUserMessage(anthropic.ContentBlockParamUnion{
-  						OfToolResult: &anthropic.ToolResultBlockParam{
-  							ToolUseID: variant.ID,
-  							Content:   toolResults,
-  						},
-  					}),
-  				},
-  			})
-  			if err != nil {
-  				log.Fatal(err)
-  			}
-  			fmt.Println(finalResponse)
+  		if variant, ok := block.AsAny().(anthropic.ToolUseBlock); ok {
+  			toolUse = &variant
+  			break
   		}
+  	}
+
+  	if toolUse != nil {
+  		var input struct {
+  			Query string `json:"query"`
+  		}
+  		if err := json.Unmarshal(toolUse.Input, &input); err != nil {
+  			log.Fatal(err)
+  		}
+  		toolResults := searchKnowledgeBase(input.Query)
+
+  		// Append Claude's turn, then the tool result, to the running conversation
+  		messages = append(messages, response.ToParam())
+  		messages = append(messages, anthropic.NewUserMessage(anthropic.ContentBlockParamUnion{
+  			OfToolResult: &anthropic.ToolResultBlockParam{
+  				ToolUseID: toolUse.ID,
+  				Content:   toolResults,
+  			},
+  		}))
+
+  		// Send the tool result back
+  		finalResponse, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
+  			Model:     anthropic.ModelClaudeOpus4_8,
+  			MaxTokens: 1024,
+  			Messages:  messages,
+  		})
+  		if err != nil {
+  			log.Fatal(err)
+  		}
+  		fmt.Println(finalResponse)
   	}
   // ...
   func searchKnowledgeBase(query string) []anthropic.ToolResultBlockParamContentUnion {
@@ -479,10 +469,21 @@
   ```
 
   ```java Java
+  import com.anthropic.models.messages.ContentBlockParam;
   import com.anthropic.models.messages.CitationsConfigParam;
   // ...
+  import com.anthropic.models.messages.MessageParam;
+  import com.anthropic.models.messages.Model;
   import com.anthropic.models.messages.SearchResultBlockParam;
+  import com.anthropic.models.messages.TextBlockParam;
+  import com.anthropic.models.messages.Tool;
+  import com.anthropic.models.messages.ToolChoice;
+  import com.anthropic.models.messages.ToolChoiceTool;
+  import com.anthropic.models.messages.ToolResultBlockParam;
+  import com.anthropic.models.messages.ToolUseBlockParam;
+  import com.anthropic.core.JsonValue;
   // ...
+
   public class SearchKnowledgeBaseExample {
       public static void main(String[] args) {
           AnthropicClient client = AnthropicOkHttpClient.fromEnv();
@@ -501,49 +502,74 @@
                   .build())
               .build();
 
+          // Build up the conversation in a list, starting with the user's question
+          List<MessageParam> messages = new ArrayList<>();
+          messages.add(MessageParam.builder()
+              .role(MessageParam.Role.USER)
+              .content("How do I configure the timeout settings?")
+              .build());
+
           MessageCreateParams params = MessageCreateParams.builder()
               .model(Model.CLAUDE_OPUS_4_8)
               .maxTokens(1024L)
               .addTool(knowledgeBaseTool)
-              .addUserMessage("How do I configure the timeout settings?")
+              .toolChoice(ToolChoice.ofTool(ToolChoiceTool.builder()
+                  .name("search_knowledge_base")
+                  .build()))
+              .messages(messages)
               .build();
 
           Message response = client.messages().create(params);
 
-          response.content().get(0).toolUse().ifPresent(toolUse -> {
-              List<ContentBlockParam> toolResult = searchKnowledgeBase(
-                  (String) ((Map<?, ?>) toolUse._input()).get("query")
-              );
-
-              MessageCreateParams finalParams = MessageCreateParams.builder()
-                  .model(Model.CLAUDE_OPUS_4_8)
-                  .maxTokens(1024L)
-                  .addUserMessage("How do I configure the timeout settings?")
-                  .addAssistantMessageOfBlockParams(List.of(
-                      ContentBlockParam.ofToolUse(ToolUseBlockParam.builder()
-                          .id(toolUse.id())
-                          .name(toolUse.name())
-                          .input(toolUse._input())
-                          .build())
-                  ))
-                  .addUserMessageOfBlockParams(List.of(
-                      ContentBlockParam.ofToolResult(
-                          ToolResultBlockParam.builder()
-                              .toolUseId(toolUse.id())
-                              .contentOfBlockParams(toolResult)
-                              .build()
-                      )
-                  ))
-                  .build();
-
-              Message finalResponse = client.messages().create(finalParams);
-              System.out.println(finalResponse);
-          });
+          // The tool_use block is not always first: find it in the content list
+          response.content().stream()
+              .flatMap(contentBlock -> contentBlock.toolUse().stream())
+              .findFirst()
+              .ifPresent(toolUse -> {
+                  Map<String, JsonValue> input =
+                      (Map<String, JsonValue>) toolUse._input().asObject().get();
+                  List<ToolResultBlockParam.Content.Block> toolResult = searchKnowledgeBase(
+                      input.get("query").asStringOrThrow()
+                  );
+
+                  // Append Claude's turn, then the tool result, to the running conversation
+                  messages.add(MessageParam.builder()
+                      .role(MessageParam.Role.ASSISTANT)
+                      .contentOfBlockParams(List.of(
+                          ContentBlockParam.ofToolUse(ToolUseBlockParam.builder()
+                              .id(toolUse.id())
+                              .name(toolUse.name())
+                              .input(toolUse._input())
+                              .build())
+                      ))
+                      .build());
+                  messages.add(MessageParam.builder()
+                      .role(MessageParam.Role.USER)
+                      .contentOfBlockParams(List.of(
+                          ContentBlockParam.ofToolResult(
+                              ToolResultBlockParam.builder()
+                                  .toolUseId(toolUse.id())
+                                  .contentOfBlocks(toolResult)
+                                  .build()
+                          )
+                      ))
+                      .build());
+
+                  // Send the tool result back
+                  MessageCreateParams finalParams = MessageCreateParams.builder()
+                      .model(Model.CLAUDE_OPUS_4_8)
+                      .maxTokens(1024L)
+                      .messages(messages)
+                      .build();
+
+                  Message finalResponse = client.messages().create(finalParams);
+                  System.out.println(finalResponse);
+              });
       }
 
-      private static List<ContentBlockParam> searchKnowledgeBase(String query) {
+      private static List<ToolResultBlockParam.Content.Block> searchKnowledgeBase(String query) {
           return List.of(
-              ContentBlockParam.ofSearchResult(
+              ToolResultBlockParam.Content.Block.ofSearchResult(
                   SearchResultBlockParam.builder()
                       .source("https://docs.company.com/product-guide")
                       .title("Product Configuration Guide")
@@ -555,7 +581,7 @@
                       .citations(CitationsConfigParam.builder().enabled(true).build())
                       .build()
               ),
-              ContentBlockParam.ofSearchResult(
+              ToolResultBlockParam.Content.Block.ofSearchResult(
                   SearchResultBlockParam.builder()
                       .source("https://docs.company.com/troubleshooting")
                       .title("Troubleshooting Guide")
@@ -619,12 +645,16 @@
       ];
   }
 
+  // Build up the conversation in a list, starting with the user's question
+  $messages = [
+      ['role' => 'user', 'content' => 'How do I configure the timeout settings?']
+  ];
+
   $response = $client->messages->create(
       maxTokens: 1024,
-      messages: [
-          ['role' => 'user', 'content' => 'How do I configure the timeout settings?']
-      ],
+      messages: $messages,
       model: 'claude-opus-4-8',
+      toolChoice: ['type' => 'tool', 'name' => 'search_knowledge_base'],
       tools: [$knowledgeBaseTool],
   );
 
@@ -639,22 +669,23 @@
   if ($toolUseBlock !== null) {
       $toolResult = searchKnowledgeBase($toolUseBlock->input['query']);
 
+      // Append Claude's turn, then the tool result, to the running conversation
+      $messages[] = ['role' => 'assistant', 'content' => $response->content];
+      $messages[] = [
+          'role' => 'user',
+          'content' => [
+              [
+                  'type' => 'tool_result',
+                  'tool_use_id' => $toolUseBlock->id,
+                  'content' => $toolResult
+              ]
+          ]
+      ];
+
+      // Send the tool result back
       $finalResponse = $client->messages->create(
           maxTokens: 1024,
-          messages: [
-              ['role' => 'user', 'content' => 'How do I configure the timeout settings?'],
-              ['role' => 'assistant', 'content' => $response->content],
-              [
-                  'role' => 'user',
-                  'content' => [
-                      [
-                          'type' => 'tool_result',
-                          'tool_use_id' => $toolUseBlock->id,
-                          'content' => $toolResult
-                      ]
-                  ]
-              ]
-          ],
+          messages: $messages,
           model: 'claude-opus-4-8',
       );
       echo $finalResponse;
@@ -707,35 +738,43 @@
     ]
   end
 
+  # Build up the conversation in a list, starting with the user's question
+  messages = [
+    { role: "user", content: "How do I configure the timeout settings?" }
+  ]
+
   response = client.messages.create(
     model: "claude-opus-4-8",
     max_tokens: 1024,
     tools: [knowledge_base_tool],
-    messages: [
-      { role: "user", content: "How do I configure the timeout settings?" }
-    ]
+    tool_choice: { type: "tool", name: "search_knowledge_base" },
+    messages: messages
   )
 
-  if response.content.first.type == :tool_use
-    tool_result = search_knowledge_base(response.content.first.input["query"])
-
+  # The tool_use block is not always first: find it in the content array
+  tool_use = response.content.find { |block| block.type == :tool_use }
+
+  if tool_use
+    tool_result = search_knowledge_base(tool_use.input[:query])
+
+    # Append Claude's turn, then the tool result, to the running conversation
+    messages << { role: "assistant", content: response.content }
+    messages << {
+      role: "user",
+      content: [
+        {
+          type: "tool_result",
+          tool_use_id: tool_use.id,
+          content: tool_result
+        }
+      ]
+    }
+
+    # Send the tool result back
     final_response = client.messages.create(
       model: "claude-opus-4-8",
       max_tokens: 1024,
-      messages: [
-        { role: "user", content: "How do I configure the timeout settings?" },
-        { role: "assistant", content: response.content },
-        {
-          role: "user",
-          content: [
-            {
-              type: "tool_result",
-              tool_use_id: response.content.first.id,
-              content: tool_result
-            }
-          ]
-        }
-      ]
+      messages: messages
     )
     puts final_response
   end
@@ -939,7 +978,8 @@
   ```csharp C#
   AnthropicClient client = new();
 
-  var parameters = new MessageCreateParams
+  // Provide search results directly in the user message
+  var response = await client.Messages.Create(new()
   {
       Model = Model.ClaudeOpus4_8,
       MaxTokens = 1024,
@@ -948,45 +988,29 @@
           new()
           {
               Role = Role.User,
-              Content =
+              Content = new MessageParamContent(
               [
-                  new SearchResultBlockParam
+                  new ContentBlockParam(new SearchResultBlockParam
                   {
                       Source = "https://docs.company.com/api-reference",
                       Title = "API Reference - Authentication",
-                      Content =
-                      [
-                          new TextBlockParam
-                          {
-                              Text = "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium."
-                          }
-                      ],
-                      Citations = new CitationsConfigParam { Enabled = true }
-                  },
-                  new SearchResultBlockParam
+                      Content = [new() { Text = "All API requests must include an API key in the Authorization header. Keys can be generated from the dashboard. Rate limits: 1000 requests per hour for standard tier, 10000 for premium." }],
+                      Citations = new() { Enabled = true },
+                  }),
+                  new ContentBlockParam(new SearchResultBlockParam
                   {
                       Source = "https://docs.company.com/quickstart",
                       Title = "Getting Started Guide",
-                      Content =
-                      [
-                          new TextBlockParam
-                          {
-                              Text = "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key."
-                          }
-                      ],
-                      Citations = new CitationsConfigParam { Enabled = true }
-                  },
-                  new TextBlockParam
-                  {
-                      Text = "Based on these search results, how do I authenticate API requests and what are the rate limits?"
-                  }
-              ]
-          }
-      ]
-  };
-
-  var message = await client.Messages.Create(parameters);
-  Console.WriteLine(message);
+                      Content = [new() { Text = "To get started: 1) Sign up for an account, 2) Generate an API key from the dashboard, 3) Install our SDK using pip install company-sdk, 4) Initialize the client with your API key." }],
+                      Citations = new() { Enabled = true },
+                  }),
+                  new ContentBlockParam(new TextBlockParam { Text = "Based on these search results, how do I authenticate API requests and what are the rate limits?" }),
+              ]),
+          },
+      ],
+  });
+
+  Console.WriteLine(response);
   ```
 
   ```go Go
@@ -1024,10 +1048,13 @@
   ```
 
   ```java Java
+  import com.anthropic.models.messages.ContentBlockParam;
   import com.anthropic.models.messages.CitationsConfigParam;
   // ...
   import com.anthropic.models.messages.SearchResultBlockParam;
+  import com.anthropic.models.messages.TextBlockParam;
   // ...
+
   public class SearchResultExample {
       public static void main(String[] args) {
           AnthropicClient client = AnthropicOkHttpClient.fromEnv();
@@ -1251,7 +1278,8 @@
       "type": "text",
       "text": "Error Handling: The API returns standard HTTP status codes."
     }
-  ]
+  ],
+  "citations": { "enabled": true }
 }
 ```
 
@@ -1275,82 +1303,784 @@
 
 ### Combining both methods
 
-You can use both tool-based and top-level search results in the same conversation:
-
-```python
-from anthropic.types import MessageParam, SearchResultBlockParam, TextBlockParam
-
-# First message with top-level search results
-messages = [
-    MessageParam(
-        role="user",
-        content=[
-            SearchResultBlockParam(
-                type="search_result",
-                source="https://docs.company.com/overview",
-                title="Product Overview",
-                content=[
-                    TextBlockParam(
-                        type="text", text="Our product helps teams collaborate..."
-                    )
+You can mix both methods in the same conversation. Claude cites from either source, and `search_result_index` counts all `search_result` blocks in request order, regardless of source.
+
+The following example replays a complete conversation. The first user message carries a pre-fetched search result, the assistant turn calls a knowledge base tool, and the tool result returns a second search result. Claude's answer cites both sources:
+
+<CodeGroup>
+  ```bash cURL
+  curl https://api.anthropic.com/v1/messages \
+    -H "x-api-key: $ANTHROPIC_API_KEY" \
+    -H "anthropic-version: 2023-06-01" \
+    -H "content-type: application/json" \
+    -d '{
+      "model": "claude-opus-4-8",
+      "max_tokens": 1024,
+      "tools": [
+        {
+          "name": "search_knowledge_base",
+          "description": "Search the company knowledge base for information",
+          "input_schema": {
+            "type": "object",
+            "properties": {
+              "query": {"type": "string", "description": "The search query"}
+            },
+            "required": ["query"]
+          }
+        }
+      ],
+      "messages": [
+        {
+          "role": "user",
+          "content": [
+            {
+              "type": "search_result",
+              "source": "https://docs.company.com/overview",
+              "title": "Product Overview",
+              "content": [
+                {
+                  "type": "text",
+                  "text": "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."
+                }
+              ],
+              "citations": {"enabled": true}
+            },
+            {
+              "type": "text",
+              "text": "What does Acme Dashboard do, and what plans is it available on?"
+            }
+          ]
+        },
+        {
+          "role": "assistant",
+          "content": [
+            {
+              "type": "text",
+              "text": "Let me check the pricing information."
+            },
+            {
+              "type": "tool_use",
+              "id": "toolu_01A09q90qw90lq917835lq9",
+              "name": "search_knowledge_base",
+              "input": {"query": "Acme Dashboard pricing plans"}
+            }
+          ]
+        },
+        {
+          "role": "user",
+          "content": [
+            {
+              "type": "tool_result",
+              "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
+              "content": [
+                {
+                  "type": "search_result",
+                  "source": "https://docs.company.com/pricing",
+                  "title": "Pricing Plans",
+                  "content": [
+                    {
+                      "type": "text",
+                      "text": "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."
+                    }
+                  ],
+                  "citations": {"enabled": true}
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }'
+  ```
+
+  ```bash CLI
+  ant messages create <<'YAML'
+  model: claude-opus-4-8
+  max_tokens: 1024
+  tools:
+    - name: search_knowledge_base
+      description: Search the company knowledge base for information
+      input_schema:
+        type: object
+        properties:
+          query:
+            type: string
+            description: The search query
+        required: [query]
+  messages:
+    - role: user
+      content:
+        - type: search_result
+          source: https://docs.company.com/overview
+          title: Product Overview
+          content:
+            - type: text
+              text: >-
+                Acme Dashboard is a monitoring tool for distributed systems.
+                It supports real-time alerting and custom metric dashboards.
+          citations:
+            enabled: true
+        - type: text
+          text: What does Acme Dashboard do, and what plans is it available on?
+    - role: assistant
+      content:
+        - type: text
+          text: Let me check the pricing information.
+        - type: tool_use
+          id: toolu_01A09q90qw90lq917835lq9
+          name: search_knowledge_base
+          input:
+            query: Acme Dashboard pricing plans
+    - role: user
+      content:
+        - type: tool_result
+          tool_use_id: toolu_01A09q90qw90lq917835lq9
+          content:
+            - type: search_result
+              source: https://docs.company.com/pricing
+              title: Pricing Plans
+              content:
+                - type: text
+                  text: >-
+                    Acme Dashboard is available on the Starter plan at $10 per
+                    user per month and the Enterprise plan with custom pricing.
+              citations:
+                enabled: true
+  YAML
+  ```
+
+  ```python Python
+  from anthropic.types import (
+      MessageParam,
+      SearchResultBlockParam,
+      TextBlockParam,
+      ToolResultBlockParam,
+      ToolUseBlockParam,
+  )
+
+  client = Anthropic()
+
+  knowledge_base_tool = {
+      "name": "search_knowledge_base",
+      "description": "Search the company knowledge base for information",
+      "input_schema": {
+          "type": "object",
+          "properties": {"query": {"type": "string", "description": "The search query"}},
+          "required": ["query"],
+      },
+  }
+
+  # Replay a conversation that provides search results both ways: the first
+  # user message carries a pre-fetched result, the tool result returns another
+  response = client.messages.create(
+      model="claude-opus-4-8",
+      max_tokens=1024,
+      tools=[knowledge_base_tool],
+      messages=[
+          MessageParam(
+              role="user",
+              content=[
+                  SearchResultBlockParam(
+                      type="search_result",
+                      source="https://docs.company.com/overview",
+                      title="Product Overview",
+                      content=[
+                          TextBlockParam(
+                              type="text",
+                              text="Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.",
+                          )
+                      ],
+                      citations={"enabled": True},
+                  ),
+                  TextBlockParam(
+                      type="text",
+                      text="What does Acme Dashboard do, and what plans is it available on?",
+                  ),
+              ],
+          ),
+          MessageParam(
+              role="assistant",
+              content=[
+                  TextBlockParam(
+                      type="text", text="Let me check the pricing information."
+                  ),
+                  ToolUseBlockParam(
+                      type="tool_use",
+                      id="toolu_01A09q90qw90lq917835lq9",
+                      name="search_knowledge_base",
+                      input={"query": "Acme Dashboard pricing plans"},
+                  ),
+              ],
+          ),
+          MessageParam(
+              role="user",
+              content=[
+                  ToolResultBlockParam(
+                      type="tool_result",
+                      tool_use_id="toolu_01A09q90qw90lq917835lq9",
+                      content=[
+                          SearchResultBlockParam(
+                              type="search_result",
+                              source="https://docs.company.com/pricing",
+                              title="Pricing Plans",
+                              content=[
+                                  TextBlockParam(
+                                      type="text",
+                                      text="Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.",
+                                  )
+                              ],
+                              citations={"enabled": True},
+                          )
+                      ],
+                  )
+              ],
+          ),
+      ],
+  )
+
+  print(response)
+  ```
+
+  ```typescript TypeScript
+  const anthropic = new Anthropic();
+
+  const knowledgeBaseTool: Anthropic.Tool = {
+    name: "search_knowledge_base",
+    description: "Search the company knowledge base for information",
+    input_schema: {
+      type: "object" as const,
+      properties: {
+        query: { type: "string", description: "The search query" }
+      },
+      required: ["query"]
+    }
+  };
+
+  // Replay a conversation that provides search results both ways: the first
+  // user message carries a pre-fetched result, the tool result returns another
+  const response = await anthropic.messages.create({
+    model: "claude-opus-4-8",
+    max_tokens: 1024,
+    tools: [knowledgeBaseTool],
+    messages: [
+      {
+        role: "user",
+        content: [
+          {
+            type: "search_result" as const,
+            source: "https://docs.company.com/overview",
+            title: "Product Overview",
+            content: [
+              {
+                type: "text" as const,
+                text: "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."
+              }
+            ],
+            citations: { enabled: true }
+          },
+          {
+            type: "text" as const,
+            text: "What does Acme Dashboard do, and what plans is it available on?"
+          }
+        ]
+      },
+      {
+        role: "assistant",
+        content: [
+          { type: "text" as const, text: "Let me check the pricing information." },
+          {
+            type: "tool_use" as const,
+            id: "toolu_01A09q90qw90lq917835lq9",
+            name: "search_knowledge_base",
+            input: { query: "Acme Dashboard pricing plans" }
+          }
+        ]
+      },
+      {
+        role: "user",
+        content: [
+          {
+            type: "tool_result" as const,
+            tool_use_id: "toolu_01A09q90qw90lq917835lq9",
+            content: [
+              {
+                type: "search_result" as const,
+                source: "https://docs.company.com/pricing",
+                title: "Pricing Plans",
+                content: [
+                  {
+                    type: "text" as const,
+                    text: "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."
+                  }
                 ],
-                citations={"enabled": True},
-            ),
-            TextBlockParam(
-                type="text",
-                text="Tell me about this product and search for pricing information",
-            ),
-        ],
-    )
-]
-
-# Claude might respond and call a tool to search for pricing
-# Then you provide tool results with more search results
+                citations: { enabled: true }
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  });
+
+  console.log(response);
+  ```
+
+  ```csharp C#
+  AnthropicClient client = new();
+
+  // Replay a conversation that provides search results both ways: the first
+  // user message carries a pre-fetched result, the tool result returns another
+  var response = await client.Messages.Create(new()
+  {
+      Model = Model.ClaudeOpus4_8,
+      MaxTokens = 1024,
+      Tools =
+      [
+          new ToolUnion(new Tool()
+          {
+              Name = "search_knowledge_base",
+              Description = "Search the company knowledge base for information",
+              InputSchema = new InputSchema()
+              {
+                  Properties = new Dictionary<string, JsonElement>
+                  {
+                      ["query"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The search query" }),
+                  },
+                  Required = ["query"],
+              },
+          }),
+      ],
+      Messages =
+      [
+          new()
+          {
+              Role = Role.User,
+              Content = new MessageParamContent(
+              [
+                  new ContentBlockParam(new SearchResultBlockParam
+                  {
+                      Source = "https://docs.company.com/overview",
+                      Title = "Product Overview",
+                      Content = [new() { Text = "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards." }],
+                      Citations = new() { Enabled = true },
+                  }),
+                  new ContentBlockParam(new TextBlockParam { Text = "What does Acme Dashboard do, and what plans is it available on?" }),
+              ]),
+          },
+          new()
+          {
+              Role = Role.Assistant,
+              Content = new MessageParamContent(
+              [
+                  new ContentBlockParam(new TextBlockParam { Text = "Let me check the pricing information." }),
+                  new ContentBlockParam(new ToolUseBlockParam
+                  {
+                      ID = "toolu_01A09q90qw90lq917835lq9",
+                      Name = "search_knowledge_base",
+                      Input = new Dictionary<string, JsonElement>
+                      {
+                          ["query"] = JsonSerializer.SerializeToElement("Acme Dashboard pricing plans"),
+                      },
+                  }),
+              ]),
+          },
+          new()
+          {
+              Role = Role.User,
+              Content = new MessageParamContent(
+              [
+                  new ContentBlockParam(new ToolResultBlockParam()
+                  {
+                      ToolUseID = "toolu_01A09q90qw90lq917835lq9",
+                      Content = new ToolResultBlockParamContent(
+                      [
+                          new SearchResultBlockParam
+                          {
+                              Source = "https://docs.company.com/pricing",
+                              Title = "Pricing Plans",
+                              Content = [new() { Text = "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing." }],
+                              Citations = new() { Enabled = true },
+                          },
+                      ]),
+                  }),
+              ]),
+          },
+      ],
+  });
+
+  Console.WriteLine(response);
+  ```
+
+  ```go Go
+  client := anthropic.NewClient()
+
+  knowledgeBaseTool := anthropic.ToolUnionParam{
+  	OfTool: &anthropic.ToolParam{
+  		Name:        "search_knowledge_base",
+  		Description: anthropic.String("Search the company knowledge base for information"),
+  		InputSchema: anthropic.ToolInputSchemaParam{
+  			Properties: map[string]any{
+  				"query": map[string]any{"type": "string", "description": "The search query"},
+  			},
+  			Required: []string{"query"},
+  		},
+  	},
+  }
+
+  // Replay a conversation that provides search results both ways: the first
+  // user message carries a pre-fetched result, the tool result returns another
+  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
+  	Model:     anthropic.ModelClaudeOpus4_8,
+  	MaxTokens: 1024,
+  	Tools:     []anthropic.ToolUnionParam{knowledgeBaseTool},
+  	Messages: []anthropic.MessageParam{
+  		anthropic.NewUserMessage(
+  			anthropic.ContentBlockParamUnion{OfSearchResult: &anthropic.SearchResultBlockParam{
+  				Content: []anthropic.TextBlockParam{
+  					{Text: "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."},
+  				},
+  				Source:    "https://docs.company.com/overview",
+  				Title:     "Product Overview",
+  				Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},
+  			}},
+  			anthropic.NewTextBlock("What does Acme Dashboard do, and what plans is it available on?"),
+  		),
+  		anthropic.NewAssistantMessage(
+  			anthropic.NewTextBlock("Let me check the pricing information."),
+  			anthropic.ContentBlockParamUnion{OfToolUse: &anthropic.ToolUseBlockParam{
+  				ID:    "toolu_01A09q90qw90lq917835lq9",
+  				Name:  "search_knowledge_base",
+  				Input: map[string]any{"query": "Acme Dashboard pricing plans"},
+  			}},
+  		),
+  		anthropic.NewUserMessage(
+  			anthropic.ContentBlockParamUnion{OfToolResult: &anthropic.ToolResultBlockParam{
+  				ToolUseID: "toolu_01A09q90qw90lq917835lq9",
+  				Content: []anthropic.ToolResultBlockParamContentUnion{
+  					{OfSearchResult: &anthropic.SearchResultBlockParam{
+  						Content: []anthropic.TextBlockParam{
+  							{Text: "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."},
+  						},
+  						Source:    "https://docs.company.com/pricing",
+  						Title:     "Pricing Plans",
+  						Citations: anthropic.CitationsConfigParam{Enabled: anthropic.Bool(true)},
+  					}},
+  				},
+  			}},
+  		),
+  	},
+  })
+  if err != nil {
+  	log.Fatal(err)
+  }
+  fmt.Println(response)
+  ```
+
+  ```java Java
+  import com.anthropic.core.JsonValue;
+  import com.anthropic.models.messages.CitationsConfigParam;
+  import com.anthropic.models.messages.ContentBlockParam;
+  // ...
+  import com.anthropic.models.messages.SearchResultBlockParam;
+  import com.anthropic.models.messages.TextBlockParam;
+  import com.anthropic.models.messages.Tool;
+  import com.anthropic.models.messages.ToolResultBlockParam;
+  import com.anthropic.models.messages.ToolUseBlockParam;
+  // ...
+
+  public class CombinedSearchResultsExample {
+      public static void main(String[] args) {
+          AnthropicClient client = AnthropicOkHttpClient.fromEnv();
+
+          Tool knowledgeBaseTool = Tool.builder()
+              .name("search_knowledge_base")
+              .description("Search the company knowledge base for information")
+              .inputSchema(Tool.InputSchema.builder()
+                  .properties(JsonValue.from(Map.of(
+                      "query", Map.of("type", "string", "description", "The search query")
+                  )))
+                  .putAdditionalProperty("required", JsonValue.from(List.of("query")))
+                  .build())
+              .build();
+
+          // Replay a conversation that provides search results both ways: the first
+          // user message carries a pre-fetched result, the tool result returns another
+          MessageCreateParams params = MessageCreateParams.builder()
+              .model(Model.CLAUDE_OPUS_4_8)
+              .maxTokens(1024L)
+              .addTool(knowledgeBaseTool)
+              .addUserMessageOfBlockParams(List.of(
+                  ContentBlockParam.ofSearchResult(SearchResultBlockParam.builder()
+                      .source("https://docs.company.com/overview")
+                      .title("Product Overview")
+                      .content(List.of(TextBlockParam.builder()
+                          .text("Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.")
+                          .build()))
+                      .citations(CitationsConfigParam.builder().enabled(true).build())
+                      .build()),
+                  ContentBlockParam.ofText(TextBlockParam.builder()
+                      .text("What does Acme Dashboard do, and what plans is it available on?")
+                      .build())
+              ))
+              .addAssistantMessageOfBlockParams(List.of(
+                  ContentBlockParam.ofText(TextBlockParam.builder()
+                      .text("Let me check the pricing information.")
+                      .build()),
+                  ContentBlockParam.ofToolUse(ToolUseBlockParam.builder()
+                      .id("toolu_01A09q90qw90lq917835lq9")
+                      .name("search_knowledge_base")
+                      .input(JsonValue.from(Map.of("query", "Acme Dashboard pricing plans")))
+                      .build())
+              ))
+              .addUserMessageOfBlockParams(List.of(
+                  ContentBlockParam.ofToolResult(ToolResultBlockParam.builder()
+                      .toolUseId("toolu_01A09q90qw90lq917835lq9")
+                      .contentOfBlocks(List.of(
+                          ToolResultBlockParam.Content.Block.ofSearchResult(SearchResultBlockParam.builder()
+                              .source("https://docs.company.com/pricing")
+                              .title("Pricing Plans")
+                              .content(List.of(TextBlockParam.builder()
+                                  .text("Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.")
+                                  .build()))
+                              .citations(CitationsConfigParam.builder().enabled(true).build())
+                              .build())
+                      ))
+                      .build())
+              ))
+              .build();
+
+          Message response = client.messages().create(params);
+          System.out.println(response);
+      }
+  }
+  ```
+
+  ```php PHP
+  $client = new Client();
+
+  $knowledgeBaseTool = [
+      'name' => 'search_knowledge_base',
+      'description' => 'Search the company knowledge base for information',
+      'input_schema' => [
+          'type' => 'object',
+          'properties' => [
+              'query' => ['type' => 'string', 'description' => 'The search query']
+          ],
+          'required' => ['query']
+      ]
+  ];
+
+  // Replay a conversation that provides search results both ways: the first
+  // user message carries a pre-fetched result, the tool result returns another
+  $response = $client->messages->create(
+      maxTokens: 1024,
+      tools: [$knowledgeBaseTool],
+      messages: [
+          [
+              'role' => 'user',
+              'content' => [
+                  [
+                      'type' => 'search_result',
+                      'source' => 'https://docs.company.com/overview',
+                      'title' => 'Product Overview',
+                      'content' => [
+                          [
+                              'type' => 'text',
+                              'text' => 'Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.'
+                          ]
+                      ],
+                      'citations' => ['enabled' => true]
+                  ],
+                  [
+                      'type' => 'text',
+                      'text' => 'What does Acme Dashboard do, and what plans is it available on?'
+                  ]
+              ]
+          ],
+          [
+              'role' => 'assistant',
+              'content' => [
+                  ['type' => 'text', 'text' => 'Let me check the pricing information.'],
+                  [
+                      'type' => 'tool_use',
+                      'id' => 'toolu_01A09q90qw90lq917835lq9',
+                      'name' => 'search_knowledge_base',
+                      'input' => ['query' => 'Acme Dashboard pricing plans']
+                  ]
+              ]
+          ],
+          [
+              'role' => 'user',
+              'content' => [
+                  [
+                      'type' => 'tool_result',
+                      'tool_use_id' => 'toolu_01A09q90qw90lq917835lq9',
+                      'content' => [
+                          [
+                              'type' => 'search_result',
+                              'source' => 'https://docs.company.com/pricing',
+                              'title' => 'Pricing Plans',
+                              'content' => [
+                                  [
+                                      'type' => 'text',
+                                      'text' => 'Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.'
+                                  ]
+                              ],
+                              'citations' => ['enabled' => true]
+                          ]
+                      ]
+                  ]
+              ]
+          ]
+      ],
+      model: 'claude-opus-4-8',
+  );
+
+  echo json_encode($response, JSON_PRETTY_PRINT);
+  ```
+
+  ```ruby Ruby
+  client = Anthropic::Client.new
+
+  knowledge_base_tool = {
+    name: "search_knowledge_base",
+    description: "Search the company knowledge base for information",
+    input_schema: {
+      type: "object",
+      properties: {
+        query: { type: "string", description: "The search query" }
+      },
+      required: ["query"]
+    }
+  }
+
+  # Replay a conversation that provides search results both ways: the first
+  # user message carries a pre-fetched result, the tool result returns another
+  response = client.messages.create(
+    model: "claude-opus-4-8",
+    max_tokens: 1024,
+    tools: [knowledge_base_tool],
+    messages: [
+      {
+        role: "user",
+        content: [
+          {
+            type: "search_result",
+            source: "https://docs.company.com/overview",
+            title: "Product Overview",
+            content: [
+              {
+                type: "text",
+                text: "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards."
+              }
+            ],
+            citations: { enabled: true }
+          },
+          {
+            type: "text",
+            text: "What does Acme Dashboard do, and what plans is it available on?"
+          }
+        ]
+      },
+      {
+        role: "assistant",
+        content: [
+          { type: "text", text: "Let me check the pricing information." },
+          {
+            type: "tool_use",
+            id: "toolu_01A09q90qw90lq917835lq9",
+            name: "search_knowledge_base",
+            input: { query: "Acme Dashboard pricing plans" }
+          }
+        ]
+      },
+      {
+        role: "user",
+        content: [
+          {
+            type: "tool_result",
+            tool_use_id: "toolu_01A09q90qw90lq917835lq9",
+            content: [
+              {
+                type: "search_result",
+                source: "https://docs.company.com/pricing",
+                title: "Pricing Plans",
+                content: [
+                  {
+                    type: "text",
+                    text: "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing."
+                  }
+                ],
+                citations: { enabled: true }
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  )
+
+  puts response
+  ```
+</CodeGroup>
+
+The response cites both sources. The pre-fetched result is `search_result_index: 0` and the tool-returned result is `search_result_index: 1`, matching the order the `search_result` blocks appear in the conversation:
+
+```json
+{
+  "role": "assistant",
+  "content": [
+    {
+      "type": "text",
+      "text": "Here's what I found about Acme Dashboard:\n\n**What it does:** "
+    },
+    {
+      "type": "text",
+      "text": "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.",
+      "citations": [
+        {
+          "type": "search_result_location",
+          "cited_text": "Acme Dashboard is a monitoring tool for distributed systems. It supports real-time alerting and custom metric dashboards.",
+          "source": "https://docs.company.com/overview",
+          "title": "Product Overview",
+          "search_result_index": 0,
+          "start_block_index": 0,
+          "end_block_index": 1
+        }
+      ]
+    },
+    {
+      "type": "text",
+      "text": "\n\n**Available plans:** "
+    },
+    {
+      "type": "text",
+      "text": "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.",
+      "citations": [
+        {
+          "type": "search_result_location",
+          "cited_text": "Acme Dashboard is available on the Starter plan at $10 per user per month and the Enterprise plan with custom pricing.",
+          "source": "https://docs.company.com/pricing",
+          "title": "Pricing Plans",
+          "search_result_index": 1,
+          "start_block_index": 0,
+          "end_block_index": 1
+        }
+      ]
+    }
+  ]
+}
 ```
 
-### Combining with other content types
-
-Both methods support mixing search results with other content:
-
-```python
-from anthropic.types import SearchResultBlockParam, TextBlockParam
-
-# In tool results
-tool_result = [
-    SearchResultBlockParam(
-        type="search_result",
-        source="https://docs.company.com/guide",
-        title="User Guide",
-        content=[TextBlockParam(type="text", text="Configuration details...")],
-        citations={"enabled": True},
-    ),
-    TextBlockParam(
-        type="text", text="Additional context: This applies to version 2.0 and later."
-    ),
-]
-
-# In top-level content
-user_content = [
-    SearchResultBlockParam(
-        type="search_result",
-        source="https://research.com/paper",
-        title="Research Paper",
-        content=[TextBlockParam(type="text", text="Key findings...")],
-        citations={"enabled": True},
-    ),
-    {
-        "type": "image",
-        "source": {"type": "url", "url": "https://example.com/chart.png"},
-    },
-    TextBlockParam(
-        type="text", text="How does the chart relate to the research findings?"
-    ),
-]
-```
+### Mixing with other content types
+
+In user messages, `search_result` blocks can sit alongside any other content block. The Method 2 example pairs search results with a `text` question, and image or document blocks can join them the same way.
+
+Tool results are stricter: if any block in a `tool_result` content array is a `search_result`, all of its blocks must be `search_result`. Mixing search results with other block types in the same tool result returns a validation error. To return supporting text alongside tool-sourced search results, include it as a text block inside one of the search results' `content` arrays, where it also becomes citable.
 
 ### Cache control
 
-Add cache control for better performance:
+Add `cache_control` on the search result block to cache it for reuse across requests. It sits alongside `citations` on the same block:
 
 ```json
 {
@@ -1358,11 +2088,12 @@
   "source": "https://docs.company.com/guide",
   "title": "User Guide",
   "content": [{ "type": "text", "text": "..." }],
-  "cache_control": {
-    "type": "ephemeral"
-  }
+  "citations": { "enabled": true },
+  "cache_control": { "type": "ephemeral" }
 }
 ```
+
+See [Prompt caching](/docs/en/build-with-claude/prompt-caching) for minimum cacheable lengths and other requirements.
 
 ### Citation control
 
@@ -1380,11 +2111,7 @@
 }
 ```
 
-When `citations.enabled` is set to `true`, Claude includes citation references when using information from the search result. This enables:
-
-* Natural citations for your custom RAG applications
-* Source attribution when interfacing with proprietary knowledge bases
-* Web search-quality citations for any custom tool that returns search results
+When `citations.enabled` is set to `true`, Claude attaches citation references to the text blocks that draw on the search result.
 
 <Warning>
   Citations are all-or-nothing: either all search results in a request must have citations enabled, or all must have them disabled. Mixing search results with different citation settings results in an error.
@@ -1418,34 +2145,28 @@
    * Ensure titles accurately reflect content
    * Keep formatting consistent
 
-3. **Handle errors gracefully:**
-
-   ```python
-   def search_with_fallback(query):
-       try:
-           results = perform_search(query)
-           if not results:
-               return {"type": "text", "text": "No results found."}
-           return format_as_search_results(results)
-       except Exception as e:
-           return {"type": "text", "text": f"Search error: {str(e)}"}
-   ```
+3. **Handle errors gracefully:** when a search fails or returns nothing, return a plain text block describing the outcome (for example, `{"type": "text", "text": "No results found."}`) instead of raising an error: Claude explains the empty result to the user, and the conversation continues.
 
 ## Limitations
 
 * Search result content blocks are available on Claude API, Amazon Bedrock, and Google Cloud.
 * Only text content is supported within search results (no images or other media).
-* The `content` array must contain at least one text block.
+* `search_result` blocks can only appear in user messages (including inside tool results). Assistant messages with search results are rejected.
+* When the [web search tool](/docs/en/agents-and-tools/tool-use/web-search-tool) is enabled in the same request, citations must be enabled on all `search_result` blocks.
 
 ## Next steps
 
 <CardGroup cols={2}>
+  <Card title="Streaming refusals" icon="lock" href="/docs/en/test-and-evaluate/strengthen-guardrails/handle-streaming-refusals">
+    Detect and handle refusal stop reasons in streaming responses, and retry refused requests on a fallback model.
+  </Card>
+
   <Card title="Citations" icon="book" href="/docs/en/build-with-claude/citations">
-    Learn how citations work across documents, custom content, and search results.
+    Ground Claude's responses in your source documents. Citations return the exact passages that support each claim, so you can verify answers and surface sources to your users.
   </Card>
 
-  <Card title="Web search tool" icon="magnifying-glass" href="/docs/en/agents-and-tools/tool-use/web-search-tool">
-    Let Claude search the web and cite sources automatically using a server tool.
+  <Card title="Web search tool" icon="browser" href="/docs/en/agents-and-tools/tool-use/web-search-tool">
+    Give Claude access to current web content with cited sources, optional dynamic filtering, and domain controls.
   </Card>
 
   <Card title="Messages API reference" icon="code" href="/docs/en/api/messages/create">