Anthropic Messages API ("Anthropic Skin")

> For Claude Code / Anthropic SDK users: see the step-by-step Claude Code Setup guide.

Surplus speaks Anthropic's Messages API natively, so tools built for Anthropic — Claude Code and the Anthropic Agent SDK — can route through the marketplace with nothing but environment variables. No local proxy, no client-side translation.

This is the same idea as OpenRouter's "Anthropic skin": point the Anthropic client's base URL at Surplus, authenticate with a Surplus buyer key, and every request is routed to the cheapest healthy seller and settled on-chain like any other marketplace call.

It is model-agnostic. Routing is by the model string, not by family — so you can run Claude Code on Claude or GPT, GLM, Llama, Gemini, DeepSeek, or any other catalog model, all through the Anthropic wire.

Base URL

https://api.surplusintelligence.ai/anthropic

This is what you set as ANTHROPIC_BASE_URL. The Anthropic client appends /v1/messages (and the other paths below) to it. The skin lives under a dedicated /anthropic prefix so it never collides with the OpenAI-compatible surface at /v1/....

Authentication

Use a Surplus buyer API key (inf_…), sent either way the Anthropic ecosystem sends it:

HeaderSet viaNotes
Authorization: Bearer inf_xxxANTHROPIC_AUTH_TOKENWhat Claude Code uses
x-api-key: inf_xxxANTHROPIC_API_KEYThe Anthropic SDK default

Always set ANTHROPIC_API_KEY="" when using ANTHROPIC_AUTH_TOKEN, or the cached Anthropic credential can take precedence. An unauthenticated or non-buyer request returns an Anthropic-shaped 401 authentication_error (the skin does not issue x402/MPP 402 challenges — Claude Code can't satisfy them; use a buyer key with credits or an on-chain allowance).

Endpoints (under /anthropic)

MethodPathDescription
POST/anthropic/v1/messagesSend a message; non-streaming or streaming (SSE)
POST/anthropic/min{N}/v1/messagesSame, but only route to sellers ≥ N% cheaper than reference
POST/anthropic/v1/messages/count_tokensEstimate input tokens for a request
GET/anthropic/v1/modelsList models (Anthropic shape)
GET/anthropic/v1/models/{id}Retrieve one model

POST /anthropic/v1/messages

curl https://api.surplusintelligence.ai/anthropic/v1/messages \

-H "x-api-key: inf_xxx" \

-H "anthropic-version: 2023-06-01" \

-H "Content-Type: application/json" \

-d '{

"model": "claude-opus-4.8",

"max_tokens": 1024,

"system": "You are concise.",

"messages": [{"role": "user", "content": "Hello!"}]

}'

Parameters

ParameterRequiredDescription
modelYesAny catalog model (canonical, -latest alias, dated Anthropic ID, or ~anthropic/…)
max_tokensYesMaximum output tokens (required by the Anthropic API)
messagesYesArray of messages; content is a string or content blocks (text, image, tool_use, tool_result, thinking)
systemNoSystem prompt — a string, or an array of text blocks (with cache_control)
temperature, top_p, top_kNoSampling controls
stop_sequencesNoCustom stop sequences
streamNotrue for SSE streaming
toolsNoTool definitions (name, description, input_schema)
tool_choiceNoauto / any / {type:"tool",name} / none
thinkingNo{type:"enabled", budget_tokens} (or adaptive) — extended thinking
metadataNo{user_id} — passed through as the OpenAI user field
providerNoProvider pin / allow-list — a string, string[], or the OpenRouter {orderonly:[...]} object
provider_base_urlNoPin to a specific provider host (matched against the marketplace allowlist)
max_price_per_1mNoPrice cap in USD per 1M input tokens — offers above it are excluded

Request and version/beta headers (anthropic-version, anthropic-beta) are accepted; version/beta are tolerated and ignored (never required).

Response (non-streaming)

A standard Anthropic Message:

