Tutorial Claude API di VPS Ubuntu 24.04
Pelajari cara membangun aplikasi Python di VPS Ubuntu 24.04 menggunakan Claude API. Panduan ini mencakup sistem streaming, penanganan error, dan kontrol biaya.
Apa yang Anda bangun
Sebuah alat baris perintah pada VPS Ubuntu 24.04 baru. Anda memasukkan pesan kesalahan atau potongan log, lalu mendapatkan diagnosis dalam bahasa Inggris sederhana: journalctl -u nginx -n 50 | explain. Program ini hanya terdiri dari sekitar enam puluh baris kode Python. Proyek ini mencakup semua kebutuhan aplikasi Claude API yang sebenarnya — penyimpanan kunci yang tepat, virtualenv, struktur respons SDK, streaming, rantai pengecualian bertipe (typed exception chain), dan unit systemd agar dapat berjalan secara otomatis.
Saya memilih proyek ini dengan sengaja. Kebanyakan tutorial "aplikasi API pertama" meminta Anda membuat chatbot yang tidak akan pernah dibuka lagi. Alat penjelas log memberikan manfaat nyata pada server sejak hari pertama. Proyek ini melatih dua hal yang sering salah dilakukan pemula: membaca objek respons dengan benar dan mengontrol pengeluaran. API menagih per token tanpa batas selain yang Anda tetapkan sendiri. Oleh karena itu, kontrol biaya adalah bagian dari desain, bukan sekadar tambahan — disiplin yang sama pentingnya saat Anda beralih ke menjalankan Claude Code pada VPS yang sama ini di tmux.
Dapatkan API key dari Console
Akses API dikelola di Anthropic Console pada platform.claude.com — daftar akun, lalu buat key di bawah Settings → API Keys (tautan dokumentasi langsung mengarah ke platform.claude.com/settings/keys). Key hanya ditampilkan satu kali, dimulai dengan sk-ant-, dan tidak dapat dilihat kembali — segera salin key tersebut atau hapus dan buat ulang.
Mengenai biaya: per Juli 2026, tidak ada paket gratis berkelanjutan untuk API. Dokumentasi harga Anthropic menyatakan pengguna baru menerima sejumlah kecil kredit gratis untuk pengujian; jumlah pastinya adalah apa yang ditampilkan Console saat pendaftaran, dan setelah kredit habis, Anda harus mengisi saldo akun agar permintaan berhasil. Ini terpisah dari langganan claude.ai — paket Pro atau Max tidak mencakup kredit API, dan API key tidak memberikan akses ke aplikasi chat. Jika Anda sedang mempertimbangkan antara langganan atau API, perbandingannya adalah topik tersendiri: paket Claude mana yang sebenarnya Anda butuhkan.
Buat key dengan cakupan terbatas pada satu proyek atau server. Jika key bocor — dan pada jangka waktu yang cukup lama, kebocoran pasti akan terjadi — Anda dapat mencabutnya tanpa merusak sistem lain yang Anda miliki.
Jangan simpan kunci di dalam .bashrc
Metode refleksif adalah export ANTHROPIC_API_KEY=sk-ant-... di ~/.bashrc. Jangan lakukan itu. Ada tiga masalah terpisah:
- Setiap proses mewarisinya. Variabel lingkungan yang diekspor dalam login shell akan menyebar ke semua hal yang Anda jalankan — aplikasi web, pelapor crash yang menyertakan lingkungan ke dalam laporan bug, atau halaman
phpinfo()yang dibiarkan aktif. Permukaan paparan kunci menjadi "semua hal yang dijalankan pengguna ini." - Mengetiknya akan masuk ke
~/.bash_history. Menjalankan perintah export secara manual akan menyimpan kunci Anda dalam file teks biasa selamanya, dan tersinkronisasi ke setiap cadangan direktori home Anda. - Kunci tidak tersedia saat systemd membutuhkannya. Layanan tidak membaca
.bashrcAnda, sehingga pola ini gagal saat Anda mengubah skrip menjadi unit — biasanya muncul sebagai error 401 misterius pada jam 6 pagi.
Pola yang benar pada server adalah menggunakan file lingkungan khusus dengan izin 600, yang hanya dimuat oleh proses yang membutuhkannya:
sudo mkdir -p /opt/explain
sudo install -m 600 -o root -g root /dev/null /etc/claude-explain.env
printf 'ANTHROPIC_API_KEY=sk-ant-YOUR-KEY-HERE\n' | sudo tee /etc/claude-explain.env >/dev/nullGunakan tee dari printf alih-alih editor jika Anda ingin menjaga kunci agar tidak masuk ke dalam file swap editor; apa pun caranya, verifikasi dengan ls -l /etc/claude-explain.env bahwa file tersebut membaca -rw------- dan dimiliki oleh root. Shell interaktif mendapatkan kunci per-invokasi melalui wrapper (di bawah), dan systemd mendapatkannya melalui EnvironmentFile= — root membaca file tersebut sebelum menurunkan hak akses, sehingga pengguna layanan tidak memerlukan akses baca ke file tersebut. Kunci tidak akan muncul di dalam kode, di git, di output ps, atau di riwayat shell.
Install SDK dalam venv
Ubuntu 24.04 menyertakan Python 3.12 dengan penegakan PEP 668, sehingga instalasi pip install anthropic langsung pada interpreter sistem akan gagal dengan error: externally-managed-environment. Error tersebut terjadi karena sistem operasi bekerja sesuai fungsinya — gunakan virtualenv:
sudo apt update && sudo apt install -y python3-venv
sudo python3 -m venv /opt/explain/venv
sudo /opt/explain/venv/bin/pip install anthropicTidak diperlukan proses aktivasi pada server: memanggil /opt/explain/venv/bin/python secara langsung akan selalu menggunakan paket dari venv.
Panggilan pertama, dan membaca respons dengan benar
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environment
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
messages=[{"role": "user", "content": "Explain what a systemd unit file is in three sentences."}],
)
for block in response.content:
if block.type == "text":
print(block.text)Dua hal dalam dua belas baris tersebut mencakup sebagian besar model mental API ini. Pertama, anthropic.Anthropic() tanpa argumen akan membaca kunci dari environment — jangan pernah memasukkannya sebagai string literal. Kedua, response.content adalah sebuah list of content blocks, bukan sebuah string. Jika Anda mencetaknya secara langsung, Anda akan mendapatkan output klasik bagi pengguna baru:
[TextBlock(citations=None, text='A systemd unit file is...', type='text')]Itu bukan bug; itu adalah repr dari objek tersebut. Respons dapat berisi berbagai tipe blok (text, tool calls, thinking), jadi Anda harus melakukan iterasi dan memeriksa block.type == "text" sebelum mengakses .text. Terapkan loop tersebut sejak awal agar Anda terhindar dari kebingungan "output berupa karakter acak".
Gunakan model ID claude-opus-4-8 yang tepat. ID generasi saat ini tidak menggunakan tanggal — jangan mengikuti kebiasaan (atau postingan blog lama) yang menyuruh Anda menambahkan akhiran tanggal; hal tersebut akan menghasilkan error 404, yang dibahas di bawah.
Alat yang sebenarnya: penjelasan
Berikut adalah program lengkapnya — input melalui stdin, diagnosis streaming sebagai output, dan penanganan error:
#!/usr/bin/env python3
"""explain: pipe an error or log excerpt in, get a diagnosis out."""
import sys
import anthropic
MODEL = "claude-opus-4-8"
def main() -> int:
text = sys.stdin.read().strip()
if not text:
print("usage: journalctl -u nginx -n 50 | explain", file=sys.stderr)
return 1
client = anthropic.Anthropic()
try:
with client.messages.stream(
model=MODEL,
max_tokens=1500,
system=(
"You are a senior Linux sysadmin. The user pipes you server "
"logs or error output. Name the most likely cause outright, "
"then give the commands to confirm and fix it. Be terse."
),
messages=[{"role": "user", "content": text}],
) as stream:
for chunk in stream.text_stream:
print(chunk, end="", flush=True)
print()
except anthropic.RateLimitError as e:
retry_after = e.response.headers.get("retry-after", "60")
print(f"rate limited; retry in {retry_after}s", file=sys.stderr)
return 2
except anthropic.APIStatusError as e:
print(f"API error {e.status_code}: {e.message}", file=sys.stderr)
return 2
except anthropic.APIConnectionError:
print("network error reaching the API", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
sys.exit(main())Simpan sebagai /opt/explain/explain.py, lalu tambahkan wrapper untuk memuat kunci guna penggunaan interaktif:
sudo tee /usr/local/bin/explain >/dev/null <<'EOF'
#!/bin/sh
set -a; . /etc/claude-explain.env; set +a
exec /opt/explain/venv/bin/python /opt/explain/explain.py "$@"
EOF
sudo chmod 755 /usr/local/bin/explain(Wrapper harus dijalankan melalui sudo atau file env harus memiliki grup yang merupakan anggota dari user admin Anda — pilih salah satu secara sengaja daripada melonggarkan izin file menjadi 644.)
Mengapa menggunakan streaming. client.messages.stream mencetak token saat data tiba alih-alih menunggu seluruh proses generasi selesai, dan ini menghindari timeout HTTP pada output yang panjang — SDK akan menolak nilai max_tokens yang sangat besar pada panggilan non-streaming karena alasan tersebut. Jika Anda memerlukan objek yang telah disusun setelahnya, panggil stream.get_final_message() di dalam blok with.
Mengapa urutan pengecualian tersebut. SDK melemparkan pengecualian bertipe, dengan yang paling spesifik di urutan pertama: RateLimitError adalah error 429 dan menyertakan header retry-after yang memberi tahu berapa lama Anda harus menunggu; APIStatusError mencakup respons non-2xx lainnya (cek e.status_code >= 500 untuk masalah sisi server); APIConnectionError berarti permintaan tidak menerima respons sama sekali. Dan sebelum Anda membuat loop retry: SDK sudah melakukan retry otomatis untuk error 429 dan 5xx, sebanyak dua kali secara default dengan exponential backoff (max_retries pada client). Saat except Anda berjalan, jatah retry sudah habis — jadi langkah yang tepat pada CLI adalah melaporkan error dan keluar, bukan menunggu lalu mencoba lagi secara terus-menerus.
Kontrol biaya
Bagian ini memerlukan pembahasan tersendiri karena API tidak memiliki batas bulanan bawaan selain dari apa yang Anda konfigurasi, dan setiap kesalahan di sini akan berakumulasi secara diam-diam.
max_tokens adalah batas pengeluaran per panggilan Anda. Token output adalah komponen yang mahal — pada Opus 4.8, harganya lima kali lipat dari harga input — dan max_tokens adalah batas keras untuk jumlah token yang dapat dihasilkan model. Prompt yang tidak terkendali tidak akan menghasilkan biaya output melebihi batas yang Anda tentukan. Sesuaikan dengan kebutuhan tugas: 1.500 sudah cukup untuk diagnosis log; tugas klasifikasi hanya membutuhkan 100. Jika respons berhenti di tengah kalimat dengan stop_reason: "max_tokens", berarti batas Anda terlalu rendah — naikkan secara sadar daripada langsung menggunakan angka yang sangat besar.
Hitung sebelum mengirim. Input juga memerlukan biaya, dan log biasanya berukuran besar. API menyediakan endpoint penghitungan yang gratis (memiliki limit rate sendiri, terpisah dari pembuatan pesan):
count = client.messages.count_tokens(
model="claude-opus-4-8",
messages=[{"role": "user", "content": big_log_text}],
)
print(count.input_tokens)Gunakan endpoint ini untuk mencegah pengiriman log sebesar 2 GB secara tidak sengaja melalui alat ini. Jangan gunakan tiktoken untuk tujuan ini — itu adalah tokenizer OpenAI, dan ia menghitung token Claude lebih rendah sekitar 15–20% pada teks biasa, dan lebih rendah lagi pada kode.
Pilih model berdasarkan tugas, bukan loyalitas. Per Juli 2026, Opus 4.8 (claude-opus-4-8) berbiaya $5 per satu juta token input dan $25 per satu juta output; Haiku 4.5 (claude-haiku-4-5) berbiaya $1/$5 dengan konteks 200K; Sonnet 5 (claude-sonnet-5) berada di tengah pada $3/$15, dengan harga perkenalan $2/$10 hingga 31 Agustus 2026. Secara konkret: kutipan log sebesar 2.000 token dengan jawaban 500 token berbiaya sekitar $0.0225 pada Opus dan $0.0045 pada Haiku. Mulailah dengan Opus saat Anda menilai kualitas output, lalu coba prompt yang sama pada Haiku — untuk transformasi sederhana bervolume tinggi, hasilnya sering kali tidak berbeda namun dengan harga seperlima kali lipat. Verifikasi angka terbaru di halaman harga sebelum memasukkan data ini secara permanen ke dalam anggaran.
Gunakan Batches untuk tugas yang bisa menunggu. Batches API memproses permintaan secara asinkron dengan harga 50% dari harga standar, dan sebagian besar batch selesai dalam satu jam. Ringkasan harian, pengisian data lama (backfills), klasifikasi massal — apa pun yang tidak memerlukan respon manusia segera harus menggunakan fitur ini.
Prompt caching untuk konteks berulang. Jika setiap panggilan mengirim ulang sistem prompt atau runbook besar yang sama, tandai sebagai cacheable:
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
system=[{
"type": "text",
"text": RUNBOOK_TEXT, # the same 30K tokens on every call
"cache_control": {"type": "ephemeral"},
}],
messages=[{"role": "user", "content": question}],
)
print(response.usage.cache_read_input_tokens) # non-zero from the second call onBiaya penulisan cache sekitar 1.25x harga input, biaya pembacaan cache sekitar 0.1x, dengan TTL 5 menit — sehingga panggilan kedua dalam jendela waktu tersebut sudah menutupi biaya panggilan pertama. Ada dua kendala. Prefix yang di-cache harus melewati batas minimum per model — beberapa ribu token pada Opus — sehingga sistem prompt yang pendek tidak akan masuk ke cache sama sekali. Dan jika cache_read_input_tokens tetap nol pada panggilan yang identik, berarti ada bagian dalam prefix Anda yang berubah pada setiap permintaan (timestamp biasanya menjadi penyebabnya).
Ingat apa yang dihitung sebagai input. Sistem prompt, definisi tool, dan — dalam percakapan multi-turn — seluruh riwayat yang Anda kirim ulang pada setiap giliran semuanya ditagih sebagai token input. Loop chat yang tidak pernah memangkas riwayat akan mengalami kenaikan biaya secara kuadratik. Memahami perhitungan lengkap sangat penting sebelum Anda membangun sistem percakapan apa pun: bagaimana penggunaan token dan penagihan Claude sebenarnya dihitung.
Jalankan di bawah systemd
Keuntungan menggunakan metode environment-file: sebuah timer yang merangkum kesalahan kemarin setiap pagi.
# /etc/systemd/system/log-digest.service
[Unit]
Description=Daily error-log digest via the Claude API
[Service]
Type=oneshot
User=explain
Group=systemd-journal
EnvironmentFile=/etc/claude-explain.env
ExecStart=/bin/sh -c 'journalctl -p err --since yesterday | /opt/explain/venv/bin/python /opt/explain/explain.py >> /var/log/log-digest.txt'# /etc/systemd/system/log-digest.timer
[Unit]
Description=Run the log digest every morning
[Timer]
OnCalendar=06:15
Persistent=true
[Install]
WantedBy=timers.targetsudo useradd -r -s /usr/sbin/nologin explain
sudo touch /var/log/log-digest.txt && sudo chown explain /var/log/log-digest.txt
sudo systemctl daemon-reload
sudo systemctl enable --now log-digest.timer
sudo systemctl start log-digest.service # test it once, right nowPerhatikan manfaat dari EnvironmentFile=: systemd membaca file dengan kepemilikan root dan mode-600 sebelum beralih ke user explain yang tidak memiliki hak istimewa, sehingga proses mendapatkan variabel tersebut sementara user tidak dapat membaca file kunci. Group systemd-journal memberikan akses log. Uji dengan systemctl start manual dan baca journalctl -u log-digest.service — jangan menunggu pukul 06:15 untuk menemukan kesalahan pengetikan. Ketika pola ini melampaui batas shell pipeline, pendekatan kunci-dalam-environment-file yang sama dapat diterapkan langsung pada workflow n8n berbasis Claude di mesin yang sama.
Mode kegagalan, dengan string yang akan Anda lihat
401 pada kunci yang valid. Pengecualian berbunyi:
anthropic.AuthenticationError: Error code: 401 - {'type': 'error', 'error': {'type': 'authentication_error', 'message': 'invalid x-api-key'}, 'request_id': 'req_011CSHoEeqs5C35K2UUqR7Fy'}Jika kunci berfungsi di shell Anda tetapi layanan menghasilkan 401, layanan tersebut tidak pernah menerima kunci tersebut — ingat bahwa systemd tidak membaca .bashrc; pastikan EnvironmentFile= mengarah ke jalur yang benar. Penyebab lainnya: tanda kutip yang ditempel ke dalam file env (ANTHROPIC_API_KEY="sk-ant-..." — systemd menghapus tanda kutip, tetapi . file pada shell wrapper Anda tetap menyertakan tanda kutip jika Anda menggunakan tanda kutip secara tidak tepat), spasi di akhir baris, atau kunci yang telah Anda cabut di Console minggu lalu.
404 karena kesalahan pengetikan model. Versi paling umum dari masalah ini adalah menambahkan akhiran tanggal pada ID model saat ini:
anthropic.NotFoundError: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'model: claude-opus-4-8-20260115'}, 'request_id': 'req_011CSJqymAvNw4bT3qmDdMbA'}ID generasi saat ini bersifat eksak sesuai penulisan — claude-opus-4-8, claude-haiku-4-5, claude-sonnet-5. Salin ID tersebut dari dokumentasi model, jangan pernah dari ingatan atau tutorial lama.
429 rate_limit_error. String tipe kesalahan adalah rate_limit_error dan respons membawa header retry-after yang berisi jumlah detik untuk menunggu. SDK telah melakukan percobaan ulang sebanyak dua kali dengan metode backoff sebelum Anda melihat pengecualian, sehingga 429 yang terus-menerus berarti laju permintaan berkelanjutan Anda benar-benar melebihi tingkatan (tier) Anda — lakukan pemrosesan secara batch atau sebar jadwalnya, jangan mempercepat loop percobaan ulang.
Mencetak objek, bukan teks. Output terlihat seperti [TextBlock(citations=None, text='...', type='text')]. Anda mencetak response.content alih-alih melakukan iterasi pada blok dan membaca .text dari blok di mana block.type == "text". Setiap contoh SDK di atas melakukannya dengan benar; salin loop tersebut.
error: externally-managed-environment. Anda menjalankan pip install pada Python sistem Ubuntu 24.04. Gunakan venv — jangan pernah menggunakan --break-system-packages pada server yang penting bagi Anda.
Jawaban terpotong. response.stop_reason == "max_tokens" berarti model mencapai batas output Anda di tengah proses berpikir. Ini bekerja sesuai desain; tingkatkan batas tersebut secara sengaja.
Setelah aplikasi pertama Anda berfungsi, membangun AI agent dengan Claude mengubah panggilan API yang sama tersebut menjadi agent yang menggunakan alat (tools).
FAQ
Berapa biaya untuk mencoba Claude API?
Sangat murah untuk alat seperti ini. Per Juli 2026, Opus 4.8 berbiaya $5 per satu juta input token dan $25 per satu juta output. Diagnosis log tipikal — beberapa ribu token input dan beberapa ratus token output — memakan biaya sekitar dua sen. Pada Haiku 4.5 ($1/$5), biayanya kurang dari setengah sen. Biaya ringkasan harian selama satu bulan lebih murah daripada harga secangkir kopi. Risiko utamanya bukan harga per panggilan, melainkan loop tak terbatas dan max_tokens tak terbatas, itulah sebabnya keduanya harus diatur secara eksplisit dalam panduan ini.
Apakah ada paket gratis untuk Claude API?
Tidak ada paket gratis berkelanjutan per Juli 2026. Dokumentasi harga Anthropic menyatakan bahwa pengguna baru menerima sejumlah kecil kredit gratis untuk menguji API — uji coba satu kali, dengan jumlah pasti yang ditampilkan di Console saat pendaftaran — setelah itu Anda harus mengisi saldo akun. Jika tujuan Anda adalah biaya marginal nol per permintaan alih-alih kualitas mutakhir, alternatifnya adalah self-host model open-weight dengan Ollama dan membayar menggunakan RAM alih-alih token.
Bagaimana cara menjaga keamanan API key di server?
Jangan pernah menaruhnya di dalam kode, jangan pernah di git, jangan pernah mengekspornya dari .bashrc, dan jangan pernah mengetiknya di shell yang menyimpan riwayat perintah. Simpan di file milik root dengan izin 600, muat per-proses — gunakan skrip pembungkus untuk penggunaan interaktif, dan EnvironmentFile= untuk systemd — serta batasi satu key per server atau proyek agar pencabutan key yang bocor menjadi tindakan presisi, bukan amputasi. Jika key tersebut pernah masuk ke situs paste atau commit git, segera cabut di Console; menghapus commit tidak akan menghapus kebocoran tersebut.
Model Claude mana yang harus saya gunakan untuk memulai?
Mulailah dengan claude-opus-4-8 saat Anda mengevaluasi apakah output yang dihasilkan sudah cukup baik untuk dikembangkan — Anda perlu menilai ide tersebut pada kualitas penuh, dan pada volume hobi perbedaan biayanya hanya beberapa sen. Setelah prompt ditetapkan, jalankan kembali input asli Anda pada claude-haiku-4-5; untuk ringkasan, klasifikasi, dan triase log, model ini sering kali sama baiknya dengan harga seperlima dari harga sebelumnya. Beralihlah ke Haiku atau Sonnet berdasarkan hasil pengukuran, bukan berdasarkan pengaturan default.