SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-07

Tutorial Claude API: Aplikasi Pertama di VPS Ubuntu 24.04

Buat penjelas log Python sekitar 60 baris di Ubuntu 24.04 dengan streaming, penanganan error bertipe, systemd, dan pembatasan biaya API Claude.

Yang Anda bangun

Tool command-line pada VPS Ubuntu 24.04 baru. Anda dapat menyalurkan pesan error atau potongan log ke tool ini untuk mendapatkan diagnosis dalam bahasa Inggris yang sederhana: journalctl -u nginx -n 50 | explain. Tool ini hanya terdiri dari sekitar 60 baris Python. Proyek ini mempraktikkan semua hal yang diperlukan aplikasi Claude API nyata: key yang disimpan dengan benar, virtualenv, bentuk respons SDK, streaming, rangkaian exception bertipe, serta unit systemd agar tool berjalan tanpa intervensi Anda.

Saya memilih proyek ini dengan sengaja. Sebagian besar tutorial “aplikasi API pertama” meminta Anda membuat chatbot yang tidak akan pernah dibuka lagi. Penjelas log langsung berguna pada server sejak hari pertama. Proyek ini juga memaksa Anda memahami dua hal yang sering salah dilakukan pemula: membaca objek respons dengan benar dan mengendalikan pengeluaran. API menagih berdasarkan token tanpa batas selain batas yang Anda tetapkan. Karena itu, pengendalian biaya merupakan bagian dari desain, bukan pertimbangan setelahnya. Disiplin yang sama penting ketika Anda mulai menjalankan Claude Code pada VPS yang sama di tmux.

Dapatkan API key dari Console

Akses API dikelola di Anthropic Console pada platform.claude.com. Daftar, lalu buat key melalui Settings → API Keys (tautan dokumentasi langsung menuju platform.claude.com/settings/keys). Key hanya ditampilkan satu kali, diawali dengan sk-ant-, dan tidak dapat diambil kembali. Segera salin key tersebut, atau hapus lalu buat ulang.

Mengenai biaya: per Juli 2026, API tidak memiliki free tier yang tersedia secara berkelanjutan. Dokumentasi harga Anthropic menyatakan bahwa pengguna baru menerima sejumlah kecil kredit gratis untuk pengujian. Jumlah pastinya adalah jumlah yang ditampilkan Console saat pendaftaran. Setelah kredit habis, Anda harus mengisi saldo akun agar permintaan berhasil. Hal ini terpisah dari langganan claude.ai. Paket Pro atau Max tidak mencakup kredit API, dan API key tidak memberi Anda akses ke aplikasi chat. Jika Anda sedang mempertimbangkan langganan dibandingkan API, pertukaran tersebut merupakan topik tersendiri: paket Claude yang sebenarnya Anda butuhkan.

Buat key yang cakupannya dibatasi pada satu project atau server. Jika sebuah key bocor, hal itu pada akhirnya akan terjadi. Anda harus dapat mencabutnya tanpa mengganggu semua sumber daya lain yang Anda miliki.

Jauhkan key dari .bashrc

Tindakan refleksifnya adalah export ANTHROPIC_API_KEY=sk-ant-... di ~/.bashrc. Jangan lakukan itu. Ada tiga masalah yang terpisah:

  • Setiap proses mewarisinya. Variabel lingkungan yang diekspor dalam login shell akan diteruskan ke semua yang Anda jalankan, termasuk aplikasi web, pelapor crash yang secara otomatis memasukkan lingkungannya ke dalam laporan bug, serta halaman phpinfo() yang masih dibiarkan aktif. Permukaan paparan key menjadi "semua yang pernah dijalankan pengguna ini."
  • Mengetiknya akan tersimpan di ~/.bash_history. Jalankan perintah export secara manual sekali, dan key Anda akan tersimpan dalam file plaintext selamanya serta disinkronkan ke setiap backup direktori home Anda.
  • Key tidak tersedia saat systemd membutuhkannya. Service tidak membaca .bashrc Anda, sehingga pola ini gagal tepat saat skrip dipromosikan menjadi unit, biasanya sebagai error 401 yang misterius pada pukul 6 pagi.

Pola yang tepat pada server adalah menggunakan file lingkungan khusus dengan permission 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/null

Gunakan tee dari printf, bukan editor, jika Anda ingin mencegah key tersimpan dalam file swap editor. Apa pun caranya, verifikasi dengan ls -l /etc/claude-explain.env bahwa file tersebut membaca -rw------- dan dimiliki oleh root. Interactive shell mendapatkan key untuk setiap pemanggilan melalui wrapper (di bawah), sedangkan systemd mendapatkannya melalui EnvironmentFile=. root membaca file tersebut sebelum menurunkan privilege, sehingga service user tidak pernah memerlukan akses baca ke file itu. Key tidak pernah muncul dalam kode, git, output ps, atau shell history.

