# Claude Sonnet 4.6 — /chat/completions

Claude Sonnet 4.6 chat API (Anthropic), balancing capability and speed for everyday chat, coding and agents, with a context window of about 200K tokens, image input and tool calls. Served on the OpenAI-compatible API and the native Anthropic API.

**Endpoint:** `POST https://nezhagate.com/v1/chat/completions`

## Quick start

Two steps: send a request, then stream the answer. Replace YOUR_API_KEY with your key and the commands below run as they are. The endpoint matches OpenAI Chat Completions, so existing OpenAI code only needs a new `base_url` and `model`.

**Step 1 · Send a request**

```
curl https://nezhagate.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "claude-sonnet-4-6", "messages": [{"role": "user", "content": "Explain photosynthesis in one sentence"}]}'
```

Response (HTTP 200)

```
{
  "id": "chatcmpl-7f3c9a1e2b4d6f8a",
  "object": "chat.completion",
  "created": 1791281400,
  "model": "claude-sonnet-4-6",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Photosynthesis is how plants use sunlight to turn carbon dioxide and water into sugar, releasing oxygen."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 18,
    "completion_tokens": 42,
    "total_tokens": 60
  }
}
```

`message.content` is the answer and `usage` holds the tokens this request is billed for; for how prompt caching bills, see Prompt caching below.

**Step 2 · Stream the answer (recommended)**

```
curl -N https://nezhagate.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "claude-sonnet-4-6", "stream": true, "messages": [{"role": "user", "content": "Explain photosynthesis in one sentence"}]}'
```

The stream is standard SSE: each chunk carries text in `choices[0].delta.content`, the last chunk carries `usage`, and it ends with `data: [DONE]`. Stream long answers and thinking models: a non-streaming request is cut off after about 10 minutes.

**Python (OpenAI SDK)**

```
from openai import OpenAI

client = OpenAI(base_url="https://nezhagate.com/v1", api_key="YOUR_API_KEY")

# 1. One request, one answer
resp = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": "Explain photosynthesis in one sentence"}],
)
print(resp.choices[0].message.content)

# 2. Streaming
stream = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": "Explain photosynthesis in one sentence"}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
```

**Or use the Anthropic SDK (native API)**

```
import anthropic

client = anthropic.Anthropic(base_url="https://nezhagate.com/anthropic", api_key="YOUR_API_KEY")
msg = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Explain photosynthesis in one sentence"}],
)
print(next(b.text for b in msg.content if b.type == "text"))
```

Point the Anthropic SDK `base_url` at `/anthropic` and the SDK adds `/v1/messages` itself; the key is the same one.

