コンテンツにスキップ

Claude Messages プロトコル

Claude Messages は Anthropic のネイティブ会話プロトコルです。クライアントが既に Anthropic 仕様に従って開発されている場合、Base URL と API Key を RouteAPI に置き換えるだけで、リクエスト構造を変更することなく直接使用できます。

Claude Messages は messages 配列で複数ターンの会話を表現し、独立した system フィールドでシステムプロンプトを表現し、max_tokens の明示的な宣言を要求します。OpenAI 互換フォーマットと比較して、コンテンツブロック構造がより統一されています。テキスト、画像、ツール呼び出し、ツール結果はすべて同じ配列内の異なる type です。

適用シナリオ:

シナリオ説明
Claude CodeAnthropic 公式コーディングエージェント、/v1/messages のみ認識
Anthropic SDKanthropic Python / TypeScript SDK、base_url を変更するだけ
ネイティブメッセージフォーマットクライアントcontent block 構造で既にプロンプトを組織しているアプリケーション
拡張思考とプロンプトキャッシングthinking、cache_control などの Claude 特有の機能に依存

クライアントが OpenAI プロトコルのみをサポートしている場合は、Chat Completions を使用してください。RouteAPI は内部で必要なフォーマット適応を完了しますが、クライアントがネイティブにサポートするプロトコルを優先選択することで、互換性が最も良くなります。

POST /v1/messages

完全なアドレス:

https://api.routeapi.ai/v1/messages

リクエストヘッダーは 2 つの認証方法をサポートし、両方とも同じ RouteAPI Token を使用します:

Authorization: Bearer sk-your-routeapi-token
Content-Type: application/json
x-api-key: sk-your-routeapi-token
anthropic-version: 2023-06-01
Content-Type: application/json

x-api-key は Anthropic SDK のデフォルトの書き方です。RouteAPI は /v1/messages パス上で自動的にそれを Token として認識するため、公式 SDK は追加の設定を必要としません。anthropic-version はそのまま上流に透過され、公式 SDK が自動的に付加します。

フィールドタイプ必須説明
modelstringはいモデル ID、現在のアカウントで利用可能なモデルである必要があります
messagesarrayはい会話メッセージリスト、少なくとも 1 つ、role は交互に出現する必要があります
max_tokensintegerはい最大出力トークン数、Claude プロトコルで強制的に要求されます
systemstring/arrayいいえシステムプロンプト、独立したフィールド、messages には入れません
temperaturenumberいいえサンプリング温度、値は 0 から 1
top_pnumberいいえnucleus sampling パラメータ
top_kintegerいいえ確率が最も高い K 個のトークンからのみサンプリング
streambooleanいいえSSE ストリーミング出力を使用するかどうか
stop_sequencesarrayいいえカスタム停止シーケンス
toolsarrayいいえツール定義リスト
tool_choiceobjectいいえツール選択戦略
thinkingobjectいいえ拡張思考設定、モデルがサポートしているかどうかに依存
metadataobjectいいえリクエストメタ情報、Claude 特有

これは OpenAI から移行する際に最も陥りやすい罠です。OpenAI の max_tokens は省略時にモデルのデフォルト上限を使用しますが、Claude プロトコルにはデフォルト値がなく、欠落している場合は上流が直接 invalid_request_error を返します。

{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [{ "role": "user", "content": "こんにちは" }]
}

max_tokens は出力上限であり、入力トークンは含まれず、正確な長さの約束でもありません。モデルは早期に終了する可能性があり(stop_reason: "end_turn")、ちょうど切り捨てられる可能性もあります(stop_reason: "max_tokens")。本番環境では、ビジネスで期待される応答の長さに基づいて設定し、ある程度の余裕を残し、同時に stop_reason をチェックして切り捨てられたかどうかを判断することをお勧めします。

