Panduan Go
Kiosapi kompatibel OpenAI lewat REST biasa. Contoh di bawah pakai net/http + encoding/json standar (tanpa dependensi eksternal) supaya jalan di proyek Go mana pun — untuk kasus chat sederhana, package github.com/sashabaranov/go-openai juga kompatibel (lihat contoh singkat di /docs).
Daftar isi
1. Persiapan
Masuk ke dashboard → Kunci API, beri nama, klik Buat key, salin kios_live_… (ditampilkan sekali).
export KIOSAPI_API_KEY="kios_live_xxxxxxxxxxxx"2. Fungsi helper request
package kiosapi
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
const BaseURL = "https://api.kiosapi.id/v1"
func apiKey() string { return os.Getenv("KIOSAPI_API_KEY") }
func Post(path string, body any, out any) error {
buf, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", BaseURL+path, bytes.NewReader(buf))
req.Header.Set("Authorization", "Bearer "+apiKey())
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return fmt.Errorf("kiosapi error: HTTP %d", resp.StatusCode)
}
return json.NewDecoder(resp.Body).Decode(out)
}
func Get(path string, out any) error {
req, _ := http.NewRequest("GET", BaseURL+path, nil)
req.Header.Set("Authorization", "Bearer "+apiKey())
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(out)
}Model ID berformat provider/nama-model — lihat daftar lengkap via GET /v1/models atau /pricing.
3. Chat completion dasar
type ChatResponse struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
} `json:"choices"`
Usage struct {
TotalTokens int `json:"total_tokens"`
} `json:"usage"`
}
var resp ChatResponse
err := kiosapi.Post("/chat/completions", map[string]any{
"model": "anthropic/claude-sonnet-4-6",
"messages": []map[string]string{
{"role": "system", "content": "Kamu asisten yang ringkas dan ramah."},
{"role": "user", "content": "Jelaskan apa itu RAG dalam 2 kalimat."},
},
}, &resp)
if err != nil {
panic(err)
}
fmt.Println(resp.Choices[0].Message.Content)
fmt.Println("Token dipakai:", resp.Usage.TotalTokens)4. Streaming
Tambahkan "stream": true dan baca respons baris demi baris (SSE).
import "bufio"
import "strings"
body, _ := json.Marshal(map[string]any{
"model": "deepseek/deepseek-v4-flash",
"messages": []map[string]string{
{"role": "user", "content": "Tulis puisi pendek tentang hujan di Jakarta."},
},
"stream": true,
})
req, _ := http.NewRequest("POST", kiosapi.BaseURL+"/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+apiKey())
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
if !strings.HasPrefix(line, "data: ") || line == "data: [DONE]" {
continue
}
var chunk struct {
Choices []struct {
Delta struct {
Content string `json:"content"`
} `json:"delta"`
} `json:"choices"`
}
json.Unmarshal([]byte(strings.TrimPrefix(line, "data: ")), &chunk)
if len(chunk.Choices) > 0 {
fmt.Print(chunk.Choices[0].Delta.Content)
}
}Model reasoning (🧠) bisa mengirim delta reasoning_content terpisah dari content — tambahkan field itu ke struct Delta untuk menampilkan status "sedang berpikir".
5. Tool calling / function calling
Format sama persis OpenAI — didukung untuk model bertanda 🔧 di katalog, termasuk Claude & Gemini (diterjemahkan otomatis oleh gateway).
tools := []map[string]any{{
"type": "function",
"function": map[string]any{
"name": "get_weather",
"description": "Ambil cuaca terkini untuk sebuah kota",
"parameters": map[string]any{
"type": "object",
"properties": map[string]any{"city": map[string]string{"type": "string", "description": "Nama kota"}},
"required": []string{"city"},
},
},
}}
messages := []map[string]any{{"role": "user", "content": "Cuaca di Bandung sekarang gimana?"}}
var resp struct {
Choices []struct {
Message struct {
Content string `json:"content"`
ToolCalls []struct {
ID string `json:"id"`
Function struct {
Name string `json:"name"`
Arguments string `json:"arguments"`
} `json:"function"`
} `json:"tool_calls"`
} `json:"message"`
} `json:"choices"`
}
kiosapi.Post("/chat/completions", map[string]any{
"model": "openai/gpt-4o", "messages": messages, "tools": tools,
}, &resp)
for _, call := range resp.Choices[0].Message.ToolCalls {
fmt.Println("Model minta panggil:", call.Function.Name, call.Function.Arguments)
messages = append(messages, map[string]any{"role": "assistant", "tool_calls": resp.Choices[0].Message.ToolCalls})
messages = append(messages, map[string]any{
"role": "tool", "tool_call_id": call.ID,
"content": `{"suhu_celsius": 27, "kondisi": "berawan"}`,
})
}
var followup ChatResponse
kiosapi.Post("/chat/completions", map[string]any{"model": "openai/gpt-4o", "messages": messages}, &followup)
fmt.Println(followup.Choices[0].Message.Content)6. Vision (kirim gambar ke model)
Kirim gambar lewat content berbentuk array (image_url + text) — format persis yang dipakai perintah lihat di Kiosapi CLI.
import (
"encoding/base64"
"os"
)
raw, _ := os.ReadFile("foto.jpg")
b64 := base64.StdEncoding.EncodeToString(raw)
var resp ChatResponse
kiosapi.Post("/chat/completions", map[string]any{
"model": "google/gemini-2.5-flash", // pilih model yang mendukung vision
"messages": []map[string]any{{
"role": "user",
"content": []map[string]any{
{"type": "text", "text": "Ada apa saja di foto ini?"},
{"type": "image_url", "image_url": map[string]string{"url": "data:image/jpeg;base64," + b64}},
},
}},
}, &resp)
fmt.Println(resp.Choices[0].Message.Content)Catatan: fitur ini sudah jalan di level API/CLI. Tombol upload gambar langsung di dashboard web belum tersedia.
7. Structured output / JSON mode
response_format diteruskan langsung ke provider upstream — jalan penuh untuk model yang mendukungnya secara native. Belum ada lapisan pemaksaan JSON yang seragam untuk semua model di katalog.
var resp ChatResponse
kiosapi.Post("/chat/completions", map[string]any{
"model": "openai/gpt-4o",
"messages": []map[string]string{{"role": "user", "content": "Beri saya data profil singkat dalam JSON: nama, umur."}},
"response_format": map[string]string{"type": "json_object"},
}, &resp)
var data map[string]any
json.Unmarshal([]byte(resp.Choices[0].Message.Content), &data)
fmt.Println(data)8. Embeddings
var resp struct {
Data []struct {
Embedding []float64 `json:"embedding"`
} `json:"data"`
}
kiosapi.Post("/embeddings", map[string]any{
"model": "openai/text-embedding-3-small",
"input": "Selamat datang di Kiosapi!",
}, &resp)
vector := resp.Data[0].Embedding
fmt.Println(len(vector), "dimensi")9. Daftar model & cek saldo
var models any
kiosapi.Get("/models", &models)
// Saldo & kuota gratis harian (endpoint khusus Kiosapi)
var saldo any
kiosapi.Get("/saldo", &saldo)
fmt.Println(saldo)10. Endpoint khusus Kiosapi
Bentuknya beda dari chat completions standar, tapi tetap lewat helper kiosapi.Post/kiosapi.Get yang sama.
Gambar — POST /v1/images/generations (sinkron)
var img struct {
Data []struct {
B64Json string `json:"b64_json"`
} `json:"data"`
Kiosapi struct {
CostRupiah int `json:"cost_rupiah"`
} `json:"kiosapi"`
}
kiosapi.Post("/images/generations", map[string]any{
"model": "google/imagen-3",
"prompt": "kucing oranye memakai topi koki, fotorealistik",
"option": "standard",
"n": 1,
}, &img)
raw, _ := base64.StdEncoding.DecodeString(img.Data[0].B64Json)
os.WriteFile("hasil.png", raw, 0644)
fmt.Println("Biaya:", img.Kiosapi.CostRupiah, "rupiah")Video — POST /v1/videos/generations (asinkron, perlu polling)
import "time"
var submit struct {
JobID string `json:"job_id"`
}
kiosapi.Post("/videos/generations", map[string]any{
"model": "alibaba/wan2.7-t2v",
"prompt": "ombak pantai saat matahari terbenam, sinematik",
"option": "standard",
"duration_seconds": 5,
}, &submit)
var job struct {
Status string `json:"status"`
VideoURL string `json:"video_url"`
Error string `json:"error"`
}
for {
time.Sleep(5 * time.Second)
kiosapi.Get("/jobs/"+submit.JobID, &job)
if job.Status != "running" {
break
}
}
if job.Status == "succeeded" {
fmt.Println("Video siap:", job.VideoURL)
} else {
fmt.Println("Gagal:", job.Error)
}Image-to-video: sertakan image (base64) + image_mime di body request.
Musik — POST /v1/music/generations
var resp struct {
JobID string `json:"job_id"`
}
kiosapi.Post("/music/generations", map[string]any{
"model": "minimax/music-2.6",
"prompt": "pop akustik Indonesia yang hangat, vokal wanita",
"lyrics": "[Verse]\nPagi cerah di kota\n[Chorus]\nBersama kita bisa",
}, &resp)
// poll GET /jobs/{resp.JobID} sampai status "succeeded" → field "audio_url"Text-to-speech — POST /v1/audio/speech
body, _ := json.Marshal(map[string]any{
"model": "minimax/speech-2.8-turbo",
"input": "Selamat datang di Kiosapi!",
"voice": "Indonesian_CalmWoman", // 9 suara asli Indonesia (model MiniMax)
})
req, _ := http.NewRequest("POST", kiosapi.BaseURL+"/audio/speech", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+apiKey())
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
audio, _ := io.ReadAll(resp.Body)
os.WriteFile("suara.mp3", audio, 0644)Reranking — POST /v1/rerank (bentuk Cohere, self-hosted)
var resp struct {
Results []struct {
Index int `json:"index"`
RelevanceScore float64 `json:"relevance_score"`
} `json:"results"`
}
kiosapi.Post("/rerank", map[string]any{
"model": "baai/bge-reranker-base",
"query": "apa itu kucing?",
"documents": []string{
"Kucing adalah hewan mamalia berkaki empat.",
"Mobil listrik semakin populer di Indonesia.",
},
"return_documents": true,
}, &resp)
for _, r := range resp.Results {
fmt.Println(r.Index, r.RelevanceScore)
}Vector DB — POST /v1/vector-indexes/... (bentuk Pinecone, self-hosted)
// 1) Bikin index (gratis)
var created any
kiosapi.Post("/vector-indexes", map[string]any{"name": "artikel-saya"}, &created)
// 2) Buat embedding lalu upsert (values wajib 1536 angka, cocok text-embedding-3-small)
var emb struct {
Data []struct{ Embedding []float64 `json:"embedding"` } `json:"data"`
}
kiosapi.Post("/embeddings", map[string]any{"model": "openai/text-embedding-3-small", "input": "Isi artikel di sini"}, &emb)
vector := emb.Data[0].Embedding
var upserted any
kiosapi.Post("/vector-indexes/artikel-saya/vectors/upsert", map[string]any{
"vectors": []map[string]any{{"id": "artikel-1", "values": vector, "metadata": map[string]string{"kategori": "berita"}}},
}, &upserted)
// 3) Cari yang paling relevan
var hasil struct {
Matches []map[string]any `json:"matches"`
}
kiosapi.Post("/vector-indexes/artikel-saya/query", map[string]any{
"vector": vector, "top_k": 5, "include_metadata": true,
}, &hasil)
fmt.Println(hasil.Matches)11. Penanganan error
var resp ChatResponse
err := kiosapi.Post("/chat/completions", map[string]any{
"model": "openai/gpt-4o",
"messages": []map[string]string{{"role": "user", "content": "Halo!"}},
}, &resp)
if err != nil {
// kiosapi.Post sudah membungkus status HTTP >= 400 sebagai error
log.Fatal(err)
}Kode HTTP umum: 401 API key salah, 400 model tak dikenal/parameter salah, 402 saldo kurang atau batas pengeluaran bulanan tercapai, 422 ditolak moderasi, 429 rate limit, 502/504 provider upstream bermasalah/timeout. Kode error media lengkap ada di /docs.
12. Rate limit & kuota
| Free | Berbayar | |
|---|---|---|
| Rate limit | 5 req/menit | 60 req/menit |
| Kuota harian | 50 request/hari (reset 07:00 WIB), email terverifikasi | tanpa batas harian |
| Output maks | ~2048 token | sesuai maxOutput model |
| Input maks | ~24k karakter | sesuai kapasitas model |
13. Contoh lengkap — mini pipeline RAG
Menggabungkan embeddings + vector DB + chat dalam satu alur.
func embed(text string) []float64 {
var resp struct {
Data []struct{ Embedding []float64 `json:"embedding"` } `json:"data"`
}
kiosapi.Post("/embeddings", map[string]any{"model": "openai/text-embedding-3-small", "input": text}, &resp)
return resp.Data[0].Embedding
}
func main() {
// Index dokumen (sekali saja)
var created any
kiosapi.Post("/vector-indexes", map[string]any{"name": "basis-pengetahuan"}, &created)
dokumen := []string{"Kiosapi adalah AI API gateway Indonesia.", "Kiosapi mendukung 100+ model AI."}
var vectors []map[string]any
for i, d := range dokumen {
vectors = append(vectors, map[string]any{
"id": fmt.Sprintf("doc-%d", i), "values": embed(d), "metadata": map[string]string{"text": d},
})
}
var upserted any
kiosapi.Post("/vector-indexes/basis-pengetahuan/vectors/upsert", map[string]any{"vectors": vectors}, &upserted)
// Query + jawab pakai konteks yang relevan
pertanyaan := "Apa itu Kiosapi?"
var hasil struct {
Matches []struct {
Metadata struct{ Text string `json:"text"` } `json:"metadata"`
} `json:"matches"`
}
kiosapi.Post("/vector-indexes/basis-pengetahuan/query", map[string]any{
"vector": embed(pertanyaan), "top_k": 2, "include_metadata": true,
}, &hasil)
var konteks strings.Builder
for _, m := range hasil.Matches {
konteks.WriteString(m.Metadata.Text + "\n")
}
var jawaban ChatResponse
kiosapi.Post("/chat/completions", map[string]any{
"model": "anthropic/claude-sonnet-4-6",
"messages": []map[string]string{
{"role": "system", "content": "Jawab berdasar konteks ini:\n" + konteks.String()},
{"role": "user", "content": pertanyaan},
},
}, &jawaban)
fmt.Println(jawaban.Choices[0].Message.Content)
}Referensi lain: spesifikasi OpenAPI 3.1 lengkap, harga & daftar model di /pricing, dan dokumentasi utama di /docs.