Skip to main content

Anthropic compatibility

POST /v1/messages implements the Anthropic Messages API shape, so Anthropic SDKs and the tools built on them, most notably Claude Code, run against MindsHub with a base-URL and auth-token change. Model names resolve through the same catalog as everywhere else, so you can point an Anthropic client at any MindsHub model: gpt, kimi, gemini-flash, not just Claude.

Claude Code

export ANTHROPIC_BASE_URL="https://api.mindshub.ai"
export ANTHROPIC_AUTH_TOKEN="$MINDSHUB_API_KEY"
claude

Three rules:

  • Use ANTHROPIC_AUTH_TOKEN, not ANTHROPIC_API_KEY. The auth-token variable sends Authorization: Bearer …, which is the only header MindsHub accepts. ANTHROPIC_API_KEY sends x-api-key, which is rejected with a 401 before the request reaches the API.

  • The base URL is the host only, no /v1. The client adds the /v1/messages path itself.

  • Optional: give MindsHub its own config directory. ANTHROPIC_AUTH_TOKEN outranks a stored claude.ai login, so this isn't required for authentication. It keeps MindsHub sessions and settings separate from your regular profile, and it is the fix if a startup 401 Authorization Required shows Claude Code preferring its stored login anyway (reported on some 2.1.x setups):

    export CLAUDE_CONFIG_DIR="$HOME/.claude-mindshub"

Three cost notes for Claude Code specifically:

  • Its default model maps to the opus alias, one of the most expensive in the catalog. Pick a cheaper model in Claude Code (/model) if cost matters.
  • Claude Code makes heavy use of prompt caching; cache writes are billable and never draw included tokens (see Billing).
  • The cost figure Claude Code displays is its own estimate and doesn't reflect MindsHub billing, especially on non-Claude aliases. Your usage summary is authoritative.

Anthropic SDKs

Install with pip install anthropic (Python) or npm install @anthropic-ai/sdk (TypeScript).

import os
import anthropic

client = anthropic.Anthropic(
base_url="https://api.mindshub.ai",
auth_token=os.environ["MINDSHUB_API_KEY"], # auth_token, not api_key
)

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

The anthropic-version header your SDK sends is accepted and ignored.

Model names

The model field accepts two forms:

  • A MindsHub alias: sonnet, opus, gpt, kimi, any alias from Models. This is how you run Claude Code on a non-Claude model.
  • A real Claude model name: anything starting with claude that contains a family name (opus, sonnet, haiku, or fable; a substring match, checked in that order) maps to the corresponding alias. claude-sonnet-5, claude-sonnet-4-6-20250929, and claude-opus-5[1m] all work; version and date segments are ignored, and the family decides the alias. This is what lets Claude Code's own model picker work unmodified.

A claude… name with no recognizable family word returns 404 model_not_found; there is no silent guessing.

The response's model field echoes back exactly the string you sent (unlike /v1/chat/completions, which reports the resolved ID).

What's supported

  • Text and multi-turn conversations; system as a string or content blocks.
  • Images (image blocks with base64 or url sources).
  • Client tool use: tools with input_schema, tool_use / tool_result blocks round-trip, is_error preserved.
  • tool_choice: auto, any, none, and {"type": "tool", "name": ...}.
  • Anthropic's hosted web tools: web_search* and web_fetch* tool types map onto the platform's web search where the target model supports it (see Chat completions → Built-in web search).
  • Prompt caching: cache_control breakpoints are honored on Claude-family targets. Cache usage is reported in the response's usage.
  • Streaming (see below), and POST /v1/messages/count_tokens.

What's not supported

These request features are accepted but ignored: the request succeeds and the feature silently doesn't happen (the same accepted-and-ignored rule as the OpenAI-compatible endpoints).