Claude プロトコルは role: "system" のメッセージを受け入れません。システムプロンプトはリクエストのトップレベルの system フィールドに配置する必要があり、messages 配列には user と assistant のみを含めることができます。

正しい書き方:

{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"system": "あなたは厳密な技術アシスタントです。回答は簡潔に保ってください。",
"messages": [{ "role": "user", "content": "API ゲートウェイとは何かを説明してください" }]
}

誤った書き方(Claude プロトコルは拒否します):

{
"messages": [
{ "role": "system", "content": "あなたは厳密な技術アシスタントです。" },
{ "role": "user", "content": "API ゲートウェイとは何かを説明してください" }
]
}

system は配列形式もサポートしており、異なる段落に個別にプロンプトキャッシュを設定するために使用されます:

{
"system": [
{ "type": "text", "text": "あなたはコードレビューアシスタントです。" },
{
"type": "text",
"text": "以下はプロジェクトのコーディング規約の全文です……",
"cache_control": { "type": "ephemeral" }
}
]
}

metadata はリクエストのメタ情報を運ぶために使用され、現在は user_id フィールドのみがあり、上流の不正使用検出に使用されます。メールアドレスや電話番号など、個人を特定できる情報をここに入れないでください。ハッシュ値または内部 ID を渡すことをお勧めします。

{
"metadata": {
"user_id": "a3f1c2d4e5b6"
}
}

messages 配列内の各メッセージには role と content の 2 つのフィールドが含まれています。role は user または assistant のみで、交互に出現する必要があり、最初は user である必要があります。

content は 2 つの形式をサポートしています。文字列は単一テキストの省略形です:

{ "role": "user", "content": "RouteAPI を一文で紹介してください" }

配列形式はコンテンツブロックで構成され、各ブロックは type で区別されます:

type出現位置説明
textuser / assistantプレーンテキストコンテンツ
imageuser画像入力、base64 と URL をサポート
documentuserドキュメント入力、モデルがサポートしているかどうかに依存
tool_useassistantモデルがツール呼び出しを要求
tool_resultuserクライアントから返されたツール実行結果
thinkingassistant拡張思考コンテンツブロック

画像は source フィールドを通じて渡されます。base64 方式では media_type も同時に提供する必要があります:

{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "/9j/4AAQSkZJRgABAQAAAQ..."
}
},
{ "type": "text", "text": "この画像にはどのようなコントロールがありますか?" }
]
}

URL 方式はより簡潔ですが、画像アドレスが上流からアクセス可能である必要があります:

{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "url",
"url": "https://example.com/screenshot.png"
}
},
{ "type": "text", "text": "このインターフェースのレイアウトを説明してください" }
]
}

テキストブロックを画像ブロックの後に配置すると、通常より良い効果が得られます。1 回のリクエストで複数の画像を配置できますが、入力トークンが大幅に増加するため、最初にサイズを圧縮することをお勧めします。

Claude のツール定義はフラットな構造で、パラメータ schema フィールドは input_schema と呼ばれます:

{
"tools": [
{
"name": "get_weather",
"description": "指定された都市の現在の天気を照会します。都市名は中国語のフルネームを使用します。",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "都市名、例: 東京" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
]
}

OpenAI のネストされた構造と比較して、違いは Claude には外側の type: "function" ラッパーがなく、function ネストレイヤーもなく、parameters が input_schema に名前変更されていることです:

{
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "指定された都市の現在の天気を照会します。",
"parameters": { "type": "object", "properties": {} }
}
}
]
}

description の品質は、モデルがツールを正しく選択するかどうかを直接決定するため、用途、パラメータフォーマット、境界条件を明確に記述することをお勧めします。

書き方動作
{ "type": "auto" }モデルがツールを呼び出すかどうかを自分で決定、デフォルト値
{ "type": "any" }ツールを呼び出す必要があるが、モデルがどれを呼び出すかを選択
{ "type": "tool", "name": "get_weather" }指定されたツールを強制的に呼び出し
{ "type": "none" }ツール呼び出しを禁止