{

"id": "msg_…",

"type": "message",

"role": "assistant",

"model": "claude-opus-4.8",

"content": [{"type": "text", "text": "Hello!"}],

"stop_reason": "end_turn",

"stop_sequence": null,

"usage": {"input_tokens": 12, "output_tokens": 3}

}

content blocks may be text, tool_use, or thinking. stop_reason is tool_use if and only if a tool_use block is present. usage.input_tokens is net of cache (cache_read_input_tokens / cache_creation_input_tokens are reported separately when the provider supplies them).

Streaming

With "stream": true, the response is the standard Anthropic SSE event sequence:

event: message_start        → the Message skeleton

event: content_block_start → per content block (text / thinking / tool_use)

event: content_block_delta → text_delta / thinking_delta / signature_delta / input_json_delta

event: content_block_stop

event: message_delta → stop_reason + cumulative usage

event: message_stop

POST /anthropic/v1/messages/count_tokens

curl https://api.surplusintelligence.ai/anthropic/v1/messages/count_tokens \

-H "x-api-key: inf_xxx" -H "Content-Type: application/json" \

-d '{"model":"claude-opus-4.8","messages":[{"role":"user","content":"Hello"}]}'

Returns an estimate (includes the system prompt and tool schemas):

{ "input_tokens": 14 }

This is a heuristic estimate (no upstream round-trip); it is sufficient for context-budget sizing.

GET /anthropic/v1/models

Anthropic-shaped model list, served from the Surplus catalog (lists all marketplace models, not just Claude):

{

"data": [{"id": "claude-opus-4.8", "type": "model", "display_name": "Claude Opus 4.8", "created_at": "2025-01-01T00:00:00.000Z"}],

"has_more": false,

"first_id": "…",

"last_id": "…"

}

Errors

Errors on /anthropic/* use the Anthropic envelope:

{ "type": "error", "error": { "type": "invalid_request_error", "message": "…" }, "request_id": "…" }

HTTPerror.typeWhen
400invalid_request_errorBad request / malformed JSON / out-of-credit
401authentication_errorMissing or invalid buyer key
404not_found_errorUnknown model id, or no sellers under your price threshold
429rate_limit_errorRate limited
413request_too_largeBody exceeds the size limit
5xxapi_error / overloaded_errorUpstream/provider error, or no healthy sellers

There is no Anthropic 402 — an out-of-credit buyer gets a 400 invalid_request_error (mirroring Anthropic's "credit balance too low" behavior).

Model names & aliases

Send any catalog model id. The skin additionally resolves:

  • -latest tags: claude-opus-latest, claude-sonnet-latest, claude-haiku-latest, plus cross-family gpt-latest, glm-latest, deepseek-latest, gemini-latest, and more.
  • Dated Anthropic IDs (e.g. claude-sonnet-4-5-20250929) → canonical.
  • The OpenRouter ~anthropic/… floating-tag prefix.

A model must exist in the catalog and have a healthy offer to route; otherwise you get an Anthropic-shaped 404/overloaded_error. Call GET /anthropic/v1/models for the catalog and GET /api/markets for models with live seller liquidity.

Feature fidelity on non-Claude models

The wire is fully translated, but some Anthropic features are model-dependent:

  • Works on every model: text, system prompt, multi-turn, streaming, tool use (tool_use/tool_result), images, stop reasons, usage/billing.
  • Best-effort: extended thinking — non-Claude models don't emit Anthropic thinking blocks; the field is accepted and the request still works, but no reasoning blocks are returned. Thinking-signature continuity across multi-turn tool loops is preserved on providers that echo signatures and degrades gracefully elsewhere.

Routing controls

Everything the marketplace offers applies here too: provider pinning / allow-lists, provider_base_url, max_price_per_1m price caps, the /anthropic/min{N}/v1/messages minimum-discount mirror, and the buyer's trusted-only / allow-untrusted preference. See Minimum-Discount Routing and BYOK / Priority Provider.