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

# Messages API and Claude Code

> Run Claude Code and the Anthropic SDKs on Runcrate models with a Runcrate API key.

Claude Code and any Anthropic SDK run on Runcrate models: point them at `https://api.runcrate.ai` with your Runcrate API key and a Runcrate model id. Requests are billed, rate limited and routed exactly as [chat completions](/docs/models/chat-completions.md) are, at the same per-token prices.

## Claude Code

```bash theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
export ANTHROPIC_BASE_URL=https://api.runcrate.ai
export ANTHROPIC_AUTH_TOKEN=rc_live_YOUR_API_KEY
export ANTHROPIC_MODEL=deepseek-ai/DeepSeek-V4.1-Flash
export ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-ai/DeepSeek-V4.1-Flash
claude
```

`ANTHROPIC_MODEL` is required. Without it Claude Code sends its own model names (`claude-sonnet-4-5`, `claude-haiku-4-5`), which are not Runcrate model ids: each request answers 404, which Claude Code reports as an issue with the selected model (the API's message says to set `ANTHROPIC_MODEL`). `ANTHROPIC_DEFAULT_HAIKU_MODEL` is the model Claude Code uses for its background tasks, such as naming a session. Any text model in the [model catalog](/docs/models/model-catalog.md) works; one with tool calling is needed for Claude Code to read files and run commands.

`ANTHROPIC_API_KEY` works in place of `ANTHROPIC_AUTH_TOKEN`; Claude Code then sends the key as `x-api-key`.

Claude Code assumes a 200K-token context window for a model id it does not know, and compacts the conversation at that size, whatever the model's own window.

## Endpoint

```
POST https://api.runcrate.ai/v1/messages
POST https://api.runcrate.ai/v1/messages/count_tokens
```

The key goes in `x-api-key` (as the Anthropic SDKs send it) or `Authorization: Bearer`. `anthropic-version` and `anthropic-beta` headers are accepted.

## Basic usage

```bash curl theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
curl https://api.runcrate.ai/v1/messages \
  -H "x-api-key: $RUNCRATE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zai-org/GLM-5.3",
    "max_tokens": 512,
    "system": "Answer in one paragraph.",
    "messages": [
      {"role": "user", "content": "Explain quantum computing in simple terms"}
    ]
  }'
```

```python Python theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
import anthropic

client = anthropic.Anthropic(
    base_url="https://api.runcrate.ai",
    api_key="rc_live_YOUR_API_KEY",
)

message = client.messages.create(
    model="zai-org/GLM-5.3",
    max_tokens=512,
    messages=[{"role": "user", "content": "Explain quantum computing in simple terms"}],
)
print(message.content[0].text)
```

```javascript JavaScript theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  baseURL: "https://api.runcrate.ai",
  apiKey: "rc_live_YOUR_API_KEY",
});

const message = await client.messages.create({
  model: "zai-org/GLM-5.3",
  max_tokens: 512,
  messages: [{ role: "user", content: "Explain quantum computing in simple terms" }],
});
console.log(message.content[0].text);
```

## Streaming

With `stream: true` the answer arrives as Anthropic's events: `message_start`, `ping`, then each block as `content_block_start`, its deltas (`text_delta`, `thinking_delta`, `input_json_delta`) and `content_block_stop`, then `message_delta` with the stop reason and usage, and `message_stop`.

```javascript theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
const stream = client.messages.stream({
  model: "zai-org/GLM-5.3",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Write a poem" }],
});
stream.on("text", (text) => process.stdout.write(text));
const final = await stream.finalMessage();
```

## Tools

Custom tools take `name`, `description` and `input_schema`. A tool call comes back as a `tool_use` block with its `input` parsed; send the result back as a `tool_result` block (`is_error: true` marks a failure).

```javascript theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
const tools = [{
  name: "get_weather",
  description: "Weather for a city",
  input_schema: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
}];
const ask = { model: "zai-org/GLM-5.3", max_tokens: 512, tools, messages: [{ role: "user", content: "Weather in Paris?" }] };

const first = await client.messages.create(ask);
const call = first.content.find((block) => block.type === "tool_use");

const answer = await client.messages.create({
  ...ask,
  messages: [
    ...ask.messages,
    { role: "assistant", content: first.content },
    { role: "user", content: [{ type: "tool_result", tool_use_id: call.id, content: "18C and sunny" }] },
  ],
});
```

## Thinking

`thinking: {type: "enabled", budget_tokens}` sets the model's reasoning effort: a budget under 4,096 is low, under 16,384 medium, and anything larger high. `output_config.effort` (`low`, `medium`, `high`, `max`) takes precedence. A reasoning model's reasoning comes back as `thinking` blocks before the answer; send them back or not, they are not passed to the model again.

## What is supported

| Messages API | On Runcrate |
| - | - |
| `system` (string or text blocks, with `cache_control`) | Supported. A `system` message inside `messages` joins the system prompt. |
| `text`, `image` (base64 or url), `document` (text source) blocks | Supported. Images need a vision model. |
| `tool_use`, `tool_result` (string or blocks, `is_error`) | Supported |
| `tools` with `input_schema` | Supported |
| `tool_choice`: `auto`, `any`, `tool`, `none`, `disable_parallel_tool_use` | Supported, where the model supports forcing a tool |
| `max_tokens`, `temperature`, `top_p`, `top_k`, `stop_sequences`, `stream` | Supported |
| `thinking`, `output_config.effort` | Mapped to reasoning effort |
| `metadata` | Accepted; never sent to the model |
| Stop reasons | `end_turn`, `max_tokens`, `stop_sequence`, `tool_use`, `refusal` |
| Usage | `input_tokens`, `output_tokens`, `cache_read_input_tokens`, `cache_creation_input_tokens` |
| Server tools (web search, code execution, ...) | Not supported: 400 |
| PDF and file documents | Not supported: 400. Send the text as a text block. |

`stop_sequence` is named when the model reports which sequence it stopped on; otherwise a stop is `end_turn`.

## Counting tokens

`POST /v1/messages/count_tokens` takes the same body and answers `{"input_tokens": N}`: an estimate at four characters a token, 1,000 tokens an image and four tokens a message. It is not billed. Each model counts its own way, so the usage on the response is the exact figure.

## Errors

Errors use Anthropic's shape, with Runcrate's code named in the message and kept as `code`. Any other detail of the error (a free allowance's `free_tier` reset times and what remains) is kept beside it, and a `Retry-After` header says when to retry a 429 or 503.

```json theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "type": "error",
  "error": {
    "type": "not_found_error",
    "message": "'claude-sonnet-4-5' is not a Runcrate model id. Claude Code sends its own model names unless you choose one: set ANTHROPIC_MODEL ...",
    "code": "model_not_found"
  },
  "request_id": "req_..."
}
```

| Status | `error.type` | Examples of `code` |
| - | - | - |
| 400 | `invalid_request_error` | `unsupported_parameter`, `tool_choice_unsupported` |
| 401 | `authentication_error` | `missing_api_key`, `invalid_api_key` |
| 402 | `billing_error` | `insufficient_credits`, `budget_exceeded`, `github_link_required` |
| 404 | `not_found_error` | `model_not_found` |
| 413 | `request_too_large` | `payload_too_large`, `free_prompt_too_large` |
| 429 | `rate_limit_error` | `rate_limit_exceeded`, `free_allowance_used`, `free_tier_paused` |
| 503 | `overloaded_error` | `free_capacity_busy`, `free_tier_paused`, `model_unavailable` |
| 5xx | `api_error` | |


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