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

# Messages API

> Call Together AI models with the Anthropic Messages API through the Anthropic Python and TypeScript SDKs.

Together AI implements the Anthropic [Messages API](https://docs.anthropic.com/en/api/messages) at `POST /v1/messages`. If your app is built on the Anthropic SDKs, change the API key, base URL, and model to run it on open-source models hosted by Together.

## Set up the client

Install the Anthropic SDK for your language:

```bash Python theme={null}
pip install anthropic
```

```bash TypeScript theme={null}
npm install @anthropic-ai/sdk
```

Set `api_key` to your [Together API key](https://docs.together.ai/docs/api-keys-authentication.md) and `base_url` to `https://api.together.ai`, then send a message:

```python Python theme={null}
import os
import anthropic

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

message = client.messages.create(
    model="zai-org/GLM-5.3",
    max_tokens=4096,
    messages=[{"role": "user", "content": "Hello!"}],
)

print(message.content[0].text)
```

```typescript TypeScript theme={null}
import Anthropic from "@anthropic-ai/sdk";

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

const message = await client.messages.create({
  model: "zai-org/GLM-5.3",
  max_tokens: 4096,
  messages: [{ role: "user", content: "Hello!" }],
});

console.log(message.content[0].type === "text" ? message.content[0].text : "");
```

```bash cURL theme={null}
curl https://api.together.ai/v1/messages \
  -H "x-api-key: $TOGETHER_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zai-org/GLM-5.3",
    "max_tokens": 4096,
    "messages": [{"role": "user", "content": "Hello!"}]
  }'
```

The Messages API accepts your API key in either an `x-api-key` header or an `Authorization: Bearer` header. If you send both, the `Authorization` header takes precedence.

## Supported models

Supported models differ between serverless inference and [dedicated model inference](https://docs.together.ai/docs/dedicated-endpoints/overview.md):

| Model | API model string | Serverless inference | Dedicated model inference |
| - | - | :-: | :-: |
| GLM-5.2 | `zai-org/GLM-5.2` | ✓ | ✓ |
| GLM-5.3 | `zai-org/GLM-5.3` | ✓ | - |
| GLM-5.3 Flash | `zai-org/GLM-5.3-Flash` | - | ✓ |

To [send requests to dedicated model inference](https://docs.together.ai/docs/dedicated-endpoints/requests.md), set the base URL to `https://api-inference.together.ai` and pass your endpoint string as `model`.

Other serverless models can accept Messages API requests, but they aren't supported and may return malformed output. To use the Messages API with an existing [dedicated endpoint](https://docs.together.ai/docs/dedicated-endpoints/overview.md) or a model that isn't supported yet, [contact sales](https://www.together.ai/contact-sales).

## Endpoint support

| Anthropic SDK method | Endpoint | Status |
| - | - | - |
| `messages.create`, `messages.stream` | `POST /v1/messages` | Supported |
| `messages.count_tokens` | `POST /v1/messages/count_tokens` | Supported |
| `messages.batches.*` | `/v1/messages/batches` | Not supported. Use the Together [batch API](https://docs.together.ai/docs/inference/batch/overview.md). |
| `models.list` | `GET /v1/models` | Not supported. `/v1/models` returns Together's own response format, so the Anthropic SDKs raise an error or return an empty list. Browse [serverless models](https://docs.together.ai/docs/serverless/models.md) instead. |

## Stream responses

To receive server-sent events as the model generates them, set `stream` to `true`. The SDKs' `messages.stream` helper sets it for you and collects the text:

```python Python theme={null}
with client.messages.stream(
    model="zai-org/GLM-5.3",
    max_tokens=4096,
    messages=[{"role": "user", "content": "Write a haiku about the ocean."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
```

```typescript TypeScript theme={null}
const stream = client.messages.stream({
  model: "zai-org/GLM-5.3",
  max_tokens: 4096,
  messages: [{ role: "user", content: "Write a haiku about the ocean." }],
});

stream.on("text", (text) => process.stdout.write(text));
await stream.finalMessage();
```

```bash cURL theme={null}
curl https://api.together.ai/v1/messages \
  -H "x-api-key: $TOGETHER_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zai-org/GLM-5.3",
    "max_tokens": 4096,
    "stream": true,
    "messages": [{"role": "user", "content": "Write a haiku about the ocean."}]
  }'
```

The stream uses Anthropic's event types: `message_start`, `content_block_start`, `content_block_delta`, `content_block_stop`, `message_delta`, and `message_stop`. Deltas arrive as `text_delta`, `thinking_delta`, or `input_json_delta`. The stream ends after `message_stop`, with no `[DONE]` line.

In `message_start`, `input_tokens` counts the whole prompt, including cached tokens, and `output_tokens` is `0`. Read the final token counts from the `message_delta` event, or from the final message that the SDK helpers return.

> [!WARNING]
>
> GLM-5.3 reasons before it answers, even when thinking is off, and that reasoning counts toward `max_tokens`. A short answer can take about 1,000 tokens of reasoning. If the reasoning uses up `max_tokens`, the stream still ends normally: the text block is empty, `message_delta` reports a `stop_reason` of `max_tokens`, and the SDK helpers raise no error. Set `max_tokens` well above the length of the answer you expect, and check `stop_reason` before you use the text.

## Use extended thinking

Set `thinking.type` to `enabled` to get the model's reasoning back as `thinking` blocks ahead of the answer:

```python Python theme={null}
message = client.messages.create(
    model="zai-org/GLM-5.3",
    max_tokens=8192,
    thinking={"type": "enabled", "budget_tokens": 4096},
    messages=[
        {
            "role": "user",
            "content": "A bat and a ball cost $1.10 in total. The bat "
            "costs $1.00 more than the ball. How much is the ball?",
        }
    ],
)

for block in message.content:
    if block.type == "thinking":
        print("Thinking:", block.thinking)
    elif block.type == "text":
        print("Answer:", block.text)
```

```typescript TypeScript theme={null}
const message = await client.messages.create({
  model: "zai-org/GLM-5.3",
  max_tokens: 8192,
  thinking: { type: "enabled", budget_tokens: 4096 },
  messages: [
    {
      role: "user",
      content:
        "A bat and a ball cost $1.10 in total. The bat costs $1.00 more than the ball. How much is the ball?",
    },
  ],
});

for (const block of message.content) {
  if (block.type === "thinking") console.log("Thinking:", block.thinking);
  if (block.type === "text") console.log("Answer:", block.text);
}
```

Thinking on Together differs from Anthropic's API:

* **`thinking.type`:** `enabled` and `adaptive` return `thinking` blocks. With `disabled`, or with no `thinking` field, the response contains only the answer.
* **`budget_tokens`:** It's required with `enabled`, but Together doesn't enforce it as a limit. The only limit is `max_tokens`, which covers reasoning and the answer combined.
* **Hidden reasoning:** Reasoning models can reason even when they return no `thinking` block, and GLM-5.3 reasons even when `thinking.type` is `disabled`. Those tokens count toward `max_tokens` and `output_tokens`. If a response has empty text and a `stop_reason` of `max_tokens`, raise `max_tokens`.
* **`output_config.effort`:** Together passes the level to the model's chat template, so its effect depends on the model.
* **Signatures:** `thinking` blocks have an empty `signature`. To keep reasoning in a multi-turn tool loop, pass the blocks back unchanged in the assistant turn.

For how each reasoning model behaves on Together, see [Reasoning](https://docs.together.ai/docs/inference/chat/reasoning.md).

## Use tools

Define tools with `name`, `description`, and `input_schema`. When the model calls a tool, the response has `stop_reason` set to `tool_use` and one `tool_use` block per call. Run each tool, then send the results back in a single `user` turn with one `tool_result` block per call:

```python Python theme={null}
import json

tools = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "input_schema": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    }
]


def get_weather(city):
    return json.dumps({"city": city, "temperature_c": 18})


messages = [
    {"role": "user", "content": "What's the weather in Paris and Tokyo?"}
]

response = client.messages.create(
    model="zai-org/GLM-5.3",
    max_tokens=4096,
    tools=tools,
    messages=messages,
)

while response.stop_reason == "tool_use":
    messages.append({"role": "assistant", "content": response.content})
    results = [
        {
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": get_weather(**block.input),
        }
        for block in response.content
        if block.type == "tool_use"
    ]
    messages.append({"role": "user", "content": results})
    response = client.messages.create(
        model="zai-org/GLM-5.3",
        max_tokens=4096,
        tools=tools,
        messages=messages,
    )

print(response.content[-1].text)
```

```typescript TypeScript theme={null}
import Anthropic from "@anthropic-ai/sdk";

const tools: Anthropic.Tool[] = [
  {
    name: "get_weather",
    description: "Get the current weather for a city.",
    input_schema: {
      type: "object",
      properties: { city: { type: "string" } },
      required: ["city"],
    },
  },
];

function getWeather(city: string): string {
  return JSON.stringify({ city, temperature_c: 18 });
}

const messages: Anthropic.MessageParam[] = [
  { role: "user", content: "What's the weather in Paris and Tokyo?" },
];

let response = await client.messages.create({
  model: "zai-org/GLM-5.3",
  max_tokens: 4096,
  tools,
  messages,
});

while (response.stop_reason === "tool_use") {
  messages.push({ role: "assistant", content: response.content });
  const results: Anthropic.ToolResultBlockParam[] = [];
  for (const block of response.content) {
    if (block.type === "tool_use") {
      const { city } = block.input as { city: string };
      results.push({
        type: "tool_result",
        tool_use_id: block.id,
        content: getWeather(city),
      });
    }
  }
  messages.push({ role: "user", content: results });
  response = await client.messages.create({
    model: "zai-org/GLM-5.3",
    max_tokens: 4096,
    tools,
    messages,
  });
}

const last = response.content[response.content.length - 1];
console.log(last.type === "text" ? last.text : "");
```

`tool_choice` accepts `auto`, `any`, `tool` (with a `name`), and `none`. To limit the model to one tool call per turn, set `tool_choice.disable_parallel_tool_use` to `true`.

Together doesn't run tools itself, so every tool runs in your app. Define custom tools, or use Anthropic's `bash`, `text_editor`, and `memory` client tools.

A request with a server tool (`web_search`, `web_fetch`, or `code_execution`) or the `computer` tool returns a `400`. The one exception is a `web_search` tool that you send alongside other tools and that `tool_choice` doesn't force. Together removes that tool from the request. Together also ignores `mcp_servers`.

## Token usage and caching

`usage` reports `input_tokens` and `output_tokens`. Thinking tokens, including hidden reasoning, count as output tokens.

Together caches repeated prompt prefixes automatically, so `cache_control` has no effect. `cache_read_input_tokens` counts the prompt tokens served from the cache, and `input_tokens` counts the rest. `cache_creation_input_tokens` is always `0`.

Together bills Messages API requests the same way as chat completions: per token at each model's [serverless prices](https://docs.together.ai/docs/serverless/models.md), or per GPU-minute on [dedicated model inference](https://docs.together.ai/docs/dedicated-endpoints/pricing.md).

## Other differences from Anthropic's API

* **`model`:** Use a Together model string. Claude model names, such as `claude-sonnet-5-5`, return a `404`.
* **`anthropic-beta` header:** Together accepts any value, but a beta header doesn't turn on Anthropic beta features. The one exception is `interleaved-thinking-2025-05-14`, which lets `budget_tokens` exceed `max_tokens`.
* **Response `id`:** It uses Together's format rather than a `msg_` prefix. Don't parse it.
* **Structured outputs:** `output_config.format` returns a `400`. Ask for JSON in the prompt, or use [structured outputs](https://docs.together.ai/docs/inference/chat/structured-outputs.md) with chat completions.
* **`metadata` and `service_tier`:** Neither changes the response. `service_tier` accepts only `auto` and `standard_only`.
* **Images and documents:** GLM-5.3 and GLM-5.2 don't accept images, so a request with an image block returns a `400`. PDF `document` blocks also return a `400`, so send documents as text.

## Errors

Errors use the same JSON format as Anthropic's API:

```json theme={null}
{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key provided. You can find your API key at https://api.together.ai/settings/api-keys."
  },
  "request_id": "<request_id>"
}
```

Each HTTP status maps to an `error.type`, and the Anthropic SDKs raise their usual exception class for each status:

| HTTP status | `error.type` |
| - | - |
| 400 | `invalid_request_error` |
| 401 | `authentication_error` |
| 402 | `billing_error` |
| 403 | `permission_error` |
| 404 | `not_found_error` |
| 413 | `request_too_large` |
| 429 | `rate_limit_error` |
| 500 | `api_error` |
| 503 | `overloaded_error` |
| 504 | `timeout_error` |

Together usually returns `503` for `overloaded_error`, where Anthropic's API returns `529`. The Anthropic Python SDK raises `InternalServerError` for a `503` rather than `OverloadedError`, so catch both if you handle overloads separately.

A failure after a stream starts arrives as an `error` event in the stream.

A few gateway-level failures, such as a `502` when Together can't connect to the model's servers, return Together's OpenAI-style error body instead. To handle both formats, branch on the HTTP status rather than the error body. For causes and fixes by status code, see [Error codes](https://docs.together.ai/docs/error-codes.md).

## Next steps

> [card] [Responses API](https://docs.together.ai/docs/inference/responses-api.md)
>
> Call Together models with the OpenAI Responses API.

> [card] [Together Link](https://docs.together.ai/docs/togetherlink.md)
>
> Run Claude Code and other coding agents on Together models.


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