OpenAI API adalah endpoint HTTP biasa: server-mu mengirim POST berisi JSON, OpenAI membalas JSON. SDK (Software Development Kit) adalah library resmi yang membungkus request itu supaya lebih enak dipakai. Bayangin memesan di loket gudang pusat pakai kartu member. Petugas mengecek kartumu dulu; kartu salah, pesanan ditolak. Kalau antrean penuh, dia bilang "balik lagi 2 detik". Tiap pesanan dapat struk berisi jumlah barang, dan struk itu yang jadi dasar tagihan. Kartu member itu API key, penolakan itu status 401, "balik lagi 2 detik" itu 429 dengan header Retry-After, dan struknya itu usage.
🧪 Tebakan cepat: kamu memanggil API pakai
fetchbiasa, lalu menulisconst data = await res.json(); console.log(data.output_text). Yang tercetak apa: jawaban modelnya, atauundefined? Tebak dulu, jawabannya ada di bagian "Request pertama dengan fetch".
Responses API: endpoint yang disarankan
Per Oktober 2026, panduan "Migrate to the Responses API" dari OpenAI menulis: Chat Completions (/v1/chat/completions) masih didukung, tapi Responses disarankan untuk semua proyek baru. Endpoint-nya POST https://api.openai.com/v1/responses. Asisten Toko Buku memakai gpt-6-luna dengan reasoning: { effort: "none" }: pertanyaan stok gak perlu model berpikir panjang, dan token "berpikir" ikut ditagih.
Dua parameter lain yang sering dipakai: max_output_tokens, batas panjang jawaban dalam token (potongan teks yang diproses model, bisa satu kata atau bagian kata), dan temperature, angka 0 sampai 2 yang mengatur seberapa acak jawabannya: tinggi lebih bervariasi, rendah lebih fokus dan konsisten. Untuk gpt-6-luna, temperature cuma boleh dikirim kalau reasoning effort-nya none.
Request pertama dengan fetch
Kode ini jalan di server Toko Buku (Node 18 ke atas sudah punya fetch):
async function tanyaModel(pertanyaan) {
const res = await fetch("https://api.openai.com/v1/responses", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
body: JSON.stringify({
model: "gpt-6-luna",
reasoning: { effort: "none" },
instructions: "Kamu asisten Toko Buku. Jawab singkat dalam bahasa Indonesia.",
input: pertanyaan, // plus data stok, seperti di pelajaran Prompt Engineering
max_output_tokens: 300,
}),
signal: AbortSignal.timeout(30_000), // batalkan kalau lebih dari 30 detik
});
if (!res.ok) {
const err = new Error(`OpenAI menjawab ${res.status}`);
err.status = res.status;
err.retryAfter = res.headers.get("retry-after");
err.code = (await res.json().catch(() => ({}))).error?.code; // misalnya "slow_down"
throw err;
}
const data = await res.json();
const pesan = data.output.find((item) => item.type === "message");
const teks = pesan.content.find((part) => part.type === "output_text").text;
return { teks, usage: data.usage };
}
Bentuk response-nya, dipotong dari API reference (isi teks dan angka usage di sini contoh):
{
"id": "resp_...",
"status": "completed",
"model": "gpt-6-luna",
"output": [
{ "type": "message", "role": "assistant",
"content": [{ "type": "output_text", "text": "Laskar Pelangi masih ada, stoknya 12." }] }
],
"usage": { "input_tokens": 400, "output_tokens": 40, "output_tokens_details": { "reasoning_tokens": 0 }, "total_tokens": 440 }
}
outputadalah array. Model reasoning bisa menaruh itemreasoningsebelum itemmessage, jadi cari itemtype: "message", jangan langsungoutput[0].- Teksnya ada di
content, di bagian bertipeoutput_text. output_textgak ada di JSON mentah. API reference menyebutnya properti khusus SDK. Jadidata.output_texthasilnyaundefined. Itu jawaban tebakannya.
Hitung biaya dari usage
Per Oktober 2026, halaman "Pricing" OpenAI menulis harga gpt-6-luna: $0,10 per 1 juta token input dan $0,50 per 1 juta token output.
const hitungBiaya = (usage) => (usage.input_tokens * 0.10 + usage.output_tokens * 0.50) / 1_000_000;
hitungBiaya({ input_tokens: 400, output_tokens: 40 }); // 0.00006 dolar
Satu pertanyaan $0,00006, jadi 10.000 pertanyaan sehari $0,60. Token reasoning ikut dihitung di output_tokens dan ditagih sebagai output, makanya effort: "none" untuk tugas sederhana.
SDK resmi
import OpenAI from "openai"; // npm install openai (versi 7.32.0 di npm, Oktober 2026)
const client = new OpenAI({ timeout: 30 * 1000 }); // OPENAI_API_KEY dibaca dari environment
const response = await client.responses.create({
model: "gpt-6-luna",
reasoning: { effort: "none" },
instructions: "Kamu asisten Toko Buku. Jawab singkat dalam bahasa Indonesia.",
input: "Stok Laskar Pelangi masih ada?",
});
console.log(response.output_text); // di SDK, properti ini ada
console.log(response.usage.input_tokens, response.usage.output_tokens);
Menurut README SDK-nya: tanpa diatur, request dibatalkan setelah 10 menit, dan error koneksi, 408, 409, 429, serta 5xx otomatis diulang 2 kali dengan jeda yang makin panjang (maxRetries untuk mengubahnya). Error-nya berupa class: OpenAI.AuthenticationError untuk 401, OpenAI.RateLimitError untuk 429, OpenAI.APIConnectionTimeoutError untuk timeout.
Kunci di server, browser lewat route-mu
API key gak boleh sampai ke browser. Variabel VITE_... yang dipakai di kode frontend ikut masuk ke bundle JavaScript, dan semua orang bisa membaca bundle. SDK OpenAI pun menolak jalan di browser kecuali kamu menulis dangerouslyAllowBrowser: true. Jadi browser memanggil route Toko Buku, dan route itu yang memanggil OpenAI:
app.post("/api/assistant", async (req, res) => {
const pertanyaan = String(req.body.pertanyaan ?? "").slice(0, 500);
try {
const { teks } = await denganRetry(() => tanyaModel(pertanyaan));
res.json({ jawaban: teks });
} catch (err) {
console.error(err); // log lengkapnya di server, bukan ke browser
res.status(err.name === "TimeoutError" ? 504 : 502).json({ error: "Asisten lagi gak bisa menjawab" });
}
});
Di Laravel caranya sama: Http::withToken(config('services.openai.key'))->timeout(30)->post('https://api.openai.com/v1/responses', [...]) di controller, kuncinya di .env.
Error: 401, 429, dan timeout
| Status | Artinya | Yang dilakukan |
|---|---|---|
401 |
API key salah atau dicabut | Cek kunci. Jangan diulang. |
429 (slow_down, rate limit) |
Request terlalu cepat | Tunggu minimal sesuai Retry-After (detik), lalu ulang beberapa kali saja |
429 (credit_balance_exhausted dan kode batas belanja lain) |
Kredit atau batas belanja habis | Isi kredit atau naikkan batasnya. Mengulang gak menolong. |
503 (server_is_overloaded) |
Model lagi penuh | Ikuti Retry-After, lalu ulang |
TimeoutError |
Gak ada jawaban dalam 30 detik | Kirim pesan "lagi lambat" ke pengguna |
const KUOTA_HABIS = ["credit_balance_exhausted", "organization_spend_limit_exceeded", "project_spend_limit_exceeded", "organization_usage_limit_exceeded"];
async function denganRetry(kerjakan, maxCoba = 3) {
for (let coba = 1; ; coba++) {
try {
return await kerjakan();
} catch (err) {
const bisaDiulang = (err.status === 429 && !KUOTA_HABIS.includes(err.code)) || err.status >= 500;
if (!bisaDiulang || coba === maxCoba) throw err;
const detik = Number(err.retryAfter) || 2 ** coba; // tanpa Retry-After: 2, 4, 8 detik
await new Promise((r) => setTimeout(r, detik * 1000));
}
}
}
Jalannya di Lab, pertanyaan stok yang kena 429 sekali:
| Waktu | Kejadian |
|---|---|
| 0 ms | Percobaan 1: POST /v1/responses |
| 120 ms | 429, header Retry-After: 2 |
| 2.120 ms | Percobaan 2 setelah menunggu 2.000 ms |
| 3.020 ms | 200 OK, usage 400 + 40 token, biaya $0,00006 |
⚠️ Jebakan yang sering ketemu
data.output_textdengan fetch biasa. Hasilnyaundefined. Ambil teks darioutput, atau pakai SDK.- Mengulang semua error.
401dan kredit habis gak sembuh dengan diulang. Ulang cuma 429 sementara dan 5xx, dengan batas percobaan. - Retry dobel. SDK sudah mengulang 2 kali. Kalau kamu bungkus lagi dengan loop sendiri, satu klik bisa jadi 9 request. Matikan salah satunya (
maxRetries: 0). - Kunci di frontend.
VITE_OPENAI_API_KEYikut ter-bundle. Kunci di.envserver, browser cuma tahu/api/assistant.
TL;DR: Pakai Responses API (POST https://api.openai.com/v1/responses), yang per Oktober 2026 disarankan OpenAI untuk proyek baru. Body: model, instructions, input, reasoning, max_output_tokens; temperature (0 sampai 2) mengatur seberapa acak jawabannya, dan untuk gpt-6-luna cuma boleh dengan effort none. Dengan fetch biasa, teksnya ada di item output bertipe message, lalu content bertipe output_text; output_text langsung cuma ada di SDK. Biaya dari usage: gpt-6-luna $0,10 / $0,50 per 1 juta token, jadi 400 + 40 token = $0,00006, dan token reasoning ditagih sebagai output. SDK (npm install openai) memberi client.responses.create, timeout default 10 menit, dan retry otomatis 2 kali. API key cuma di server; browser memanggil route-mu. 401 jangan diulang, 429 tunggu sesuai Retry-After, timeout pakai AbortSignal.timeout.