"disable_parallel_tool_use": true を追加すると、モデルが一度に 1 つのツール呼び出しのみを開始するように制限できます。

ツール呼び出しは完全な会話のやり取りです。モデルが tool_use ブロックを返した後、元の assistant メッセージと実行結果を一緒に返す必要があります。

ステップ 1、モデルがツール呼び出しリクエストを返します:

{
"role": "assistant",
"content": [
{ "type": "text", "text": "東京の天気を調べてみます。" },
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": { "city": "東京", "unit": "celsius" }
}
]
}

ステップ 2、この assistant メッセージをそのまま messages に追加し、次に結果を運ぶ user メッセージを追加します。tool_use_id は前のステップの id と完全に一致する必要があります:

{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "東京、晴れ、気温 23 摂氏度、湿度 45%。"
}
]
}

ツール実行が失敗した場合は is_error マーカーを使用して、モデルに戦略を変更する必要があることを知らせ、再試行しないようにします:

{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "天気サービスがタイムアウトし、データを取得できませんでした。",
"is_error": true
}

tool_result は user ロールに属し、Claude プロトコルには OpenAI のような独立した role: "tool" はありません。モデルが一度に複数の tool_use ブロックを返した場合、対応するすべての tool_result は同じ user メッセージの content 配列に配置する必要があります。

{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-4-5",
"content": [
{
"type": "text",
"text": "RouteAPI は複数の AI モデルプロバイダーを統一的に管理する API ゲートウェイです。"
}
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 24,
"output_tokens": 18
}
}

content は常に配列であり、1 つのテキストセグメントのみの場合でも同様です。クライアントは content[0] がテキストブロックであると仮定しないでください。モデルが拡張思考を有効にするか、ツール呼び出しを開始すると、最初のブロックは thinking または tool_use になる可能性があります。

stop_reason の値:

値意味
end_turnモデルが自然に応答を終了
max_tokensmax_tokens 上限に達して切り捨て
stop_sequencestop_sequences 内のシーケンスにヒット
tool_useモデルがツール呼び出しを要求、結果を待機中

stream: true を設定すると SSE が返されます。Claude のストリーミングフォーマットは OpenAI とかなり異なります。各イベントには明確な event: タイプ名があり、終了マーカーは message_stop イベントであり、data: [DONE] ではありません。

event: message_start
data: {"type":"message_start","message":{"id":"msg_01XFD","type":"message","role":"assistant","content":[],"model":"claude-sonnet-4-5","usage":{"input_tokens":24,"output_tokens":1}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"RouteAPI"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" は"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":18}}
event: message_stop
data: {"type":"message_stop"}

イベントタイプの説明:

イベント説明
message_startメッセージ開始、初期 usage を運ぶ(この時点で output_tokens は不正確)
content_block_startコンテンツブロックが開始、index が位置を識別
content_block_delta増分コンテンツ、テキストは text_delta、ツールパラメータは input_json_delta
content_block_stop現在のコンテンツブロックが終了
message_deltaメッセージレベルの増分、最終的な stop_reason と累積 output_tokens を運ぶ
message_stop応答全体が終了
pingハートビートイベント、無視可能
errorストリーム途中でエラー

ツール呼び出しのパラメータは JSON 文字列を断片的に返すため、すべての input_json_delta の partial_json を連結して完全にした後に解析する必要があります:

event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"city\":"}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"\"東京\"}"}}

index でグループ化して累積し、連結プロセス中に中間状態を解析しようとしないでください。

フィールド説明
input_tokens入力トークン数、キャッシュヒット部分を含まない
output_tokens出力トークン数
cache_creation_input_tokensプロンプトキャッシュに書き込まれたトークン数
cache_read_input_tokensプロンプトキャッシュから読み取られたトークン数
server_tool_useサーバーサイドツール使用量、例: web_search_requests

