Dokumentasi

Dokumentasi

API Penggunaan

Lihat jumlah token dan biaya untuk API key yang membuat permintaan. Satu key, datanya sendiri — tidak ada akses lintas key. Gunakan ini untuk membuat dasbor penagihan sendiri, rekonsiliasi bulanan, atau peringatan biaya.

GET /v1/usage mengembalikan angka agregat token dan biaya untuk API key di header Authorization. Kelompokkan per hari atau jam; secara opsional, pisahkan berdasarkan model. Tidak ada akses untuk membaca data key lain — untuk itu, masuk ke dasbor.

Autentikasi

Kirimkan kunci API Kunavo Anda sebagai token Bearer, yaitu kunci yang sama yang data penggunaannya ingin Anda baca. Endpoint ini hanya mengembalikan data penggunaan yang ditagihkan ke kunci tersebut — tidak ada parameter untuk meminta data milik kunci lain.

Perlakukan satu proyek sebagai satu key. Pola yang disarankan adalah membuat API key terpisah untuk setiap aplikasi yang Anda tagih — dasbor Kunavo dan endpoint ini kemudian secara otomatis merangkum data "per proyek". Kelola key di /app/keys.

Permintaan

ParameterWajibBawaanCatatan
start_dateyes—YYYY-MM-DD, UTC. Inklusif. Harus ≤ hari ini (UTC).
end_dateyes—YYYY-MM-DD, UTC. Inklusif. Tanggal setelah hari ini dibaca sebagai hari ini; respons menggemakan end_date yang digunakan.
bucketnodaySalah satu dari day, hour.
group_byno(none)Dipisahkan koma. Yang didukung: model. Abaikan untuk mendapatkan satu baris per bucket.
Batas rentang waktu. bucket=day menerima hingga 90 hari; bucket=hour menerima hingga 7 hari. Jika melebihi batas tersebut, permintaan mengembalikan 400 invalid_request_error — pecah menjadi beberapa panggilan.
Zona waktu. Bucket diselaraskan dengan hari/jam UTC. Jika Anda memerlukan hari berdasarkan jam lokal di zona waktu lain, kueri data harian lalu sesuaikan batasnya di sisi klien. Parameter tz= mendatang akan menangani ini di sisi server tanpa mengubah kolom yang sudah ada.

Contoh

Total harian untuk 30 hari terakhir, digabungkan untuk semua model:

usage_30d.sh
# Daily totals for the last 30 days
curl https://api.kunavo.com/v1/usage \
  -G \
  -H "Authorization: Bearer $KUNAVO_API_KEY" \
  --data-urlencode "start_date=2026-04-28" \
  --data-urlencode "end_date=2026-05-27"

Satu baris untuk setiap pasangan (hari, model):

usage_by_model.sh
# Per-model daily breakdown — one row per (day, model)
curl https://api.kunavo.com/v1/usage \
  -G \
  -H "Authorization: Bearer $KUNAVO_API_KEY" \
  --data-urlencode "start_date=2026-05-01" \
  --data-urlencode "end_date=2026-05-27" \
  --data-urlencode "group_by=model"

Python — jumlahkan rentang waktu menjadi satu angka:

usage_query.py
import os, datetime as dt, requests

today = dt.date.today()
start = today - dt.timedelta(days=29)  # last 30 days inclusive

r = requests.get(
    "https://api.kunavo.com/v1/usage",
    headers={"Authorization": f"Bearer {os.environ['KUNAVO_API_KEY']}"},
    params={
        "start_date": start.isoformat(),
        "end_date":   today.isoformat(),
        "group_by":   "model",
    },
    timeout=30,
)
r.raise_for_status()
body = r.json()

# body["data"] is one row per (day, model). Sum cost across the window:
total_usd = sum(float(row["cost_usd"]) for row in body["data"])
print(f"Spent ${total_usd:.4f} on key {body['api_key']['name']}")

Respons

200 OK — application/json dengan daftar baris. Bucket tanpa aktivitas bernilai nol tidak disertakan; klien dapat mengisi bagian yang kosong jika memerlukan rangkaian data tanpa jeda.