## Authentication
```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

## At a glance

| Context window | ~200K tokens |
| --- | --- |
| Input | Text, images |
| Thinking | Off by default; can be turned on, with summaries, on the native API |
| Tool calling | Yes (OpenAI `tools` format) |
| Web search | Yes (`web_search: true`; the search itself is free) |
| Endpoints | /v1/chat/completions · /anthropic/v1/messages |
| Streaming | Yes (`stream: true`, SSE) |
| Billing | Per token: input, output (thinking included) and cache hits each have a price; failures are free |

## Request parameters

The JSON body of `POST /v1/chat/completions`, the same as OpenAI Chat Completions. The common parameters, and what each one does on this model:

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| model | string | Yes | The model id: `claude-sonnet-4-6`. |
| messages | array | Yes | The conversation: each message has a `role` (`system`, `user`, `assistant` or `tool`) and `content`. `content` is a string, or an array of `text` and `image_url` parts (image input, see below). For multi-turn chat, send the whole history in order. |
| stream | boolean | No | `true` streams the answer as SSE, with `usage` in the last chunk. Defaults to `false`. |
| max_tokens | integer | No | The output cap, thinking tokens included. Set too low, the model can spend it all before writing the answer: the gateway then returns 400 `max_tokens_too_small` and bills nothing (512 or more is recommended). On some models a value that is too small is raised to a floor that leaves room for a full answer, still billed on actual usage only. |
| temperature / top_p | number | No | Sampling parameters, passed to the model as sent (`temperature` from 0 to 2). Thinking models may not follow them. |
| tools / tool_choice | array / string | No | Function calling in the standard OpenAI format. When the model calls a function it returns `finish_reason: "tool_calls"` with `tool_calls`; see below. |
| web_search | boolean | No | `true` runs a web search first and hands the results to the model as reference; see below. |

Other OpenAI parameters (such as `stop`, `seed`, `user`) are passed to the model as sent; whether they take effect is up to the model.

## Price

Billed per token at the live rates below (per million tokens). Tokens written to the prompt cache bill at the cache-write rate and cache hits at the cache-hit rate. Only successful requests are billed, on actual usage. 1 credit = $0.005.

| Type | USD / 1M tokens | Credits / 1M tokens |
| --- | --- | --- |
| Input | $1.50 | 300 |
| Output (thinking included) | $7.50 | 1500 |
| Cache hit | $0.15 | 30 |
| Cache write | $1.875 | 375 |

For example, a request with 2,000 input tokens and 1,000 output tokens (thinking included) costs about $0.0105.

## Thinking

Answers directly by default, without thinking. To turn thinking on and get a summary, use the native `/anthropic` API with `"thinking": {"type": "adaptive", "display": "summarized"}` in the request.

Thinking tokens count as output tokens and bill at the output price.

## Prompt caching

Long prompts may be written to the cache automatically even without a marker (billed at the cache-write rate), and a repeated prefix then bills at the cache-hit rate. On the native `/anthropic` API you can place the cache yourself with `"cache_control": {"type": "ephemeral"}` on the system prompt or a message block; Claude Code does this for you.

The native API reports cached tokens in `usage.cache_creation_input_tokens` (written) and `usage.cache_read_input_tokens` (hits), and the bill uses those numbers.

## Image input

Put an `image_url` part in the `content` array. A base64 data URI (`data:image/png;base64,...`) is the recommended form and can always be read; a public image link has to be downloaded first, and occasionally it cannot be fetched or times out. Images count as input tokens by the model vendor rules.

```
curl https://nezhagate.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "claude-sonnet-4-6", "messages": [{"role": "user", "content": [{"type": "text", "text": "What is in this image"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgo..."}}]}]}'
```

## Tool calling (function calling)

Describe your functions in `tools` with JSON Schema. When the model decides to call one, it returns `finish_reason: "tool_calls"` with the function name and arguments (a JSON string) in `message.tool_calls`.

```
curl https://nezhagate.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "claude-sonnet-4-6", "messages": [{"role": "user", "content": "What is the weather in Shanghai right now"}], "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Current weather of a city", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}]}'
```

Run the function, append the result as a `role: "tool"` message with the matching `tool_call_id`, and send the conversation again; the model then gives its final answer. `tool_choice` takes `auto`, `none`, `required` or a named function.

## Web search

Add `"web_search": true` to the request (or a `{"type": "web_search"}` entry in `tools`): the gateway first searches the live web based on the conversation, hands up to 5 results with their links to the model as reference, and the model answers and cites its sources. The search itself is free; the reference text bills as input tokens.

```
curl https://nezhagate.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "claude-sonnet-4-6", "web_search": true, "messages": [{"role": "user", "content": "What are the main tech news stories today"}]}'
```

## The native Anthropic API (/anthropic)

- The endpoint is `https://nezhagate.com/anthropic/v1/messages`, and requests and responses use the Anthropic Messages format: streaming events, tool calls (`tool_use` / `tool_result`) and image blocks all match Anthropic, so the Anthropic SDK and Claude Code work as they are.

- Authenticate with `x-api-key: YOUR_API_KEY` (`Authorization: Bearer` works too); the key is the same one as on `/v1`. The `anthropic-version` and `anthropic-beta` headers are passed on as sent, and `max_tokens` is required.

- Only Claude models are served here; any other model returns 404 `not_found_error`. Also available: `POST /anthropic/v1/messages/count_tokens` (counts tokens, free) and `GET /anthropic/v1/models`.

- Turn thinking on with `"thinking": {"type": "adaptive", "display": "summarized"}` (with thinking on, `temperature` must be 1).