ストリーミング応答では、output_tokens は message_delta イベント内の値を基準にする必要があります。message_start 内のものは初期プレースホルダーです。課金基準はコンソールログに基づいており、実際にサポートされるフィールドは選択したモデルによって異なります。

パラメータマッピングテーブル

Section titled “パラメータマッピングテーブル”
Claude MessagesOpenAI Chat Completions差異説明
modelmodel一致
system(トップレベルフィールド)messages[0] の role: "system"位置が異なる、Claude は system メッセージを受け入れない
messagesmessagesClaude は user / assistant の交互のみを許可
max_tokensmax_tokens / max_completion_tokensClaude は必須、OpenAI はオプション
stop_sequencesstop名前が異なる
temperaturetemperatureClaude の上限は 1、OpenAI の上限は 2
top_k対応なしOpenAI はサポートしていない
tools[].input_schematools[].function.parameters階層とフィールド名が両方異なる
tool_choice: {"type":"any"}tool_choice: "required"書き方が異なる
metadata.user_iduser位置が異なる
thinkingreasoning_effort制御方法が異なる
対応なしnClaude は一度に複数の候補を生成することをサポートしていない
対応なしfrequency_penalty / presence_penaltyClaude はサポートしていない
対応なしresponse_formatClaude はツールまたはプロンプトを使用して出力構造を制約

応答構造の差異:

項目Claude MessagesOpenAI Chat Completions
トップレベルコンテンツcontent 配列choices[0].message
テキスト位置content[0].textchoices[0].message.content
終了理由stop_reasonfinish_reason
ツール呼び出しcontent 内の tool_use ブロックmessage.tool_calls
ツール結果ロールuser メッセージ内の tool_result ブロック独立した role: "tool"
入力使用量usage.input_tokensusage.prompt_tokens
出力使用量usage.output_tokensusage.completion_tokens
合計フィールドなし、手動で加算する必要があるusage.total_tokens
ストリーミング終了message_stop イベントdata: [DONE]

OpenAI から Claude Messages に移行する際は、次の順序で確認してください:

  1. system メッセージを messages 配列からトップレベル system フィールドに移動します。
  2. max_tokens を追加します。これは必須項目です。
  3. messages の最初が user であることを確認し、ロールが厳密に交互であり、連続する同じロールのメッセージがないことを確認します。
  4. ツール定義から type と function ラッパーレイヤーを削除し、parameters を input_schema に名前変更します。
  5. ツール結果を role: "tool" から user メッセージ内の tool_result ブロックに変更し、tool_use_id を揃えます。
  6. temperature が元々 1 より大きい場合は、Claude の値の範囲内に下げる必要があります。
  7. 応答解析を content 配列を反復処理し、type でディスパッチするように変更し、固定インデックスを仮定しないでください。
  8. ストリーミング解析を event: タイプでディスパッチするように変更し、終了条件を message_stop に置き換えます。

改造コストが高い場合は、OpenAI プロトコルを使用して Claude シリーズモデルを呼び出し続けることもでき、RouteAPI がフォーマット変換を完了します。トレードオフは、Claude 特有の機能の一部(拡張思考の完全な制御、細粒度のプロンプトキャッシング)が OpenAI フォーマットでは完全に表現できないことです。

curl:

Terminal window
curl https://api.routeapi.ai/v1/messages \
-H "x-api-key: $ROUTEAPI_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"system": "あなたは厳密な技術アシスタントです。回答は簡潔に保ってください。",
"messages": [
{ "role": "user", "content": "RouteAPI を一文で紹介してください" }
]
}'

Anthropic Python SDK、base_url を変更するだけ:

import os
from anthropic import Anthropic
client = Anthropic(
api_key=os.environ["ROUTEAPI_KEY"],
base_url="https://api.routeapi.ai",
)
message = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system="あなたは厳密な技術アシスタントです。回答は簡潔に保ってください。",
messages=[
{"role": "user", "content": "RouteAPI を一文で紹介してください"},
],
)
print(message.content[0].text)
print(message.usage.input_tokens, message.usage.output_tokens)