Instal SDK dalam venv

Ubuntu 24.04 menyertakan Python 3.12 dengan penegakan PEP 668. Karena itu, menjalankan pip install anthropic langsung pada interpreter sistem gagal dengan error: externally-managed-environment. Error tersebut menunjukkan bahwa OS bekerja sesuai ketentuan. 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 anthropic

Aktivasi tidak diperlukan pada server. Memanggil /opt/explain/venv/bin/python secara langsung selalu menggunakan paket dari venv.

Panggilan pertama dan cara 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 menjelaskan sebagian besar model mental API. Pertama, anthropic.Anthropic() tanpa argumen membaca kunci dari environment. Jangan pernah meneruskannya sebagai string literal. Kedua, response.content adalah daftar blok konten, bukan string. Jika langsung dicetak, Anda akan mendapatkan output klasik bagi pengguna baru:

[TextBlock(citations=None, text='A systemd unit file is...', type='text')]

Itu bukan bug. Output tersebut adalah repr objek. Respons dapat berisi beberapa jenis blok (teks, pemanggilan tool, proses berpikir). Karena itu, lakukan iterasi dan periksa block.type == "text" sebelum mengakses .text. Terapkan loop tersebut sejak hari pertama agar kebingungan seperti "outputnya tidak terbaca" tidak terjadi.

Gunakan ID model yang tepat, yaitu claude-opus-4-8. ID generasi saat ini tidak menyertakan tanggal. Jangan mengikuti kebiasaan lama atau artikel blog lama yang menyarankan penambahan sufiks tanggal. Cara tersebut menghasilkan 404, yang dibahas di bawah.

Alat yang digunakan: penjelasan

Berikut program lengkapnya. Program ini membaca dari stdin, menghasilkan diagnosis secara streaming, dan menangani 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 yang memuat key untuk 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 group yang diikuti oleh user admin Anda. Tentukan salah satu pendekatan tersebut secara sengaja, bukan melonggarkan permission file menjadi 644.)

Mengapa menggunakan streaming. client.messages.stream mencetak token saat token tersebut tiba, bukan menunggu seluruh proses generasi selesai dalam keadaan tanpa output. Pendekatan ini juga menghindari HTTP timeout pada output panjang. SDK akan menolak nilai max_tokens yang sangat besar pada pemanggilan non-streaming karena alasan tersebut. Jika Anda memerlukan object yang sudah dirangkai setelahnya, panggil stream.get_final_message() di dalam blok with.

Mengapa urutan exception seperti itu. SDK menghasilkan typed exception dengan urutan dari yang paling spesifik: RateLimitError adalah 429 dan membawa header retry-after yang memberi tahu berapa lama Anda harus menunggu; APIStatusError mencakup respons non-2xx lainnya. Periksa e.status_code >= 500 untuk masalah pada sisi server. APIConnectionError berarti request sama sekali tidak menerima respons. Sebelum membuat loop retry, perhatikan bahwa SDK sudah melakukan retry untuk error 429 dan 5xx secara otomatis, dua kali secara default dengan exponential backoff (max_retries pada client). Saat except Anda dijalankan, seluruh retry tersebut sudah habis. Karena itu, tindakan yang tepat dalam CLI adalah melaporkan error lalu keluar, bukan menunggu dan terus mengirim request.

Pengendalian biaya

Bagian ini perlu dibahas secara khusus karena API tidak memiliki batas bulanan bawaan selain batas yang Anda konfigurasi, dan setiap kesalahan di sini dapat menambah biaya tanpa disadari.

max_tokens adalah batas maksimum biaya per panggilan. Token output merupakan komponen yang lebih mahal. Pada Opus 4.8, harganya lima kali lipat dari harga input, dan max_tokens adalah batas maksimum jumlah token yang boleh dihasilkan model. Prompt yang berjalan tanpa kendali tidak dapat menghasilkan biaya output melebihi batas yang Anda tetapkan. Sesuaikan nilainya dengan pekerjaan: 1,500 sudah cukup untuk diagnosis log, sedangkan tugas klasifikasi memerlukan 100. Jika respons berhenti di tengah kalimat dengan stop_reason: "max_tokens", berarti nilainya terlalu rendah. Naikkan secara sadar, bukan dengan menetapkannya sangat besar secara default.