- Errors come back in the Anthropic format, `{"type": "error", "error": {"type": ..., "message": ...}}`; too little balance returns 402 `billing_error`.

- Web search is off by default on this endpoint (so it never changes the prefix Claude Code repeats on every turn and breaks its cache); turn it on with the `x-web-search: 1` header.

```
curl https://nezhagate.com/anthropic/v1/messages \
  -H "x-api-key: YOUR_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{"model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Explain photosynthesis in one sentence"}]}'
```

[Use it in Claude Code →](https://nezhagate.com/en/docs/integrations/claude-code)

## Reliability and automatic retries

- When a request errors, times out or hits a capacity limit, the gateway retries it automatically before anything is returned, so you need not retry and are never billed twice.

- Once a stream starts sending, it is no longer retried automatically: if it breaks partway, you are billed for the usage produced up to that point (see [Billing and refunds](https://nezhagate.com/en/docs/guide/billing)).

- When the model is at capacity the gateway returns 429 with `Retry-After`; wait that many seconds and retry. Failed requests are not billed. Each model status is on [the status page](https://nezhagate.com/en/status).

## Errors and refunds

Errors come back as an `error` object (`message`, `type`, `code`, `param`); decide by the HTTP status and `code`, never by matching the text of `message`. Failed requests are not billed; the full list is in [Error codes](https://nezhagate.com/en/docs/guide/errors).

| HTTP | code | Cause | What to do |
| --- | --- | --- | --- |
| 400 | invalid_messages / invalid_role / invalid_max_tokens | A chat request's `messages` is missing, empty or malformed, a `role` is not one of the allowed values, or `max_tokens` is not a positive integer. `param` names the field. | Fix the field named in `param`. |
| 400 | model_not_found | The model id is misspelled, `model` is missing, or the model cannot be used on this endpoint (a chat model on the image endpoint, for example). | Use a model id from `GET /v1/models` or the [API reference](https://nezhagate.com/en/docs/api). |
| 400 | max_tokens_too_small | A reasoning model spent all of `max_tokens` thinking and wrote no answer (non-streaming chat). | Raise `max_tokens` and retry. |
| 4xx | invalid_request | The model provider rejected the request (an over-long context or an unsupported parameter, for example); the status and `message` are passed on as the provider sent them, and `code` is the provider's value. | Change the request as `message` says. |
| 401 | invalid_api_key | The key does not exist, was deleted or is disabled, or the account is disabled. | Check the key on the API Keys page of the console. |
| 402 | insufficient_quota | Not enough balance to hold this request. The gateway holds the highest possible cost of the request first, so a burst of concurrent calls can trigger it too. | After a burst, wait a few seconds and retry; otherwise top up. |
| 403 | permission_denied | The model is not on the key's model allowlist. | Use a model on the allowlist, or edit the allowlist in the console. |
| 429 | rate_limit_exceeded | Over the requests-per-minute limit set on this key (counted over the last 60 seconds). No `Retry-After` header. | Wait a few seconds and retry, or raise the key's limit in the console. |
| 429 | rate_limit_exceeded | The model is at capacity and the request was not sent. Chat endpoints only: image and video jobs are never refused but queue instead. The OpenAI-compatible endpoints add `Retry-After`. | Wait the seconds in `Retry-After`, then retry. |
| 502 | upstream_timeout | The upstream timed out, including taking too long to send the first token. | Retry; use `stream: true` for long answers. |
| 502 | upstream_unavailable | The upstream failed or is unavailable for the moment. | Retry shortly. |
| 502 | upstream_busy | The model provider is rate-limiting. A provider's 429 is never passed on as a 429; you get this 502 instead. | Retry shortly. |
| 502 | empty_completion | The model provider returned empty content. This is not a judgement of your content. | Retry. |
| 503 | model_maintenance | The model is under maintenance and takes no requests; no other model answers in its place meanwhile. | Retry when maintenance ends; see [Status](https://nezhagate.com/en/status). |

If a stream fails after it started sending, the status code stays as it was: the error arrives as a data line carrying `error`, or the stream just ends (without `data: [DONE]`). Treat it as interrupted.