response.json
{
  "object": "list",
  "api_key": {
    "id": "k_abc123",
    "name": "production",
    "prefix": "sk-kn-aZ8x"
  },
  "start_date": "2026-05-01",
  "end_date":   "2026-05-27",
  "bucket": "day",
  "data": [
    {
      "bucket_start": "2026-05-01T00:00:00.000Z",
      "model": "claude-sonnet-4-6",
      "requests": 1284,
      "errors": 7,
      "input_tokens": 1532890,
      "output_tokens": 245100,
      "cached_input_tokens": 980000,
      "cache_write_tokens": 12000,
      "web_search_requests": 0,
      "cost_usd": "3.452100"
    },
    {
      "bucket_start": "2026-05-01T00:00:00.000Z",
      "model": "gpt-5",
      "requests": 88,
      "errors": 0,
      "input_tokens": 42000,
      "output_tokens": 12300,
      "cached_input_tokens": 0,
      "cache_write_tokens": 0,
      "web_search_requests": 0,
      "cost_usd": "0.210400"
    }
  ]
}
KolomTipeMakna
bucket_startstring (ISO 8601, UTC)Awal hari atau jam yang diagregasikan pada baris ini.
modelstringSlug model. Hanya tersedia jika group_by=model.
requestsnumberPanggilan yang berhasil dalam bucket ini.
errorsnumberPanggilan yang berakhir dengan galat/waktu habis/dibatalkan. Tidak ditagihkan.
input_tokensnumberTotal token input (mencakup subset cache + penulisan cache).
output_tokensnumberTotal token output.
cached_input_tokensnumberSubset dari input_tokens yang dilayani dari cache prompt di penyedia hulu.
cache_write_tokensnumberToken yang ditulis ke cache bucket ini (hanya Anthropic; 0 di tempat lain).
web_search_requestsnumberPencarian web di sisi server, masing-masing ditagihkan di luar biaya token: pencarian Claude melalui /v1/messages, dan alat OpenAI web_search pada model GPT melalui /v1/responses (0 untuk yang lain).
cost_usdstringJumlah yang ditagihkan kepada Anda dalam USD untuk bucket ini, dengan presisi 6 desimal. Berupa string untuk menjaga presisi; gunakan parseFloat() dengan aman.
Mengapa biaya berupa string. Biaya per panggilan yang sangat kecil (beberapa seperseratus ribu sen untuk lalu lintas yang banyak menggunakan cache) akan dibulatkan menjadi nol dalam IEEE-754 jika diubah menjadi string secara naif. Dengan mempertahankan format data sebagai string dengan 6 desimal, kode akuntansi Anda dapat memprosesnya dengan presisi arbitrer (misalnya, Decimal()).

Cache di sisi klien

Endpoint menetapkan Cache-Control: private, max-age=60. Sebagian besar klien (browser, permintaan dengan sesi, serta cache HTTP apa pun di sisi Anda) akan mengembalikan respons yang sama untuk parameter yang sama dalam rentang 60 detik — cocok untuk dasbor yang melakukan polling. Untuk memaksa pembacaan baru, ubah string kueri (misalnya, tambahkan &_=ts sebagai pengacak cache) atau kirim Cache-Control: no-cache di sisi Anda.

Kesalahan

StatusKodeKapan
401authentication_errorKunci Bearer tidak ada, formatnya salah, atau telah dicabut.
400invalid_request_errorFormat start_date/end_date tidak valid, tanggal akhir sebelum tanggal mulai, start_date setelah hari ini, rentang melebihi batas bucket, atau nilai group_by tidak didukung.
500internal_errorBasis data tidak dapat dijangkau atau terjadi kegagalan yang tidak terduga.

Catatan penting

  • Key yang dihapus. Saat Anda mencabut API key, baris penggunaan historisnya kehilangan api_key_id (diatur ke null), sehingga total historis tersebut tidak dapat lagi diambil melalui endpoint ini. Data tetap tersedia dalam tampilan agregat akun di dasbor. Jika Anda memerlukan jejak audit per key yang tidak dapat diubah, ambil dan simpan data sebelum mencabutnya.
  • Hampir waktu nyata. Baris data muncul dalam beberapa detik setelah panggilan terkait selesai. Tidak ada batas kesegaran yang dijamin — jika Anda memerlukan kecocokan yang tepat dengan faktur, kueri data kemarin sekali sehari, bukan setiap menit.
  • Biaya adalah biaya yang ditagihkan. Nilai cost_usd di sini sama dengan jumlah yang didebit dari saldo dompet Anda. Nilai ini sudah memperhitungkan batas minimum max(catalog, upstream × markup) yang dijelaskan di Penagihan. Respons ini tidak memuat angka "grosir" terpisah.

Langkah selanjutnya