Documentation

Documentation

API d’utilisation

Consultez le nombre de jetons et les coûts associés à la clé API à l’origine de la requête. Une clé, ses propres données : aucun accès aux autres clés. Utilisez cette API pour alimenter vos tableaux de bord de facturation, vos rapprochements mensuels ou vos alertes de coût.

GET /v1/usage renvoie les données agrégées sur les jetons et les coûts de la clé API figurant dans l’en-tête Authorization. Regroupez les données par jour ou par heure ; vous pouvez aussi les ventiler par modèle. Vous ne pouvez pas lire les données d’une autre clé — pour cela, connectez-vous au tableau de bord.

Authentification

Transmettez votre clé API Kunavo en tant que jeton Bearer : il s’agit de la même clé dont vous voulez consulter l’utilisation. Le point de terminaison renvoie uniquement l’utilisation facturée à cette clé précise — aucun paramètre ne permet de demander les données d’une autre clé.

Utilisez une clé par projet. Il est recommandé de créer une clé API distincte pour chaque application que vous facturez. Les tableaux de bord Kunavo et ce point de terminaison regroupent alors naturellement les données « par projet ». Gérez vos clés sur /app/keys.

Requête

ParamètreObligatoirePar défautRemarques
start_dateyes—YYYY-MM-DD, UTC. Inclusif. Doit être inférieur ou égal à aujourd’hui (UTC).
end_dateyes—YYYY-MM-DD, UTC. Inclusif. Une date postérieure à aujourd’hui est interprétée comme aujourd’hui ; la réponse reprend la end_date utilisée.
bucketnodayL’une des valeurs day, hour.
group_byno(none)Valeurs séparées par des virgules. Valeur(s) prise(s) en charge : model. Omettez ce paramètre pour obtenir une ligne par intervalle.
Limites de période. bucket=day accepte jusqu’à 90 jours ; bucket=hour accepte jusqu’à 7 jours. Au-delà, la requête renvoie 400 invalid_request_error — répartissez la période sur plusieurs appels.
Fuseau horaire. Les intervalles sont alignés sur les jours/heures UTC. Pour obtenir les jours civils d’un autre fuseau horaire, interrogez l’API par jour et ajustez les intervalles aux limites côté client. Un futur paramètre tz= permettra de le faire côté serveur, sans modifier les champs existants.

Exemples

Totaux quotidiens sur les 30 derniers jours, tous modèles confondus :

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"

Une ligne par couple (jour, modèle) :

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 — additionner les valeurs de la période pour obtenir un seul nombre :

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']}")

Réponse

200 OK — application/json avec une liste de lignes. Les intervalles sans activité sont omis ; le client comble les lacunes s’il a besoin d’une série continue.

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"
    }
  ]
}
ChampTypeSignification
bucket_startchaîne (ISO 8601, UTC)Début du jour ou de l’heure agrégé par cette ligne.
modelstringIdentifiant du modèle. Présent uniquement lorsque group_by=model.
requestsnumberAppels réussis pendant cet intervalle.
errorsnumberAppels terminés par une erreur, un délai d’attente dépassé ou une annulation. Non facturés.
input_tokensnumberNombre total de jetons d’entrée (comprend les sous-ensembles mis en cache et écrits dans le cache).
output_tokensnumberNombre total de jetons de sortie.
cached_input_tokensnumberSous-ensemble de input_tokens servi depuis le cache d’invite du fournisseur en amont.
cache_write_tokensnumberTokens écrits dans le cache pour cette période (Anthropic uniquement ; 0 ailleurs).
web_search_requestsnumberRecherches Web côté serveur, facturées en supplément des jetons : celles de Claude via /v1/messages, et l’outil web_search d’OpenAI sur un modèle GPT via /v1/responses (0 dans les autres cas).
cost_usdstringMontant facturé en USD pour cet intervalle, avec une précision de 6 décimales. Il s’agit d’une chaîne pour préserver la précision ; analysez-la sans risque avec parseFloat().
Pourquoi le coût est-il une chaîne ? Les coûts minuscules par appel (quelques cent-millièmes de cent pour les requêtes qui utilisent beaucoup le cache) sont arrondis à zéro par IEEE-754 si vous les convertissez naïvement en chaîne. Le format transmis sous forme de chaîne à 6 décimales permet à votre code comptable d’effectuer une analyse en précision arbitraire (par exemple Decimal()).

Mise en cache côté client

Le point de terminaison définit Cache-Control: private, max-age=60. La plupart des clients (navigateurs, requêtes avec session, tout cache HTTP de votre côté) renverront la même réponse pour les mêmes paramètres pendant une fenêtre de 60 secondes — ce qui convient aux tableaux de bord qui interrogent l’API à intervalles réguliers. Pour forcer une nouvelle lecture, modifiez la chaîne de requête (par exemple en ajoutant un paramètre &_=ts de contournement du cache) ou transmettez Cache-Control: no-cache de votre côté.

Erreurs

StatutCodeQuand
401authentication_errorClé Bearer manquante, mal formée ou révoquée.
400invalid_request_errorFormat de start_date/end_date incorrect, date de fin antérieure à la date de début, date start_date postérieure à aujourd’hui, période dépassant la limite de l’intervalle ou valeur group_by non prise en charge.
500internal_errorBase de données inaccessible ou erreur inattendue.

Réserves

  • Clés supprimées. Lorsque vous révoquez une clé API, les lignes d’utilisation antérieures perdent leur api_key_id (défini sur null) ; leurs totaux historiques ne peuvent alors plus être récupérés via ce point de terminaison. Les données restent disponibles dans la vue agrégée du compte du tableau de bord. Si vous avez besoin d’une piste d’audit immuable par clé, récupérez et stockez les données avant de la révoquer.
  • Quasi temps réel. Les lignes sont disponibles quelques secondes après la fin de l’appel concerné. Aucun délai maximal de mise à jour n’est garanti ; si vous devez faire correspondre les données exactement à votre facture, interrogez les données de la veille une fois par jour plutôt qu’à la minute.
  • Le coût correspond au montant facturé. La valeur cost_usd correspond ici au montant débité de votre portefeuille. Elle tient déjà compte du plancher max(catalog, upstream × markup) décrit dans Facturation. Cette réponse ne contient aucun montant « de gros » distinct.

Étapes suivantes