> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nimbleway.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Anthropic SDK

> Use the Nimble SDK as a tool inside Claude's tool-use API.

Give Claude models real-time web data by wiring the Nimble SDK into Anthropic tool use. No extra packages needed.

## Prerequisites

<CodeGroup>
  ```bash Python theme={"system"}
  pip install anthropic nimble_python
  ```

  ```bash Node theme={"system"}
  npm install @anthropic-ai/sdk @nimble-way/nimble-js
  ```
</CodeGroup>

Set environment variables:

```bash theme={"system"}
export ANTHROPIC_API_KEY="your-anthropic-api-key"
export NIMBLE_API_KEY="your-nimble-api-key"
```

Get a Nimble API key from the [dashboard](https://online.nimbleway.com/settings/api-keys) (free trial available).

The examples set the Nimble client's `client_source` (`clientSource` in Node) to `anthropic-sdk`, which sends an `X-Client-Source` header that attributes requests to this integration. It defaults to `sdk` when unset.

## Quick Start: Tool Runner

The Anthropic Python SDK includes a `tool_runner` that handles the tool-calling loop automatically. Define Nimble tools with the `@beta_tool` decorator and the SDK manages execution, message history, and retries.

```python Python theme={"system"}
import os
import json
import anthropic
from anthropic import beta_tool
from nimble_python import Nimble

client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
nimble_client = Nimble(api_key=os.environ["NIMBLE_API_KEY"], client_source="anthropic-sdk")

@beta_tool
def nimble_search(query: str) -> str:
    """Search the web using Nimble and return relevant results.

    Args:
        query: The search query to execute.
    """
    result = nimble_client.search(query=query)
    return json.dumps(result, default=str)

@beta_tool
def nimble_extract(url: str) -> str:
    """Extract clean content from a URL using Nimble.

    Args:
        url: The URL to extract content from.
    """
    result = nimble_client.extract(url=url)
    return json.dumps(result, default=str)

runner = client.beta.messages.tool_runner(
    model="claude-sonnet-5",
    max_tokens=4096,
    tools=[nimble_search, nimble_extract],
    messages=[{"role": "user", "content": "What are the latest trends in AI agents?"}],
)

for message in runner:
    for block in message.content:
        if hasattr(block, "text"):
            print(block.text)
```

<Tip>
  The `@beta_tool` decorator auto-generates the JSON schema from type hints and doc strings, so no manual schema definition is needed.
</Tip>

## Agent API V2: autonomous research tool

The tools above (`search`, `extract`, `crawl`, `map`, and [Extract Templates](/nimble-sdk/web-tools/extract/template)) are **synchronous and single-shot**. One call returns data directly, and Claude does the reasoning.

[Agent API V2](/nimble-sdk/web-search-agents/overview) is different. It exposes an **asynchronous research agent** that plans, searches across many sources, and returns a synthesized answer with a per-claim [trust report](/nimble-sdk/web-search-agents/trust). The lifecycle is **create a run → poll to a terminal state → retrieve the result**. Wrap that whole lifecycle in one function so Claude sees a single "deep research" tool.

### 1. Wrap the run lifecycle

This helper uses a stable `agent_name`, so every call — from this process or any other — reuses the same agent and its memory instead of spinning up a fresh one each time. It creates the run, polls until terminal, handles `failed` and `cancelled` runs, and returns the answer plus its trust and citation metadata. Errors return a safe message, so no API key or raw exception is surfaced.

```python Python theme={"system"}
import os
import time
from nimble_python import Nimble

nimble_client = Nimble(api_key=os.environ["NIMBLE_API_KEY"], client_source="anthropic-sdk")

# Stable name for this integration's research agent. The first call creates
# it; every later call (here or from another process) reuses it and its memory.
# Pick a name unique to this deployment - anyone reusing the same name on the
# same account reuses the same agent and its memory.
NIMBLE_AGENT_NAME = os.environ["NIMBLE_AGENT_NAME"]

def run_deep_research(query: str, timeout_s: int = 300) -> dict:
    """Run one Agent API V2 research task end to end and return a cited answer."""
    try:
        # Create-or-reuse the named agent, then start the run.
        run = nimble_client.agents.run(input=query, agent_name=NIMBLE_AGENT_NAME)

        agent_id = run.web_search_agent_id

        # Poll until the run reaches a terminal state.
        deadline = time.time() + timeout_s
        while run.is_active:
            if time.time() > deadline:
                return {"status": "timeout", "error": "Run did not finish in time."}
            time.sleep(10)
            run = nimble_client.agents.runs.get(run.id, agent_id=agent_id)

        # Handle failed or cancelled runs.
        if run.status != "completed":
            return {"status": run.status, "error": f"Run ended as '{run.status}'."}

        # Retrieve the completed result and summarize its trust report.
        result = nimble_client.agents.runs.result(run.id, agent_id=agent_id)
        output = result.output
        return {
            "status": "completed",
            "answer": output.content,
            "confidence": output.trust.confidence,
            "citations": [
                {"url": s.url, "title": s.title, "source_type": s.type}
                for s in output.trust.sources
            ],
        }
    except Exception:
        # Never leak credentials or raw exceptions back to the model.
        return {"status": "error", "error": "Nimble request failed."}
```

<Tip>
  Pass an `output_schema` to `agents.run` (or `agents.runs.create`) to get structured JSON in `result.output` instead of prose. Trust is then keyed by JSON path. See [Dataset Building](/nimble-sdk/web-search-agents/use-cases/dataset-building).
</Tip>

### 2. Expose it with the Tool Runner

Wrap the helper with `@beta_tool` and pass it to `tool_runner`. Claude calls one tool; the async run loop stays hidden.

```python Python theme={"system"}
import os
import json
import anthropic
from anthropic import beta_tool

client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

@beta_tool
def nimble_deep_research(query: str) -> str:
    """Run an autonomous, fully-cited web-research task with Nimble Agent API V2.

    Use for open-ended questions that need synthesis across multiple sources,
    not single-page lookups. Returns a cited answer with a trust grade.

    Args:
        query: The research question or task, in plain language.
    """
    return json.dumps(run_deep_research(query), default=str)

runner = client.beta.messages.tool_runner(
    model="claude-sonnet-5",
    max_tokens=4096,
    tools=[nimble_deep_research],
    messages=[{
        "role": "user",
        "content": "Compare the pricing and positioning of Datadog and Grafana Cloud.",
    }],
)

for message in runner:
    for block in message.content:
        if hasattr(block, "text"):
            print(block.text)
```

The tool returns the synthesized answer, an overall `confidence` grade (`high`, `medium`, or `low`), and the source URLs behind it, so Claude can weigh how much to trust each claim. See [Trust](/nimble-sdk/web-search-agents/trust) for the full report structure.

## Messages API (Manual Loop)

For more control, define tools using the Anthropic `input_schema` format and handle the `tool_use` → `tool_result` loop manually.

### 1. Define the Tool Schema

```python Python theme={"system"}
tools = [
    {
        "name": "nimble_search",
        "description": "Search the web using Nimble and return relevant results.",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "The search query to execute"
                }
            },
            "required": ["query"]
        }
    },
    {
        "name": "nimble_extract",
        "description": "Extract clean content from a URL using Nimble.",
        "input_schema": {
            "type": "object",
            "properties": {
                "url": {
                    "type": "string",
                    "description": "The URL to extract content from"
                }
            },
            "required": ["url"]
        }
    }
]
```

### 2. Handle the Tool-Use Loop

```python Python theme={"system"}
import os
import json
import anthropic
from nimble_python import Nimble

client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
nimble_client = Nimble(api_key=os.environ["NIMBLE_API_KEY"], client_source="anthropic-sdk")

def process_tool_call(name, tool_input):
    if name == "nimble_search":
        result = nimble_client.search(query=tool_input["query"])
        return json.dumps(result, default=str)
    elif name == "nimble_extract":
        result = nimble_client.extract(url=tool_input["url"])
        return json.dumps(result, default=str)

messages = [
    {"role": "user", "content": "What are the latest trends in AI agents?"}
]

response = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=4096,
    tools=tools,
    messages=messages,
)

# Tool-use loop: keep processing until Claude stops calling tools
while response.stop_reason == "tool_use":
    tool_use_blocks = [block for block in response.content if block.type == "tool_use"]
    messages.append({"role": "assistant", "content": response.content})

    tool_results = []
    for tool_use in tool_use_blocks:
        result = process_tool_call(tool_use.name, tool_use.input)
        tool_results.append({
            "type": "tool_result",
            "tool_use_id": tool_use.id,
            "content": result,
        })

    messages.append({"role": "user", "content": tool_results})

    response = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=4096,
        tools=tools,
        messages=messages,
    )

for block in response.content:
    if hasattr(block, "text"):
        print(block.text)
```

### Node.js Example

```typescript Node theme={"system"}
import Anthropic from "@anthropic-ai/sdk";
import Nimble from "@nimble-way/nimble-js";

const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const nimble = new Nimble({ apiKey: process.env.NIMBLE_API_KEY, clientSource: "anthropic-sdk" });

const tools: Anthropic.Tool[] = [
  {
    name: "nimble_search",
    description: "Search the web using Nimble and return relevant results.",
    input_schema: {
      type: "object" as const,
      properties: {
        query: { type: "string", description: "The search query to execute" },
      },
      required: ["query"],
    },
  },
  {
    name: "nimble_extract",
    description: "Extract clean content from a URL using Nimble.",
    input_schema: {
      type: "object" as const,
      properties: {
        url: { type: "string", description: "The URL to extract content from" },
      },
      required: ["url"],
    },
  },
];

async function processToolCall(name: string, toolInput: Record<string, string>) {
  if (name === "nimble_search") {
    return await nimble.search({ query: toolInput.query });
  }
  if (name === "nimble_extract") {
    return await nimble.extract({ url: toolInput.url });
  }
}

const messages: Anthropic.MessageParam[] = [
  { role: "user", content: "What are the latest trends in AI agents?" },
];

let response = await client.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 4096,
  tools,
  messages,
});

while (response.stop_reason === "tool_use") {
  const toolUseBlocks = response.content.filter(
    (block): block is Anthropic.ToolUseBlock => block.type === "tool_use"
  );

  messages.push({ role: "assistant", content: response.content });

  const toolResults: Anthropic.ToolResultBlockParam[] = [];
  for (const toolUse of toolUseBlocks) {
    const result = await processToolCall(
      toolUse.name,
      toolUse.input as Record<string, string>
    );
    toolResults.push({
      type: "tool_result",
      tool_use_id: toolUse.id,
      content: JSON.stringify(result),
    });
  }

  messages.push({ role: "user", content: toolResults });

  response = await client.messages.create({
    model: "claude-sonnet-5",
    max_tokens: 4096,
    tools,
    messages,
  });
}

for (const block of response.content) {
  if (block.type === "text") {
    console.log(block.text);
  }
}
```

## How It Works

The Anthropic tool-use flow has three steps:

<Steps>
  <Step title="Send tools and message">
    Pass the tool definitions and user message to Claude. If Claude decides to use a tool, it returns a `tool_use` content block with the tool name and input.
  </Step>

  <Step title="Execute the tool">
    Parse the `tool_use` block and call the corresponding Nimble SDK method. Return the result as a `tool_result` message.
  </Step>

  <Step title="Get the final answer">
    Claude processes the tool result and responds with a text answer. If it needs more data, it may call another tool, and the loop continues until `stop_reason` is `end_turn`.
  </Step>
</Steps>

## Available Tools

Any Nimble SDK method can be exposed as an Anthropic tool. Here are the most common ones:

| Tool | SDK Method | Use Case |
| - | - | - |
| `nimble_search` | `client.search()` | Web search with structured results |
| `nimble_extract` | `client.extract()` | Extract content from a URL |
| `nimble_crawl` | `client.crawl.run()` | Crawl an entire site |
| `nimble_map` | `client.map()` | Discover all URLs on a domain |

<Tip>
  See the [Python SDK](/nimble-sdk/sdks/python) and [Node SDK](/nimble-sdk/sdks/node) docs for the full list of methods and parameters.
</Tip>

## Next Steps

<CardGroup cols={2}>
  <Card title="Python SDK" icon="python" href="/nimble-sdk/sdks/python">
    Full Python SDK reference with all methods and configuration options
  </Card>

  <Card title="Node SDK" icon="node-js" href="/nimble-sdk/sdks/node">
    Full Node.js SDK reference with TypeScript support
  </Card>

  <Card title="Web Search Agent" icon="robot" href="/nimble-sdk/web-search-agents/overview">
    Agent API V2: autonomous research runs with per-claim trust
  </Card>

  <Card title="OpenAI" icon="bolt" href="/integrations/connectors/openai">
    Use Nimble with OpenAI function calling and the Agents SDK
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.