Suivre et prévoir les coûts
Ce qui est facturé
Les modèles Gemini sont facturés au jeton (token), un morceau de mot : en ordre de grandeur, un mot français fait un à deux jetons. On paie séparément :
- les jetons d'entrée : instruction système + prompt (ici, les avis) ;
- les jetons de sortie : la réponse, plus les jetons de réflexion (thinking) des modèles qui réfléchissent avant de répondre.
Les prix (en dollars par million de jetons) dépendent du modèle et du mode : la page Tarifs indique notamment que le mode lot coûte la moitié du tarif standard.
flowchart LR R["Réponse de Gemini"] --> U["usage_metadata"] U --> P["prompt_token_count"] U --> C["candidates_token_count"] U --> T["thoughts_token_count"] P --> J["resultats.jsonl : tokens_in"] C --> K["tokens_out = réponse + réflexion"] T --> K J --> RP["rapport.py : coût = in x P_in + out x P_out"] K --> RP
Enregistrer les jetons avec chaque lot
u = r.usage_metadata
return {"resultats": valider(avis, r.text), "tokens_in": u.prompt_token_count or 0,
"tokens_out": (u.candidates_token_count or 0) + (u.thoughts_token_count or 0)}Les jetons sont écrits dans la même ligne que les résultats du lot : le point de reprise est aussi le registre des coûts. Un lot refait après une coupure n'est compté qu'une fois dans le fichier... mais il a été payé deux fois chez le fournisseur ; c'est le coût d'une coupure, borné par la concurrence.
Calculer le coût
prix_in = float(os.environ.get("PRICE_IN_PER_M", "0.10"))
prix_out = float(os.environ.get("PRICE_OUT_PER_M", "0.40"))
cout = t_in / 1e6 * prix_in + t_out / 1e6 * prix_outLes prix viennent de l'environnement (fichier .env.example) : les valeurs par défaut sont des exemples. Recopiez celles de la page Tarifs pour votre modèle, le jour où vous faites le calcul : les prix changent.
Prévoir : extrapoler depuis un échantillon
On ne lance pas 100 000 documents « pour voir ». On mesure sur un petit lot représentatif, puis on extrapole :
| Mesure sur 200 avis (2026-10-15) | Valeur |
|---|---|
| jetons d'entrée | 6 884, soit environ 34 par avis |
| jetons de sortie (réflexion comprise) | 9 468, soit environ 47 par avis |
| requêtes | 20 (lots de 10) |
Pour 100 000 avis semblables : environ 3,4 millions de jetons d'entrée et 4,7 millions de sortie. Avec les prix d'exemple du .env.example (0,10 par million) : $ en synchrone, environ la moitié en mode lot. Refaites ce calcul avec les vrais prix de votre modèle.
| Levier | Effet sur le coût |
|---|---|
| mode lot (Batch API, Vertex AI batch) | environ moitié prix, si le délai est acceptable |
| modèle plus léger (variante lite) | souvent bien moins cher : à valider sur la qualité (leçon 4.2) |
| regrouper par lots | instruction système payée une fois par lot |
limiter la réflexion (types.ThinkingConfig) | moins de jetons de sortie sur une tâche simple de classement |
| sortie courte et structurée | pas de phrases inutiles dans la réponse |
Sur un essai de 200 documents, vous mesurez 9 468 jetons de sortie. Combien en prévoir pour 50 000 documents semblables ?
On ramène à un coût par document (environ 47 jetons), puis on multiplie. C'est une estimation : elle n'est valable que si l'échantillon ressemble au reste (longueur, langue).
Quels éléments font partie des jetons de sortie facturés ? (plusieurs réponses)
La réponse et la réflexion sont de la sortie. L'instruction système et le prompt sont de l'entrée, facturée à un autre prix.
Exercice : un garde-fou de budget
Ajoutez à traiter() un paramètre budget_jetons : dès que la somme des jetons (entrée + sortie) des lots terminés le dépasse, plus aucun nouveau lot ne démarre.
Voir la solution
async def traiter(client, avis, sortie, taille_lot=10, concurrence=4, rpm=60,
traiter_lot=classer_lot, budget_jetons=None) -> dict:
...
stats = {"lots_ok": 0, "lots_echec": 0, "avis_ok": 0, "jetons": 0}
async def un_lot(lot):
async with sem:
if budget_jetons and stats["jetons"] >= budget_jetons:
return # budget atteint : on ne démarre plus rien
await debit.attendre()
res = await avec_reessais(lambda: traiter_lot(client, lot))
ecrire_lot(sortie, {"lot": [a["id"] for a in lot], "modele": MODEL, **res})
stats["jetons"] += res["tokens_in"] + res["tokens_out"]
...Le test se vérifie à l'entrée du sémaphore : les lots déjà en vol se terminent (au plus concurrence lots au-delà du budget). Les lots non démarrés n'ont pas de ligne dans le point de reprise : ils seront traités à la prochaine exécution, avec un nouveau budget.
Résultat obtenu
Sortie réelle de rapport.py sur les 200 avis classés le 2026-10-15 (gemini-3.5-flash-lite, prix d'exemple du .env.example) :
Jetons : 6884 en entrée, 9468 en sortie ; coût estimé au tarif standard 0.0045 $ (au tarif Batch, moitié prix : 0.0022 $)Le même jour, le lot de 20 avis sur Vertex AI (gemini-2.5-flash) a consommé 822 jetons d'entrée et 3 066 de sortie, soit environ 153 jetons de sortie par avis contre environ 47 ici : la différence vient surtout de la réflexion du modèle.
Je sais faire
- Lire
usage_metadataet compter aussi les jetons de réflexion - Enregistrer les jetons avec chaque lot dans le point de reprise
- Calculer un coût à partir de prix configurables, sans les coder en dur
- Extrapoler un coût depuis un échantillon et poser un garde-fou de budget