Anda dapat membangun AI chatbot yang melakukan streaming satu balasan dalam satu sore. Menjaganya tetap hidup setelah 429 pertama adalah pekerjaan yang sebenarnya. Kami membangun layanan FastAPI kecil dengan streaming SSE, memori percakapan sisi server, dan daftar izinkan (allowlist) model, semuanya di balik satu kunci TokenLab. Tujuannya adalah backend yang dapat Anda letakkan di balik antarmuka produk nyata dan mengubah model tanpa menulis ulang integrasi. Jika Anda belum melakukan penukaran base URL, baca panduan migrasi terlebih dahulu. Jika kekhawatiran utama Anda adalah backoff di bawah beban, pasangkan panduan ini dengan panduan pembatasan laju AI API.
Poin Penting
- Chatbot yang siap produksi membutuhkan enam bagian: endpoint sinkron, endpoint streaming, status percakapan sisi server, daftar izinkan model, penanganan error nyata, dan jalur peningkatan penyimpanan.
- Muat kunci dari
TOKENLAB_API_KEYdi setiap proses dan simpan di server. Dokumentasi autentikasi (diamati 2026-10-03) menyatakan jangan pernah mengekspos kunci dalam kode sisi klien atau melakukan commit pada kunci tersebut. GET /v1/modelsbersifat publik, jadi respons yang berhasil tidak membuktikan kunci Anda berfungsi. Buktikan dengan satu permintaan chat nyata (Quickstart, diamati 2026-10-03).- SSE mencakup sebagian besar produk chat. Perlakukan stream yang mati setelah output tiba sebagai tidak lengkap, bukan sebagai jawaban selesai.
- Daftar izinkan dalam panduan ini memetakan tier ke
deepseek-v4-flash,gpt-5.5,claude-sonnet-5, dangemini-3.5-flash. Masing-masing mencantumkanopenai_chat_completionssebagai format yang diterima (diamati 2026-10-03). - Sebagai perkiraan dari harga di bawah, satu giliran dengan 2.000 token input dan 500 token output menelan biaya sekitar $0,0006 hingga $0,0075, tergantung pada modelnya.
Membangun AI Chatbot: Pengaturan dan Layanan Inti
Layanan yang sudah jadi memiliki enam bagian yang bergerak:
- Endpoint
/chatsinkron untuk pengujian smoke test. - Endpoint
/chat/streamstreaming untuk UI nyata. - Status percakapan yang dikunci oleh
conversation_id. - Daftar izinkan model, sehingga frontend tidak dapat meminta ID arbitrer.
- Penanganan error yang tidak runtuh pada 429 pertama.
- Jalur dari prototipe dalam memori ke Redis atau PostgreSQL.
Itu sudah cukup untuk bot dukungan, asisten internal, atau versi pertama dari widget chat tertanam. Setiap blok kode di bawah ini ditambahkan ke satu file, main.py, dalam urutan yang ditampilkan.
Langkah 1: Atur kunci dan instal stack
Buat kunci di bawah API Keys di workspace Anda. Nilai lengkapnya hanya ditampilkan sekali, dan awalan dalam daftar kunci tidak dapat mengautentikasi permintaan. Semua kunci TokenLab dimulai dengan sk-. Periksa saldo Anda di Billing juga, karena permintaan pertama ditagih sesuai harga model saat ini (Quickstart, diamati 2026-10-03).
Simpan kunci dalam variabel lingkungan, jangan pernah di sumber. Prompt di bawah ini menjaganya agar tidak masuk ke baris perintah dan riwayat shell, dan hanya berlaku untuk terminal saat ini serta proses turunannya. Gunakan pengelola rahasia untuk aplikasi yang dideploy. Quickstart juga memiliki versi PowerShell.
printf 'TokenLab API key: '
read -r -s TOKENLAB_API_KEY
printf '\n'
export TOKENLAB_API_KEY
pip install fastapi uvicorn openai pydantic
Anda tidak memerlukan SDK khusus TokenLab. SDK OpenAI resmi berfungsi setelah Anda mengubah kunci dan base URL (panduan SDK, diamati 2026-10-03).
Langkah 2: Tentukan skema, daftar izinkan, dan memori
Kita mulai dengan bagian yang dibagikan. Skema permintaan menggunakan tier, bukan string model mentah, dan setiap contoh curl nanti dalam panduan ini mengirimkan bidang yang sama.
Demo chatbot pertama biasanya menyimpan transkrip di browser dan mengirimnya kembali setiap giliran. Misalnya, itu berfungsi untuk prototipe. Hal ini menjadi berantakan setelah Anda memerlukan percobaan ulang, sesi yang dapat dilanjutkan, atau alat sisi server. Jadi server memiliki riwayat sejak awal.
import json
import logging
import os
import time
import uuid
from collections import defaultdict
from fastapi import FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from openai import APIConnectionError, APIStatusError, OpenAI, RateLimitError
from pydantic import BaseModel, Field
log = logging.getLogger("chatbot")
app = FastAPI()
client = OpenAI(
api_key=os.environ["TOKENLAB_API_KEY"],
base_url="https://api.tokenlab.sh/v1",
timeout=30.0,
max_retries=0,
)
ALLOWED_MODELS = {
"default": "deepseek-v4-flash",
"flagship": "gpt-5.5",
"balanced": "claude-sonnet-5",
"budget": "gemini-3.5-flash",
}
SYSTEM_PROMPT = "You are a helpful assistant. Be concise and direct."
MAX_HISTORY = 20 # pesan disimpan per percakapan (pilihan Anda)
MAX_REPLY_TOKENS = 1000 # batas output per giliran (pilihan Anda)
class ChatRequest(BaseModel):
message: str = Field(min_length=1, max_length=4000)
tier: str = "default"
conversation_id: str | None = None
def resolve_model(tier: str) -> str:
model = ALLOWED_MODELS.get(tier)
if model is None:
raise HTTPException(status_code=400, detail=f"unknown tier: {tier}")
return model
conversations: dict[str, list[dict]] = defaultdict(list)
def build_messages(conv_id: str, user_msg: str) -> list[dict]:
history = conversations[conv_id][-MAX_HISTORY:]
return [
{"role": "system", "content": SYSTEM_PROMPT},
*history,
{"role": "user", "content": user_msg},
]
def remember(conv_id: str, user_msg: str, reply: str) -> None:
conversations[conv_id].append({"role": "user", "content": user_msg})
conversations[conv_id].append({"role": "assistant", "content": reply})
Klien menetapkan max_retries=0 dan timeout 30 detik, sesuai dengan Quickstart. Kami membuat keputusan percobaan ulang sendiri, karena percobaan ulang SDK buta pada giliran yang di-stream dapat mengulangi biaya.
Langkah 3: Petakan error dan tambahkan endpoint sinkron
Kami menyimpan satu fungsi kecil yang mengubah pengecualian SDK apa pun menjadi kode status dan pesan yang aman bagi pengguna. Detailnya masuk ke log Anda, bukan ke pengguna.
def describe_error(exc: Exception) -> tuple[int, str]:
if isinstance(exc, RateLimitError): # periksa dulu: ini adalah subkelas dari APIStatusError
return 429, "The assistant is busy. Please retry shortly."
if isinstance(exc, APIConnectionError):
return 502, "Connection problem. Please retry."
if isinstance(exc, APIStatusError):
log.error("upstream status=%s code=%s", exc.status_code, exc.code)
if exc.status_code in (401, 402, 403): # kunci, saldo, atau izin: masalah operator
return 503, "The assistant is unavailable right now."
if exc.status_code in (400, 404): # bidang buruk atau ID model: bug kami
return 500, "Something went wrong on our side."
return 502, "The assistant is unavailable right now."
return 500, "Something went wrong."
def retry_delay(exc: RateLimitError) -> float:
try:
return min(float(exc.response.headers.get("retry-after", "2")), 10.0)
except ValueError:
return 2.0
def create_with_one_retry(**kwargs):
try:
return client.chat.completions.create(**kwargs)
except RateLimitError as exc:
time.sleep(retry_delay(exc))
return client.chat.completions.create(**kwargs)
@app.post("/chat")
def chat(req: ChatRequest):
model = resolve_model(req.tier)
conv_id = req.conversation_id or str(uuid.uuid4())
messages = build_messages(conv_id, req.message)
try:
response = create_with_one_retry(
model=model, messages=messages, max_tokens=MAX_REPLY_TOKENS
)
except (APIConnectionError, APIStatusError) as exc:
status, message = describe_error(exc)
raise HTTPException(status_code=status, detail=message)
reply = response.choices[0].message.content or ""
log.info("model=%s usage=%s", model, response.usage)
remember(conv_id, req.message, reply)
return {"conversation_id": conv_id, "reply": reply}
Jalankan satu permintaan sebelum membangun apa pun. Jika ini gagal, jangan terus membangun di atasnya.
Stream Balasan dan Jaga AI Chatbot Tetap Responsif
Sebagian besar produk chatbot terasa lambat bukan karena modelnya lambat, tetapi karena UI tetap kosong sampai respons penuh mendarat. Kami menambahkan SSE karena mencakup sebagian besar produk chat dan membawa beban operasional yang lebih sedikit daripada websocket. Jika produk Anda sudah menjalankan klien browser melalui HTTP biasa, SSE menjaga arsitektur tetap lebih sederhana.
Langkah 4: Tambahkan endpoint streaming
Dokumentasi streaming (panduan Streaming, diamati 2026-10-03) menjaga streaming Chat Completions tetap valid untuk framework yang sudah mengharapkan potongan SSE dari /v1/chat/completions. Kami tetap pada jalur itu karena keempat model yang diizinkan menerimanya. Kami mengenkode setiap event sebagai JSON, sehingga baris baru di dalam balasan tidak dapat merusak framing.
def sse(payload: dict) -> str:
return f"data: {json.dumps(payload)}\n\n"
@app.post("/chat/stream")
def chat_stream(req: ChatRequest):
model = resolve_model(req.tier)
conv_id = req.conversation_id or str(uuid.uuid4())
messages = build_messages(conv_id, req.message)
def generate():
parts: list[str] = []
finish_reason = None
try:
with client.chat.completions.create(
model=model,
messages=messages,
max_tokens=MAX_REPLY_TOKENS,
stream=True,
stream_options={"include_usage": True},
) as stream:
for chunk in stream:
if chunk.usage:
log.info("model=%s usage=%s", model, chunk.usage)
if not chunk.choices:
continue
choice = chunk.choices[0]
if choice.delta.content:
parts.append(choice.delta.content)
yield sse({"delta": choice.delta.content})
if choice.finish_reason:
finish_reason = choice.finish_reason
except (APIConnectionError, APIStatusError) as exc:
_, message = describe_error(exc)
yield sse({"error": message})
return
if finish_reason != "stop":
yield sse({"error": f"Reply incomplete ({finish_reason})."})
return
remember(conv_id, req.message, "".join(parts))
yield sse({"done": True, "conversation_id": conv_id})
return StreamingResponse(generate(), media_type="text/event-stream")
Dua detail penting di sini. Pertama, include_usage: true menambahkan penggunaan token ke potongan stream, dan potongan penggunaan akhir tidak memiliki choices, jadi loop melewatinya. Kedua, kami menyimpan giliran hanya ketika finish_reason adalah stop. Penyelesaian length berarti batas token memotong balasan, dan dokumentasi mencantumkan keduanya sebagai kondisi akhir yang normal.
Karena error muncul di dalam generator, status HTTP sudah 200 saat kegagalan terjadi. Klien harus membaca event error. Blok with menutup stream upstream saat generator keluar. Dokumentasi menyarankan untuk membatalkan upstream saat pengguna berhenti atau terputus, jadi uji apakah server ASGI Anda benar-benar menghentikan generator saat terputus.
Langkah 5: Baca stream di browser
Browser harus memanggil backend Anda sendiri yang terautentikasi dan tidak pernah memanggil TokenLab secara langsung. Parse berdasarkan batas event, karena satu potongan jaringan belum tentu satu event SSE yang lengkap.
async function sendMessage(payload, onDelta) {
const response = await fetch('/chat/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload), // { message, tier, conversation_id }
});
if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const events = buffer.split('\n\n');
buffer = events.pop();
for (const raw of events) {
if (!raw.startsWith('data: ')) continue;
const event = JSON.parse(raw.slice(6));
if (event.error) throw new Error(event.error);
if (event.delta) onDelta(event.delta);
}
}
}
Tingkatkan memori ke Redis saat Anda membutuhkannya
Kamus dalam memori baik untuk satu proses. Gunakan Redis saat percakapan memerlukan TTL, kemampuan untuk dilanjutkan, atau beberapa instance. Gunakan PostgreSQL saat transkrip adalah data produk yang harus Anda kueri, audit, atau laporkan. Perubahan Redis adalah pemipaan penyimpanan, bukan logika. Jalankan pip install redis, lalu buat build_messages dan remember membaca serta menulis melalui helper ini.
import redis
redis_client = redis.Redis(
host=os.environ.get("REDIS_HOST", "127.0.0.1"), port=6379, decode_responses=True
)
def load_history(conv_id: str) -> list:
raw = redis_client.get(f"chat:{conv_id}")
return json.loads(raw) if raw else []
def save_history(conv_id: str, history: list) -> None:
redis_client.setex(f"chat:{conv_id}", 60 * 60 * 24, json.dumps(history))
Tangani Error Saat Anda Membangun AI Chatbot untuk Produksi
Pengguna yang berhadapan dengan pelanggan tidak peduli apakah kegagalan berasal dari pembatasan laju, saldo kosong, atau pemadaman upstream. Mereka peduli apakah UI membeku. Bayangkan widget dukungan yang stream-nya mati setelah empat puluh kata. Dokumentasi TokenLab mengatakan stream yang terputus setelah output tiba bukanlah respons lengkap, dan mengulanginya dapat menghasilkan output yang berbeda dan biaya kedua (Tangani error API, diamati 2026-10-03). Itulah mengapa Langkah 4 tidak pernah menyimpan giliran yang belum selesai.
Langkah 6: Terapkan aturan percobaan ulang
Tangani error berdasarkan status HTTP dan code, bukan berdasarkan message yang dapat dibaca manusia, yang dapat berubah tanpa pemberitahuan. Tabel ini menggabungkan dokumentasi dengan apa yang dilakukan layanan kami.
| Status | Arti (per dokumentasi) | Apa yang dilakukan layanan ini |
|---|---|---|
400 |
Bidang, ID model, atau input tidak valid | Kembalikan 500, catat, jangan pernah coba lagi |
401 |
Kunci hilang, tidak valid, kedaluwarsa, atau dicabut | Kembalikan 503, peringatkan operator |
402 |
Saldo atau batas API-key terlalu rendah (insufficient_balance, quota_exceeded) |
Kembalikan 503, peringatkan operator |
403 |
Kunci tidak dapat menggunakan model ini | Kembalikan 503, periksa daftar model kunci |
404 |
Sumber daya atau model tidak ditemukan | Kembalikan 500, periksa ID terhadap /v1/models |
429 |
Batas permintaan tercapai | Tunggu Retry-After, coba lagi sekali (hanya sinkron) |
500–504 |
Layanan tidak tersedia atau kegagalan jaringan | Coba lagi hanya jika retryable adalah true; hormati retry_after |
Dua peringatan dari dokumentasi. 503 all_channels_failed tidak selalu berarti pemadaman sementara. Jika retryable adalah false, jangan ulangi permintaan yang sama, dan periksa GET /v1/models sebelum memilih model lain. Selain itu, Chat Completions dan Responses menggunakan objek error gaya OpenAI, tetapi Anthropic Messages dan Gemini menyimpan format mereka sendiri. Layanan kami memanggil Chat Completions pada setiap model yang diizinkan, jadi satu parser berfungsi. Jika Anda memindahkan tier ke format asli, Anda memerlukan parser kedua.
Simpan ID Permintaan dari header respons dengan endpoint, model, waktu, dan ID pengguna atau pekerjaan Anda sendiri. Dokumentasi tidak menyebutkan header di halaman yang kami gunakan, jadi konfirmasikan terhadap jalur pemecahan masalah sebelum Anda menghubungkan log Anda.
Sisa daftar periksa produksi singkat:
- Tambahkan ID permintaan Anda sendiri agar Anda dapat menghubungkan kegagalan frontend ke log backend.
- Batasi konkurensi per pengguna dan ukuran permintaan.
max_length=4000padamessageadalah titik awal yang harus Anda sesuaikan. - Batasi riwayat berdasarkan jumlah pesan, seperti yang dilakukan
MAX_HISTORY. Dokumentasi penagihan mengatakan tokenisasi berbeda berdasarkan model, bahasa, dan input media, jadi rumus jumlah karakter bukanlah perkiraan penagihan yang andal. - Catat model, latensi, penggunaan, dan alasan penyelesaian pada setiap giliran.
- Jaga agar pesan yang terlihat pengguna tetap terpisah dari detail error internal.
- Kirim satu giliran pengujian ke tier alternatif, sehingga Anda tahu fallback berfungsi sebelum pemadaman nyata pertama.
Pilih Model dan Perkirakan Biaya untuk AI Chatbot Anda
Jangan pernah biarkan frontend meneruskan string model arbitrer ke API. Bidang teks bebas mengundang kesalahan ketik, model yang sudah pensiun, dan model yang tidak pernah Anda maksudkan untuk diekspos. Peta ALLOWED_MODELS memberi Anda satu tempat untuk menukar model saat penyedia menghentikan satu model, tanpa pembaruan klien. ID model yang tidak dikenal mengembalikan model_not_found, dan dokumentasi mengarahkan Anda ke /v1/models untuk ID saat ini. Periksa direktori model sebelum Anda mengunci daftar izinkan.
Harga saat ini per 1 juta token
Ini adalah harga TokenLab dalam USD dari entri API langsung setiap model, diamati 2026-10-03. Harga terakhir diperbarui 2026-10-02T16:53:30.068Z. Keempat model menunjukkan tahap siklus hidup active.
| Tier | ID Model | Input / 1M | Output / 1M | Cache read / 1M | Sumber |
|---|---|---|---|---|---|
| default | deepseek-v4-flash (off-peak) |
$0.15 | $0.60 | $0.003 | model API |
| default | deepseek-v4-flash (peak) |
$0.30 | $1.20 | $0.006 | model API |
| flagship | gpt-5.5 |
$1.50 | $9.00 | $0.15 | model API |
| balanced | claude-sonnet-5 |
$0.90 | $4.50 | $0.09 | model API |
| budget | gemini-3.5-flash |
$0.75 | $4.50 | $0.075 | model API |
Untuk deepseek-v4-flash, entri mencantumkan jendela puncak pada hari kerja, tidak termasuk hari libur nasional Tiongkok. Off-peak mencakup akhir pekan dan hari libur tersebut. claude-sonnet-5 juga mencantumkan harga tulis cache sebesar $1,125 per 1 juta token.
Satu permintaan yang berhasil
Asumsikan satu giliran dengan 2.000 token input dan 500 token output. Ukuran itu adalah asumsi kami, bukan pengukuran. Untuk gpt-5.5:
- Input: 2.000 × $1,50 / 1.000.000 = $0,0030
- Output: 500 × $9,00 / 1.000.000 = $0,0045
- Total: $0,0075 per giliran (perkiraan, tidak ada cache hit)
Menggunakan aritmatika yang sama, kita mendapatkan perkiraan ini: deepseek-v4-flash menelan biaya $0,0006 saat off-peak dan $0,0012 saat peak, claude-sonnet-5 menelan biaya $0,00405, dan gemini-3.5-flash menelan biaya $0,00375. Selama 1.000 giliran tersebut, itu kira-kira $7,50 pada gpt-5.5 dibandingkan $0,60 hingga $1,20 pada deepseek-v4-flash. Giliran nyata tumbuh seiring akumulasi riwayat, jadi baca usage dari respons atau potongan stream akhir daripada mempercayai ukuran ini.
Dalam praktiknya, kami merutekan sebagian besar percakapan ke tier default dan mencadangkan gpt-5.5 atau claude-sonnet-5 untuk giliran yang membutuhkannya. Dokumentasi penagihan memperingatkan bahwa harga terendah per token tidak selalu merupakan biaya terendah per tugas yang diselesaikan. Jalankan prompt yang sama melalui dua tier dan bandingkan biaya akhir di Usage.
Dua pemeriksaan sebelum Anda mengirim. Dokumentasi penagihan mengatakan jangan melakukan hard-code pada tabel harga yang disalin, jadi baca GET /v1/models/:model/pricing jika aplikasi Anda menampilkan biaya (Penagihan dan harga, diamati 2026-10-03). Selain itu, entri deepseek-v4-flash mencantumkan json-mode, prompt-cache, dan tool-use tetapi tidak vision. Jika chatbot Anda menerima gambar, rutekan giliran tersebut ke tier yang entri-nya mencantumkan vision.
Smoke Test dan Kirim AI Chatbot Anda
Langkah 7: Jalankan dari ujung ke ujung
Jalankan server di terminal di mana TOKENLAB_API_KEY diekspor. Payload curl menggunakan bidang message, tier, dan conversation_id yang sama dengan ChatRequest.
uvicorn main:app --reload --port 8000
# 1. Endpoint sinkron: harapkan balasan singkat dan conversation_id
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"message": "Reply only with OK.", "tier": "default"}'
# 2. Streaming: harapkan baris data: {"delta": ...}, lalu {"done": true, ...}
curl -N -X POST http://localhost:8000/chat/stream \
-H "Content-Type: application/json" \
-d '{"message": "Hello!", "tier": "default"}'
# 3. Memori: gunakan kembali conversation_id yang dikembalikan di atas
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"message": "What did I just say?", "tier": "default", "conversation_id": "PASTE_ID_FROM_STEP_1"}'
# 4. Kegagalan paksa: harapkan HTTP 400 dan "unknown tier: nope"
curl -i -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"message": "Hello!", "tier": "nope"}'
Untuk memaksa kegagalan upstream, mulai ulang server dengan TOKENLAB_API_KEY yang salah. Klien Anda harus mendapatkan pesan 503 yang bersih, dan log Anda harus menunjukkan 401 upstream dengan invalid_api_key. Kemudian buka Requests untuk mengonfirmasi model, status, dan penggunaan panggilan yang berhasil. Jika Anda dapat melakukan stream satu giliran, mempertahankan satu percakapan, dan mengembalikan error yang bersih pada kegagalan paksa, Anda memiliki fondasi yang tepat.
Setelah itu, peningkatan berikutnya adalah infrastruktur yang membosankan daripada AI tambahan:
- Autentikasi, sehingga satu pengguna tidak dapat membaca percakapan pengguna lain.
- Persistensi, sehingga sesi bertahan setelah deploy.
- Pembatasan laju per pengguna, sehingga satu pengguna yang berisik tidak dapat menghabiskan kuota Anda.
- Atribusi penggunaan, jika chatbot berhadapan dengan pelanggan.
- Ringkasan latar belakang, jika percakapan memerlukan memori jangka panjang.
Setelah migrasi base URL selesai, perubahan model hanyalah pengeditan konfigurasi ke ALLOWED_MODELS.
FAQ
Apakah saya memerlukan websocket untuk membangun AI chatbot?
Tidak. SSE, yang ditunjukkan di Langkah 4, mencakup sebagian besar produk chat. Websocket memberikan nilai saat Anda memerlukan push dua arah di luar permintaan dan respons, seperti kolaborasi langsung atau event yang dimulai server. Untuk UI chat standar, SSE lebih sederhana untuk dideploy dan di-debug.
Model mana yang harus digunakan chatbot saya secara default?
Mulai dengan tier harga rendah seperti deepseek-v4-flash dan tambahkan claude-sonnet-5 atau gpt-5.5 di balik daftar izinkan untuk giliran yang lebih sulit. Harga di atas, diamati 2026-10-03, menunjukkan kesenjangannya. Kemudian uji prompt Anda sendiri pada dua tier dan bandingkan biaya akhir di Usage. Periksa direktori model sebelum Anda berkomitmen, karena jajaran berubah.
Mengapa chatbot saya mengembalikan 402 atau 429?
402 berarti saldo akun atau batas penggunaan kunci itu sendiri terlalu rendah. Kodenya adalah insufficient_balance dan quota_exceeded, dan mengulangi permintaan tidak akan membantu. 429 berarti Anda mencapai batas permintaan, jadi tunggu Retry-After lalu coba lagi. Endpoint sinkron kami mencoba lagi sekali dan endpoint streaming melaporkan error ke klien.
Bisakah browser memanggil TokenLab secara langsung?
Tidak dengan aman. Dokumentasi Autentikasi mengatakan jangan pernah mengekspos kunci API dalam kode sisi klien. Simpan kunci di lingkungan server dan buat browser memanggil backend Anda sendiri yang terautentikasi, seperti yang dilakukan sampel frontend.
Buat kunci API di tokenlab.sh, tambahkan dana, dan jalankan smoke test di atas.
Sumber
Harga diamati pada 2026-10-03
- TokenLab Docs: QuickstartDiamati pada 2026-10-03
- TokenLab Docs: AuthenticationDiamati pada 2026-10-03
- TokenLab Docs: StreamingDiamati pada 2026-10-03
- TokenLab Docs: Handle API errorsDiamati pada 2026-10-03
- TokenLab Docs: Billing and pricingDiamati pada 2026-10-03
- TokenLab Docs: SDKs & LibrariesDiamati pada 2026-10-03
- TokenLab Docs: Create Chat CompletionDiamati pada 2026-10-03
- TokenLab Docs: List ModelsDiamati pada 2026-10-03



