> ## 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.

# OpenAI

> Use the Nimble SDK as a tool inside OpenAI function calling and the Agents SDK.

Give OpenAI models real-time web data by wiring the Nimble SDK into function calling. No extra packages needed.

## Prerequisites

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

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

Set environment variables:

```bash theme={"system"}
export OPENAI_API_KEY="your-openai-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 `openai`, which sends an `X-Client-Source` header that attributes requests to this integration. It defaults to `sdk` when unset.

## Quick Start: OpenAI Agents SDK

The [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/) provides a higher-level framework for building agents. Wrap Nimble tools with the `@function_tool` decorator and the SDK handles the tool-calling loop automatically.

```bash theme={"system"}
pip install openai-agents nimble_python
```

```python Python theme={"system"}
import os
import asyncio
from agents import Agent, Runner, function_tool
from nimble_python import Nimble

nimble_client = Nimble(api_key=os.environ["NIMBLE_API_KEY"], client_source="openai")

@function_tool
def nimble_search(query: str) -> str:
    """Search the web using Nimble and return relevant results."""
    result = nimble_client.search(query=query)
    return str(result)

@function_tool
def nimble_extract(url: str) -> str:
    """Extract clean content from a URL using Nimble."""
    result = nimble_client.extract(url=url)
    return str(result)

async def main():
    agent = Agent(
        name="Web Research Agent",
        instructions=(
            "You are a research assistant with access to real-time web data. "
            "Use nimble_search to find information and nimble_extract to read specific pages."
        ),
        tools=[nimble_search, nimble_extract],
    )

    result = await Runner.run(agent, "What are the latest trends in AI agents?")
    print(result.final_output)

asyncio.run(main())
```

## 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 the OpenAI model 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 the OpenAI model 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="openai")

# 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 as a tool

Wrap the helper with `@function_tool` and hand it to an agent. The model calls one tool; the async run loop stays hidden.

```python Python theme={"system"}
import json
import asyncio
from agents import Agent, Runner, function_tool

@function_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.
    """
    return json.dumps(run_deep_research(query), default=str)

async def main():
    agent = Agent(
        name="Deep Research Agent",
        instructions=(
            "You are a research analyst. Use nimble_deep_research for questions "
            "that need current, cited web evidence. Report the confidence grade "
            "and cite the sources it returns."
        ),
        tools=[nimble_deep_research],
    )

    result = await Runner.run(
        agent,
        "Compare the pricing and positioning of Datadog and Grafana Cloud.",
    )
    print(result.final_output)

asyncio.run(main())
```

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

## Chat Completions API

For more control over the tool-calling loop, define Nimble tools as OpenAI function schemas and handle calls manually.

### 1. Define the Tool Schema

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

### 2. Handle Tool Calls

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

openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
nimble_client = Nimble(api_key=os.environ["NIMBLE_API_KEY"], client_source="openai")

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

messages = [
    {"role": "system", "content": "You are a research assistant with access to real-time web data."},
    {"role": "user", "content": "What are the latest trends in AI agents?"}
]

response = openai_client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    tools=tools,
)

assistant_msg = response.choices[0].message
messages.append(assistant_msg)

if assistant_msg.tool_calls:
    for tc in assistant_msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = handle_tool_call(tc.function.name, args)
        messages.append({
            "role": "tool",
            "tool_call_id": tc.id,
            "content": result,
        })

    final = openai_client.chat.completions.create(
        model="gpt-4o",
        messages=messages,
    )
    print(final.choices[0].message.content)
```

### Node.js Example

```typescript Node theme={"system"}
import OpenAI from "openai";
import Nimble from "@nimble-way/nimble-js";

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const nimble = new Nimble({ apiKey: process.env.NIMBLE_API_KEY, clientSource: "openai" });

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

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

const messages: OpenAI.ChatCompletionMessageParam[] = [
  { role: "system", content: "You are a research assistant with access to real-time web data." },
  { role: "user", content: "What are the latest trends in AI agents?" },
];

const response = await openai.chat.completions.create({
  model: "gpt-4o",
  messages,
  tools,
});

const assistantMsg = response.choices[0].message;
messages.push(assistantMsg);

if (assistantMsg.tool_calls) {
  for (const tc of assistantMsg.tool_calls) {
    const args = JSON.parse(tc.function.arguments);
    const result = await handleToolCall(tc.function.name, args);
    messages.push({
      role: "tool",
      tool_call_id: tc.id,
      content: JSON.stringify(result),
    });
  }

  const final = await openai.chat.completions.create({
    model: "gpt-4o",
    messages,
  });

  console.log(final.choices[0].message.content);
}
```

## Available Tools

Any Nimble SDK method can be exposed as an OpenAI 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="LangChain" icon="link" href="/integrations/connectors/langchain">
    Pre-built LangChain tools and retrievers for Nimble
  </Card>
</CardGroup>


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