DokumentasiReferensi API

Format Anthropic

Permukaan kedua: Messages API untuk SDK Anthropic dan [CC].

Ringkasan

Selain permukaan OpenAI, NusaRouter melayani permukaan Anthropic: Messages API untuk SDK Anthropic, [CC], dan klien lain yang berbicara format itu. Permukaan lama tetap ada dan tidak berubah; lihat Chat completions.

POST/v1/messagesbutuh API key
POST/v1/messages/count_tokensbutuh API key

Dua permukaan ini hidup di host yang sama dan di namespace /v1 yang sama di kabel. Yang berbeda hanya base URL yang kamu berikan ke SDK-nya, dan itu bagian yang paling sering salah.

dua base URL
# SDK Anthropic / [CC] — base ROOT, tanpa /v1
ANTHROPIC_BASE_URL=https://api.nusarouter.web.id

# SDK OpenAI (permukaan lama) — base TERMASUK /v1
OPENAI_BASE_URL=https://api.nusarouter.web.id/v1

Autentikasi dan base URL

Autentikasinya sama seperti permukaan lain: satu API key, dan bisa dikirim dengan dua cara. Keduanya berfungsi di sini.

x-api-key: sk-nusa-... atau Authorization: Bearer sk-nusa-.... SDK Anthropic memakai x-api-key; klien OpenAI memakai Authorization. NusaRouter menerima keduanya, jadi kamu tidak perlu mengubah key yang sudah ada.

Header anthropic-version yang biasa dikirim SDK Anthropic juga diterima. Header itu pula yang membuat GET /v1/models menjawab bentuk Anthropic; tanpa header itu, GET /v1/models tetap menjawab bentuk OpenAI yang sudah kamu kenal.

Chat

Request dan response memakai bentuk Anthropic, tapi modelnya adalah id yang sama dengan permukaan OpenAI.

POST/v1/messagesbutuh API key

model diisi public_model_id, persis id yang kamu pakai di permukaan OpenAI. Tidak ada alias claude-*: id yang tidak ada di katalog akan ditolak.

max_tokens diterima. Kalau kamu tidak mengirimnya, NusaRouter memakai batas keluaran yang dikonfigurasi untuk model itu; kalau modelnya tidak punya batas, request ditolak 400. Jadi kirim max_tokens eksplisit.

curl https://api.nusarouter.web.id/v1/messages \
  -H "x-api-key: sk-nusa-..." \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-flash-v4.1",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Halo!"}]
  }'

Response non-streaming berbentuk Anthropic:

200 application/json
{
  "id": "msg_...",
  "type": "message",
  "role": "assistant",
  "model": "deepseek-flash-v4.1",
  "content": [
    { "type": "text", "text": "Halo! Ada yang bisa saya bantu?" }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 9,
    "output_tokens": 11,
    "cache_read_input_tokens": 0,
    "cache_creation_input_tokens": 0
  }
}

stop_reason bernilai end_turn, max_tokens, tool_use, stop_sequence, atau refusal. stop_sequence selalu null di sini, karena model di balik layar tidak melaporkan string penghenti yang cocok.

Streaming

Set stream: true dan server mengirim Server-Sent Events dengan urutan event Anthropic.

urutan event
message_start
  content_block_start  (text)      ->  content_block_delta (text_delta)      ->  content_block_stop
  content_block_start  (tool_use)  ->  content_block_delta (input_json_delta) ->  content_block_stop
message_delta            (stop_reason + usage akhir)
message_stop

Setiap blok konten dibuka content_block_start, diisi content_block_delta, lalu ditutup content_block_stop. message_start membawa usage awal nol; usage final dan stop_reason ada di message_delta, dan aliran ditutup message_stop.

Pemanggilan tool

Bentuknya mengikuti Messages API. Kamu mendeklarasikan tool dengan input_schema, dan model membalas dengan blok tool_use.

deklarasi tool
{
  "model": "deepseek-flash-v4.1",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_cuaca",
      "description": "Ambil cuaca sebuah kota.",
      "input_schema": {
        "type": "object",
        "properties": { "kota": { "type": "string" } },
        "required": ["kota"]
      }
    }
  ],
  "messages": [{ "role": "user", "content": "Bagaimana cuaca di Bandung?" }]
}

tool_choice menerima auto, any, tool, atau none. tool_use.id dikembalikan apa adanya saat kamu mengirim hasilnya balik, jadi replay tetap konsisten.

Untuk mengembalikan hasilnya, kirim pesan user berisi blok tool_result dengan tool_use_id yang cocok. Blok tool_result boleh bercampur dengan teks di pesan yang sama; NusaRouter menerjemahkannya ke giliran tool yang benar di belakang layar.

Hitung token

Hitung perkiraan token input sebelum mengirim request, tanpa menjalankannya.

POST/v1/messages/count_tokensbutuh API key

Menerima bentuk request Messages yang sama, mengembalikan {"input_tokens": N}.

200 application/json
{ "input_tokens": 42 }

Error

Error di permukaan ini memakai envelope Anthropic, bukan envelope permukaan OpenAI.

bentuk error
{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "Pesan yang aman untuk ditampilkan.",
    "code": "invalid_request"
  }
}

Status HTTP-nya dipertahankan, dengan satu pengecualian yang penting: validation di sini 400, bukan 422 seperti di permukaan OpenAI.

Contoh tipe yang akan kamu lihat: invalid_request_error, authentication_error, not_found_error (model atau path tidak ada), rate_limit_error, billing_error (saldo atau limit key habis), timeout_error (model di balik layar kehabisan waktu), request_too_large (body melewati 30 MiB), overloaded_error, dan api_error.

Tipe Anthropic lebih kasar dari kode permukaan OpenAI: dua sebab bisa berbagi satu tipe. Karena itu error.code tetap dikirim berisi kode platform yang spesifik (insufficient_balance, spending_limit_exceeded, rate_limit_exceeded, concurrency_exceeded, …), supaya kamu bisa bercabang persis. SDK Anthropic mengabaikan field tambahan ini, jadi klien standar tidak terpengaruh.

Troubleshooting

GejalaPenyebabSolusi
404Base URL Anthropic kamu berakhir /v1, jadi SDK memanggil /v1/v1/messages.setel base-nya ke https://api.nusarouter.web.id (root, tanpa /v1)
Permukaan tertukarBase URL Anthropic butuh root; base URL OpenAI butuh /v1. Keduanya mudah tertukar.Anthropic SDK → https://api.nusarouter.web.id; OpenAI SDK → https://api.nusarouter.web.id/v1
404 not_found_errorId model salah, atau kamu memakai nama alias claude-*.pakai public_model_id dari katalog, tanpa alias
400 soal max_tokensmax_tokens tidak dikirim, dan modelnya tidak punya batas keluaran yang dikonfigurasi.kirim max_tokens eksplisit
count_tokens tidak sama dengan tagihanTokenizernya berbeda dari tokenizer Anthropic; angkanya perkiraan.pakai sebagai perkiraan, bukan angka tagihan