FeatureBehavior
thinking (extended thinking)Ignored as a request control: thinking blocks in message history are stripped, and you can't set a budget. Models that think by default still return a thinking block in content (see the response-shape notes below); reasoning bills as output tokens either way.
metadataIgnored.
Server tools (computer_*, bash_*, text_editor_*, code_execution, memory)Dropped from the request.
mcp_servers (server-side MCP)Ignored. (Claude Code's own MCP support is client-side and unaffected.)
Files API references ("source": {"type": "file"})Unsupported, and behavior is target-dependent: some providers reject them, others ignore them. Use base64 or url image sources.
Message Batches APINot implemented. POST /v1/messages/batches returns a plain 404.
Citations / search-result blocksNot returned. Web-search results reach you only as text the model wrote.

Sampling parameters (temperature, top_p, top_k, stop_sequences) follow the same per-model policy as the OpenAI endpoints: forwarded where the target model accepts them, dropped and named in X-MindsHub-Dropped-Params where it doesn't. On the current Claude 5 models (opus, sonnet, fable) that means temperature, top_p, and top_k are dropped while stop_sequences work; haiku forwards everything. One quirk: a stop triggered by your sequence reports stop_reason: "end_turn", not stop_sequence, so the truncation is silent.

Unknown top-level request fields follow the same rule today: accepted and ignored, not a validation error. Unknown nested content, say an unrecognized block type inside messages, can be forwarded to the provider and fail there instead.

Response shape

{
"id": "msg_5084f82319564e568c6e82fd8520c125",
"type": "message",
"role": "assistant",
"model": "sonnet",
"content": [{"type": "text", "text": "Hello!"}],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 11,
"output_tokens": 4,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0
}
}
  • content blocks are text, tool_use, and, on models that think by default (opus, fable), a leading thinking block, usually with empty or withheld content plus a signature. Don't assume the first block is your text.
  • stop_reason is one of end_turn, tool_use, max_tokens, refusal.
  • All four usage fields are always present. As in Anthropic's own API, input_tokens excludes cached tokens: the three input fields partition the prompt.

Streaming

"stream": true produces the standard Anthropic event sequence:

event: message_start → event: ping → event: content_block_start
→ event: content_block_delta … → event: content_block_stop
→ event: message_delta → event: message_stop

Differences from Anthropic's native stream:

  • ping is sent once, right after message_start, not periodically. Don't use pings as a liveness signal.
  • On non-Claude models, message_start reports input_tokens: 0. The real counts arrive in the final message_delta. Token-tracking UIs will undercount mid-stream on non-Claude models.
  • There is no error event, and no reliable failure signal. A mid-generation failure can end the stream early without message_stop, but it can also surface as a normal-looking completion: an ordinary message_delta with stop_reason: "end_turn" followed by message_stop, with truncated or empty content. If your application must detect failed generations, sanity-check the output; don't rely on the event sequence.
  • If the request fails before generation starts, you get a JSON error response (see below), not an SSE stream.

Counting tokens

curl https://api.mindshub.ai/v1/messages/count_tokens \
-H "Authorization: Bearer $MINDSHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "sonnet", "messages": [{"role": "user", "content": "Hello"}]}'
{"input_tokens": 8}

Counts are estimates for every model: a single Claude tokenizer is used regardless of the model you pass, so counts are close for Claude-family targets and rougher for everything else. (Anthropic documents its own token counts as estimates too.) Budget with them; don't reconcile billing with them. count_tokens calls are free and unmetered.

Errors

Errors on this endpoint use the Anthropic envelope:

{"type": "error", "error": {"type": "not_found_error", "message": "The model 'foo' does not exist or you do not have access to it."}}

The status codes and their meanings are the same as everywhere else; see Errors. Three quirks specific to this endpoint:

  • A 402 (empty wallet) arrives typed invalid_request_error, and a 503 typed api_error. Read the HTTP status, not just the type string.
  • On errors, none of the X-MindsHub-* denial headers or Retry-After are currently sent on this endpoint (a fix is rolling out). Back off on any 429 without waiting for a hint. This applies to error responses only; the param-adaptation headers are reported normally on successful requests.
  • A malformed request body returns 400 (message prefixed Invalid request body:), not the 422 the OpenAI-compatible endpoints use.

Behavior on this page verified against the platform in July 2026.