# Tools and SDKs

Anything that can talk to OpenAI or Anthropic can talk to Nymbot. In almost every case it is a base URL, a key and a model name.

Every setup below needs an API key, made in the app's **API** menu (see [API keys](https://nymbot.ai/docs/api/#keys)), and a model id from [the model list](https://nymbot.ai/docs/api-chat/#models). The examples use `anthropic/claude-sonnet-5` on the Pro balance. `nymbot/auto` is the cheaper choice on the standard balance for plain chat, but it cannot call tools, which Goose needs; for that, pick a catalog model with `capabilities.tools`.

Setup for coding agents such as Claude Code, Codex, Cline, Kilo Code and Continue is on its own page: [coding tools](https://nymbot.ai/docs/api-tools/).

> **Give each tool its own key**
>
> Make a separate key for each tool, with a cap. Coding agents make many model calls for one task, and a cap on the key is the simplest way to know the most a tool can spend. If a tool misbehaves, revoke its key and nothing else is affected.

## OpenAI SDK

The official Python and JavaScript libraries work unchanged. Point `base_url` at Nymbot and pass your key. Chat Completions, Responses, images, audio and embeddings all work through them.

cURL

```
curl https://nymbot.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $NYMBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "anthropic/claude-sonnet-5", "messages": [{"role": "user", "content": "Hello"}]}'
```

Python

```
# pip install openai
import os
from openai import OpenAI

client = OpenAI(base_url="https://nymbot.ai/api/v1", api_key=os.environ["NYMBOT_API_KEY"])

reply = client.chat.completions.create(
    model="anthropic/claude-sonnet-5",
    messages=[{"role": "user", "content": "Hello"}],
)
print(reply.choices[0].message.content)
```

JavaScript

```
// npm install openai
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://nymbot.ai/api/v1", apiKey: process.env.NYMBOT_API_KEY });

const reply = await client.chat.completions.create({
  model: "anthropic/claude-sonnet-5",
  messages: [{ role: "user", content: "Hello" }],
});
console.log(reply.choices[0].message.content);
```

Nymbot's own fields, such as `plugins` for web search, go in `extra_body` in Python and straight into the request object in JavaScript. The `nymbot` cost object in a response is `reply.model_extra["nymbot"]` in Python and `reply.nymbot` in JavaScript. Libraries that read the `OPENAI_BASE_URL` and `OPENAI_API_KEY` environment variables can be pointed at Nymbot with those alone.

## Anthropic SDK

The Anthropic libraries use `https://nymbot.ai/api` as the base URL, without `/v1`, because they add `/v1/messages` themselves. Any catalog model works, not only Claude.

cURL

```
curl https://nymbot.ai/api/v1/messages \
  -H "x-api-key: $NYMBOT_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{"model": "claude-sonnet-5", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello"}]}'
```

Python

```
# pip install anthropic
import os
import anthropic

client = anthropic.Anthropic(base_url="https://nymbot.ai/api", api_key=os.environ["NYMBOT_API_KEY"])

message = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello"}],
)
print(message.content[0].text)
```

JavaScript

```
// npm install @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({ baseURL: "https://nymbot.ai/api", apiKey: process.env.NYMBOT_API_KEY });

const message = await client.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Hello" }],
});
console.log(message.content[0].text);
```

## Aider

Aider talks to any OpenAI-compatible server. Set the base URL and key, and put `openai/` in front of the model id so Aider knows which kind of server it is:

Shell

```
export OPENAI_API_BASE=https://nymbot.ai/api/v1
export OPENAI_API_KEY=sk-nymbot-...
aider --model openai/anthropic/claude-sonnet-5
```

Aider may warn that it does not know the model's context size or price. The warning is harmless; `--no-show-model-warnings` hides it.

## Open WebUI

In Open WebUI, open **Admin Panel**, then **Settings**, then **Connections**, and add an OpenAI API connection:

- URL: `https://nymbot.ai/api/v1`
- Key: your API key

Open WebUI reads the model list from the API, so every chat model appears in its picker. When running it in Docker, the same can be set with the environment variables `OPENAI_API_BASE_URL` and `OPENAI_API_KEY`.

Open WebUI keeps your conversations on its own server. Nymbot does not store them, but Open WebUI does, wherever you run it.

## Goose

Run `goose configure`, choose **Configure Providers**, pick **OpenAI**, and give it these values when asked. The same can be set in the environment:

Shell

```
export GOOSE_PROVIDER=openai
export OPENAI_HOST=https://nymbot.ai/api
export OPENAI_BASE_PATH=v1/chat/completions
export OPENAI_API_KEY=sk-nymbot-...
export GOOSE_MODEL=anthropic/claude-sonnet-5
goose session
```

Goose calls tools on almost every step, so pick a model with `capabilities.tools` in the model list; `nymbot/auto` refuses tools.

## Plain HTTP

Nothing needs an SDK. Every endpoint is JSON over HTTPS, apart from the file uploads, which are multipart forms. Any language or tool that can make an HTTP request can use the API:

- Send the key as `Authorization: Bearer sk-nymbot-…`.
- Send JSON bodies with `Content-Type: application/json`.
- For streaming, read the response as server-sent events; with cURL, add `-N` so it prints them as they arrive.
- Read the cost from the `X-Nymbot-Cost-Sats` header, and keep the `X-Request-Id` header if you might need to ask about a request.

cURL

```
curl -i https://nymbot.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $NYMBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "nymbot/auto", "messages": [{"role": "user", "content": "Hello"}]}'
```

Python

```
import os
import requests

res = requests.post(
    "https://nymbot.ai/api/v1/chat/completions",
    headers={"Authorization": "Bearer " + os.environ["NYMBOT_API_KEY"]},
    json={"model": "nymbot/auto", "messages": [{"role": "user", "content": "Hello"}]},
)
print(res.headers["X-Nymbot-Cost-Sats"], res.headers["X-Request-Id"])
print(res.json()["choices"][0]["message"]["content"])
```

JavaScript

```
const res = await fetch("https://nymbot.ai/api/v1/chat/completions", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.NYMBOT_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ model: "nymbot/auto", messages: [{ role: "user", content: "Hello" }] }),
});
console.log(res.headers.get("X-Nymbot-Cost-Sats"), res.headers.get("X-Request-Id"));
console.log((await res.json()).choices[0].message.content);
```