Hitung sebelum mengirim. Input juga dikenai biaya, sedangkan log dapat berukuran besar. API memiliki endpoint penghitung yang dapat digunakan secara gratis. Endpoint ini memiliki rate limit sendiri yang 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 tersebut agar tidak secara tidak sengaja meneruskan log berukuran 2 GB melalui tool. Jangan gunakan tiktoken untuk tujuan ini. Itu adalah tokenizer milik OpenAI, dan biasanya menghitung token Claude sekitar 15–20% lebih rendah pada teks umum, serta lebih rendah lagi pada kode.

Pilih model berdasarkan tugas, bukan karena loyalitas. Per Juli 2026, Opus 4.8 (claude-opus-4-8) mengenakan biaya $5 per juta token input dan $25 per juta token output. Haiku 4.5 (claude-haiku-4-5) mengenakan biaya $1/$5 dengan context 200K. Sonnet 5 (claude-sonnet-5) berada di antara keduanya, dengan biaya $3/$15 dan harga perkenalan $2/$10 hingga August 31, 2026. Secara konkret, potongan log berukuran 2,000 token dengan jawaban 500 token berbiaya sekitar $0.0225 pada Opus dan $0.0045 pada Haiku. Mulailah dengan Opus saat menilai kualitas output, lalu uji prompt yang sama pada Haiku. Untuk transformasi sederhana bervolume tinggi, hasilnya sering kali tidak dapat dibedakan, dengan biaya seperlima dari Opus. Verifikasi angka terbaru pada halaman pricing sebelum memasukkan angka ini secara tetap ke dalam anggaran.

Gunakan Batches untuk pekerjaan yang dapat ditunda. Batches API memproses request secara asynchronous dengan biaya 50% dari harga standar, dan sebagian besar batch selesai dalam waktu satu jam. Digest malam, backfill, klasifikasi massal, serta pekerjaan lain yang tidak memerlukan manusia menunggu sebaiknya menggunakan fitur ini.

Gunakan prompt caching untuk context yang berulang. Jika setiap panggilan mengirim ulang system prompt atau runbook besar yang sama, tandai sebagai dapat disimpan dalam cache:

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 on

Penulisan cache berbiaya sekitar 1.25x harga input, sedangkan pembacaan cache berbiaya sekitar 0.1x, dengan TTL 5 menit. Karena itu, panggilan kedua dalam jangka waktu tersebut sudah menutup biaya panggilan pertama. Ada dua hal yang perlu diperhatikan. Prefix yang disimpan dalam cache harus memenuhi minimum per-model, yaitu beberapa ribu token pada Opus. Karena itu, system prompt yang pendek tidak akan disimpan dalam cache. Selain itu, jika cache_read_input_tokens tetap bernilai nol pada panggilan identik, berarti ada bagian prefix yang berubah pada setiap request. Penyebab yang paling umum adalah timestamp.

Pahami hal-hal yang dihitung sebagai input. System prompt, definisi tool, dan seluruh riwayat yang dikirim ulang pada setiap turn dalam percakapan multi-turn semuanya ditagihkan sebagai token input. Chat loop yang tidak pernah memangkas riwayat akan meningkatkan biaya secara kuadratik. Anda perlu memahami perhitungan lengkapnya sebelum membangun aplikasi percakapan: cara menghitung penggunaan token dan penagihan Claude secara tepat.

Jalankan dengan systemd

Manfaat dari disiplin penggunaan file environment: timer yang merangkum error 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.target
sudo 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 now

Perhatikan manfaat EnvironmentFile=: systemd membaca file milik root dengan mode 600 sebelum beralih ke pengguna tanpa hak istimewa explain. Dengan demikian, proses memperoleh variabel tersebut, sementara pengguna tidak dapat membaca file key. Grup systemd-journal memberikan akses ke log. Uji dengan systemctl start secara manual dan baca journalctl -u log-digest.service. Jangan menunggu hingga 06:15 hanya untuk menemukan typo. Jika pola ini berkembang melampaui pipeline shell, pendekatan key di dalam file environment yang sama dapat langsung diterapkan pada workflow n8n berbasis Claude di server yang sama.

Mode kegagalan dan string yang akan Anda lihat

401 pada key yang valid. Exception tersebut berbunyi:

anthropic.AuthenticationError: Error code: 401 - {'type': 'error', 'error': {'type': 'authentication_error', 'message': 'invalid x-api-key'}, 'request_id': 'req_011CSHoEeqs5C35K2UUqR7Fy'}

Jika key berfungsi di shell, tetapi service mengembalikan 401, berarti service tidak pernah menerimanya. Ingat, systemd tidak membaca .bashrc; periksa apakah EnvironmentFile= mengarah ke path yang benar. Penyebab lain adalah tanda kutip yang ikut ditempel ke file env (ANTHROPIC_API_KEY="sk-ant-..."; systemd menghapus tanda kutip tersebut, tetapi wrapper shell Anda dengan . file mempertahankannya dalam nilai jika Anda menggunakan tanda kutip dengan cara yang tidak tepat), spasi di akhir baris, atau key yang Anda cabut melalui Console minggu lalu.