base_url はドメインまで入力すれば良く、SDK が自動的に /v1/messages を追加します。ストリーミング呼び出しには client.messages.stream() を使用します:

with client.messages.stream(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "API ゲートウェイとは何かを段階的に説明してください"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message()
print()
print(final.stop_reason, final.usage.output_tokens)

curl、base64 を使用:

Terminal window
curl https://api.routeapi.ai/v1/messages \
-H "x-api-key: $ROUTEAPI_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "'"$(base64 -w 0 screenshot.jpg)"'"
}
},
{ "type": "text", "text": "この画像にはどのような UI コントロールがありますか?" }
]
}
]
}'

Python SDK:

import base64
import os
from anthropic import Anthropic
client = Anthropic(
api_key=os.environ["ROUTEAPI_KEY"],
base_url="https://api.routeapi.ai",
)
with open("screenshot.jpg", "rb") as f:
image_data = base64.standard_b64encode(f.read()).decode("utf-8")
message = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": image_data,
},
},
{"type": "text", "text": "この画像にはどのような UI コントロールがありますか?"},
],
}
],
)
print(message.content[0].text)

結果の受け渡しを含む完全な 2 ラウンドトリップ:

import json
import os
from anthropic import Anthropic
client = Anthropic(
api_key=os.environ["ROUTEAPI_KEY"],
base_url="https://api.routeapi.ai",
)
tools = [
{
"name": "get_weather",
"description": "指定された都市の現在の天気を照会します。都市名は中国語のフルネームを使用します。",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "都市名、例: 東京"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["city"],
},
}
]
def get_weather(city: str, unit: str = "celsius") -> str:
# ここを実際の天気サービス呼び出しに置き換えます
return f"{city}、晴れ、気温 23 摂氏度、湿度 45%。"
messages = [{"role": "user", "content": "東京の天気はどうですか?"}]
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
# stop_reason が tool_use の場合のみツールを実行して返す必要があります
if response.stop_reason == "tool_use":
# 元の assistant メッセージをそのまま追加する必要があります。そうしないと tool_use_id が揃いません
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type != "tool_use":
continue
try:
result = get_weather(**block.input)
is_error = False
except Exception as exc:
result = f"ツール実行失敗: {exc}"
is_error = True
tool_results.append(
{
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
"is_error": is_error,
}
)
# 同じラウンドのすべてのツール結果を 1 つの user メッセージに入れます
messages.append({"role": "user", "content": tool_results})
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
print(response.content[0].text)

対応する curl 第 2 ラウンドリクエスト:

Terminal window
curl https://api.routeapi.ai/v1/messages \
-H "x-api-key: $ROUTEAPI_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [
{
"name": "get_weather",
"description": "指定された都市の現在の天気を照会します。",
"input_schema": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
],
"messages": [
{ "role": "user", "content": "東京の天気はどうですか?" },
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": { "city": "東京" }
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "東京、晴れ、気温 23 摂氏度、湿度 45%。"
}
]
}
]
}'
  • パラメータの実際のサポートレベルは、選択したモデルと上流サービスの機能に依存します。thinking、cache_control、mcp_servers などのオプション機能は、まずテスト環境で検証することをお勧めします。
  • 明示的に 0 または false を渡すオプションパラメータは、ユーザーの明示的な設定と見なされ、デフォルト値として破棄されません。
  • 本番環境では、モデル ID を固定することをお勧めします。一時的なエイリアスや表示名に依存しないでください。
  • 各リクエストの request ID、モデル ID、ステータスコード、トークン使用量を記録して、レイテンシとコストの異常をトラブルシューティングしやすくします。
  • エラー応答は Claude の {"type": "error", "error": {...}} 構造に従います。詳細は エラー処理 を参照してください。