404 akibat typo pada model. Bentuk yang paling sering terjadi 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 harus sama persis seperti yang tertulis, claude-opus-4-8, claude-haiku-4-5, claude-sonnet-5. Salin ID tersebut dari dokumentasi model, bukan dari ingatan atau tutorial lama.

429 rate_limit_error. String jenis error adalah rate_limit_error, dan respons menyertakan header retry-after yang berisi jumlah detik untuk menunggu. SDK sudah mencoba ulang dua kali dengan backoff sebelum Anda melihat exception tersebut. Jadi, 429 yang terus-menerus berarti laju permintaan berkelanjutan Anda memang melebihi tier. Kelompokkan pekerjaan atau sebarkan waktunya. Jangan memperketat loop percobaan ulang.

Yang tercetak adalah objek, bukan teks. Output terlihat seperti [TextBlock(citations=None, text='...', type='text')]. Anda mencetak response.content, bukan melakukan iterasi pada blok dan membaca .text dari blok yang memenuhi block.type == "text". Semua contoh SDK di atas sudah melakukannya dengan benar. Salin loop tersebut.

error: externally-managed-environment. Anda menjalankan pip install pada Python sistem milik Ubuntu 24.04. Gunakan venv. Jangan pernah menjalankan --break-system-packages pada server yang penting bagi Anda.

Jawaban terpotong. response.stop_reason == "max_tokens" berarti model mencapai batas output di tengah proses penjelasan. Ini sesuai rancangan. Naikkan batas tersebut secara sengaja.

Setelah aplikasi pertama Anda berhasil berjalan, membangun agen AI dengan Claude mengubah pemanggilan API yang sama menjadi agen yang menggunakan tools.

FAQ

Berapa biaya Claude API untuk uji coba?

Biayanya sangat kecil untuk alat seperti ini. Per Juli 2026, Opus 4.8 berbiaya $5 per satu juta token input dan $25 per satu juta token output. Dengan demikian, diagnosis log yang umum, dengan beberapa ribu token input dan beberapa ratus token output, biayanya sekitar dua sen. Dengan Haiku 4.5 ($1/$5), biayanya kurang dari setengah sen. Digest harian selama sebulan biayanya kurang dari harga secangkir kopi. Risikonya bukan biaya per pemanggilan, melainkan loop tanpa batas dan max_tokens tanpa batas. Karena itu, keduanya ditetapkan secara eksplisit dalam panduan ini.

Apakah Claude API memiliki tingkat gratis?

Tidak ada tingkat gratis yang berkelanjutan per Juli 2026. Dokumentasi harga Anthropic menyebutkan bahwa pengguna baru menerima sejumlah kecil kredit gratis untuk menguji API sebagai uji coba satu kali. Jumlah pastinya ditampilkan di Console saat pendaftaran. Setelah itu, Anda harus mengisi saldo akun. Jika tujuan Anda adalah biaya marginal nol per permintaan, bukan kualitas terdepan, alternatifnya adalah meng-host sendiri model open-weight dengan Ollama dan membayar menggunakan RAM, bukan token.

Bagaimana cara menjaga keamanan API key di server?

Jangan pernah menyimpannya di dalam kode, git, atau mengekspornya dari .bashrc. Jangan pula mengetikkannya ke shell karena riwayat shell akan menyimpannya. Simpan key dalam file milik root dengan permission 600. Muat key untuk setiap proses. Gunakan wrapper script untuk penggunaan interaktif dan EnvironmentFile= untuk systemd. Batasi satu key untuk setiap server atau project agar pencabutan key yang bocor menjadi tindakan terarah, bukan kerusakan besar. Jika key pernah masuk ke situs paste atau commit git, segera cabut key tersebut di Console. Menghapus commit tidak membuat key yang telah bocor menjadi aman kembali.

Model Claude mana yang sebaiknya saya gunakan terlebih dahulu?

Mulailah dengan claude-opus-4-8 saat Anda mengevaluasi apakah output-nya cukup baik untuk dijadikan dasar pengembangan. Anda juga dapat menilai ide tersebut dengan kualitas penuh. Pada volume penggunaan hobi, perbedaan biayanya hanya beberapa sen. Setelah prompt ditetapkan, jalankan kembali input nyata Anda pada claude-haiku-4-5. Untuk peringkasan, klasifikasi, dan triase log, model ini sering kali sama baiknya dengan biaya seperlima. Beralihlah ke Haiku atau Sonnet berdasarkan hasil pengukuran, bukan sebagai pilihan default.