SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor

Comment limiter le coût d’un agent IA sur un VPS

Un agent sans surveillance facture chaque boucle. Découvrez les plafonds, budgets de tâches, prompt caching, batch et champs d’usage pour suivre vos dépenses.

Comment éviter qu’un agent IA toujours actif fasse exploser la facture

Le contrôle des coûts d’un agent IA sur un VPS (serveur privé virtuel) repose sur des plafonds définis avant son démarrage, car personne ne surveille le compteur pendant son exécution. Limitez chaque réponse avec max_tokens, bornez le nombre d’itérations de la boucle dans votre propre code, mettez en cache la partie du prompt qui ne change jamais et journalisez les statistiques d’utilisation de chaque réponse pour identifier les tâches qui consomment le plus. La location du serveur est facturée à un prix mensuel fixe. L’API du modèle est facturée au nombre de tokens, et une boucle sans supervision est très efficace pour consommer des tokens discrètement.

Cette section suppose que vous disposez déjà d’un agent qui appelle l’API Messages depuis une machine que vous contrôlez. Créer un agent IA avec Claude sur un VPS explique la mise en place elle-même.

Pourquoi le coût d’un agent sans supervision évolue différemment

Une session interactive implique une personne. Lorsque le modèle prend une mauvaise direction ou lit un journal de 40,000 lignes, la personne qui le surveille l’arrête. Un agent sans supervision ne dispose d’aucun tel frein : il s’exécute jusqu’à la fin de la boucle, puis un timer le relance.

La fréquence est le multiplicateur souvent oublié. Une tâche planifiée toutes les cinq minutes s’exécute 288 fois par jour et environ 8,640 fois par mois. Quel que soit le coût d’une exécution, c’est cette valeur qu’il faut multiplier. De nombreux agents « toujours actifs » n’ont pas besoin de fonctionner en permanence. Ils doivent répondre dans un délai donné, exprimé en minutes. Il s’agit d’une planification.

Un agent consomme également des ressources qu’une fenêtre de chat n’utilise pas.

  • Les définitions des outils sont transmises avec chaque requête. Le prompt système d’utilisation des outils consomme 290 tokens avec Claude Opus 4.8 et tool_choice de auto ou none, et 410 avec any ou tool. L’outil bash en ajoute 325. Chaque serveur MCP que vous attachez ajoute ses schémas à ce volume. MCP signifie model context protocol.
  • Les résultats des outils sont des tokens d’entrée. Une commande qui affiche 8,000 lignes place ces 8,000 lignes dans la requête suivante, puis dans chaque requête suivante de ce tour.
  • Les pages récupérées sont des tokens d’entrée. Une page web moyenne de 10 kB représente environ 2,500 tokens, et un PDF de recherche de 500 kB environ 125,000. max_content_tokens tronque uniquement le texte, car il « s’applique au contenu textuel, et non au contenu binaire tel que les PDF ». Limitez plutôt un PDF avec max_uses et allowed_domains.
  • La recherche web est facturée par recherche, à raison de $10 pour 1,000 recherches, quel que soit le nombre de résultats renvoyés. Une recherche qui échoue n’est pas facturée.

Aucun de ces éléments n’est coûteux une seule fois. Ils le sont tous 8,640 fois.

Les plafonds stricts et les plafonds indicatifs répondent à des problèmes différents

max_tokens est appliqué. Il s’agit d’une limite stricte sur la sortie totale d’une requête, en incluant le raisonnement et le texte de la réponse. Claude ne la dépasse jamais, et le modèle ne voit pas cette valeur. Lorsqu’elle est atteinte, stop_reason: "max_tokens" est renvoyé et la réponse est tronquée. Pour les agents, le point important est le suivant : chaque requête d’une boucle d’utilisation d’outils possède son propre max_tokens. Cette limite s’applique donc à une réponse, et non à la tâche entière. Dix appels d’outils à 4,000 tokens représentent un plafond de 40,000 tokens pour le tour.

Le budget de tâche est indicatif. task_budget est inclus dans output_config et indique au modèle combien de tokens il peut utiliser pour l’ensemble de la boucle agentique, en comptant le raisonnement, les appels d’outils, les résultats des outils et la sortie.

resp = client.beta.messages.create(
    model="claude-opus-4-8",
    max_tokens=4096,
    betas=["task-budgets-2026-03-13"],
    output_config={"task_budget": {"type": "tokens", "total": 64000}},
    messages=messages,
)

« Les budgets de tâche sont une indication souple, pas une limite stricte. » Claude peut dépasser ce budget au milieu d’une action, tandis que la limite appliquée à la sortie reste max_tokens. « Le compte à rebours n’est visible que par le modèle », et les réponses ne contiennent aucun champ indiquant le budget restant. La valeur minimale acceptée pour task_budget.total est de 20,000 tokens. Une valeur inférieure renvoie une erreur 400. Un budget trop faible pour le travail demandé entraîne un comportement proche d’un refus : le modèle réduit la portée de la tâche ou s’arrête prématurément.

Un détail entraîne une facturation supplémentaire au lieu de la réduire. Si votre client décrémente task_budget.remaining à chaque requête de suivi, la valeur modifiée invalide tout préfixe mis en cache qui la contient. Définissez cette valeur une seule fois, dans la première requête.

Les budgets de tâche sont en bêta pour Claude Fable 5.1, Claude Fable 5, Claude Opus 5.5, Claude Opus 5, Claude Opus 4.8, Claude Opus 4.7, Claude Sonnet 5.5 et Claude Haiku 5.5. Claude Sonnet 5 et Claude Haiku 4.5 sont indiqués comme Not supported. Les budgets de tâche ne s’appliquent pas à Claude Code. Une session Claude Code détachée dans tmux dépend donc plutôt de la bonne gestion de la session.

Le troisième plafond se trouve dans la Claude Console : attribuez à l’agent son propre workspace, puis définissez une limite mensuelle de dépenses et des limites de débit par minute pour ce workspace. « Vous ne pouvez pas définir de limites sur le Default Workspace », et « les limites appliquées à l’organisation s’appliquent toujours, même si la somme des limites des workspaces est supérieure ». Ajoutez des notifications de dépenses afin d’être alerté lorsqu’un seuil est atteint, avant d’arriver au plafond.

Choix du modèle par tâche et effet réel du niveau d’effort

Le choix du modèle se fait pour chaque tâche. En octobre 2026, par million de tokens, pour l’entrée puis la sortie : Claude Fable 5.1 et Fable 5 coûtent respectivement $10 et $50, Claude Opus 5.5 $4 et $20, Claude Opus 5, Opus 4.8 et Opus 4.7 $5 et $25, Claude Sonnet 5.5 et Sonnet 5 $2 et $10, Claude Haiku 5.5 $0.10 et $0.50 pour les prompts de 100,000 tokens maximum ($0.50 et $2.50 au-delà, les tokens mis en cache étant inclus dans le décompte), et Claude Haiku 4.5 $1 et $5. Anthropic a d’abord proposé Sonnet 5 à ce tarif de lancement, puis en a fait le tarif standard et a annulé l’augmentation prévue à $3 et $15. Une étape qui se contente de classer des lignes de log n’a pas besoin d’Opus.

Ne comptez pas non plus sur l’utilisation gratuite pour absorber une charge élevée. En dehors de la petite réserve de crédits accordée par l’API Claude lors de l’inscription, la seule allocation récurrente est le crédit API mensuel inclus avec les offres Claude Max et Team : $100 avec Max 5x, $200 avec Max 20x et jusqu’à $500 mutualisés avec Team, renouvelés à chaque cycle de facturation. Vous pouvez le réclamer après 7 jours avec une offre éligible, sans fournir de moyen de paiement. Il couvre l’API Claude, Managed Agents, l’Agent SDK et le playground, mais pas Claude Code. Il expire à la fin de chaque cycle et n’est pas reporté. Son utilisation est comptabilisée dans votre plafond de dépenses. Une fois épuisé, les requêtes s’arrêtent, sauf si l’organisation dispose également de crédits achetés.

Le niveau d’effort est le deuxième levier. output_config.effort accepte low, medium, high, xhigh et max, mais tous les modèles ne prennent pas en charge tous les niveaux. La valeur par défaut est high sur la plupart des modèles et medium sur Claude Opus 5.5 et Claude Haiku 5.5. Définir explicitement la valeur par défaut d’un modèle revient à ne pas la renseigner. Un niveau d’effort inférieur ne réduit pas seulement la longueur du raisonnement : la documentation indique qu’il amène Claude à effectuer moins d’appels d’outils et à regrouper plusieurs opérations en moins d’appels. Pour un agent, l’économie est plus importante, car un appel d’outil évité correspond à une requête complète qui n’a jamais lieu.

Le piège est que le niveau d’effort perturbe le cache. Modifier la valeur au niveau supérieur entre deux requêtes invalide la mise en cache du prompt. Dans l’exemple documenté avec Claude Opus 5.5, la requête 2 indiquait cache_read_input_tokens: 3546. La requête 3 a changé le niveau d’effort de medium, la valeur par défaut de ce modèle, à low, et indiquait cache_creation_input_tokens sur 3546 et cache_read_input_tokens sur 0. Faites donc varier le niveau d’effort supérieur entre les charges de travail, jamais au sein d’une même conversation mise en cache. Deux méthodes permettent de modifier la profondeur sans invalider le cache. Avec Claude Fable 5.1, Opus 5.5, Opus 5, Sonnet 5.5 et Haiku 5.5, une modification du niveau d’effort par message (beta, en-tête mid-conversation-output-config-2026-07-01) définit un nouveau niveau et conserve le préfixe mis en cache. Avec n’importe quel modèle, vous pouvez aussi l’indiquer dans le prompt : une ligne comme « Answer directly without deliberating. » dans le dernier message utilisateur conserve les points de reprise précédents. Avec Claude Haiku 5.5, toutefois, cette phrase ne réduit pas systématiquement le raisonnement.

Les tokens de raisonnement sont facturés au tarif de sortie et comptabilisés dans max_tokens. C’est pourquoi une réponse tronquée signifie souvent que le raisonnement a consommé le budget. Consultez usage.output_tokens_details.thinking_tokens pour connaître le nombre de tokens. Ce qui remplit réellement la facture de tokens Claude détaille ce compteur.

Mettre en cache le préfixe stable et éviter de le modifier par erreur

Une écriture dans le cache coûte 1.25 fois le prix de base des entrées avec le cache de cinq minutes, et 2 fois ce prix avec le cache d’une heure. Sur la plupart des modèles, une lecture du cache coûte 0.1 fois ce prix. Le cache devient donc rentable après une lecture avec la durée de 5 minutes (écriture à 1.25x), ou après deux lectures avec la durée d’une heure (écriture à 2x). Les lectures sont encore moins chères sur les modèles les plus récents : 0.05 fois sur Claude Opus 5.5 et Claude Sonnet 5.5, et 0.025 fois sur Claude Fable 5.1.

Une ligne explique pourquoi ce mécanisme convient à un agent actif en permanence : « Le cache est actualisé sans coût supplémentaire chaque fois que le contenu mis en cache est utilisé. » Un job exécuté toutes les deux minutes avec le cache de cinq minutes conserve son préfixe en cache toute la journée pour une seule écriture.

Voici trois façons de perdre le cache sans s’en apercevoir.

Un préfixe qui change. « Les préfixes du cache sont créés dans l’ordre suivant : tools, system, puis messages. » Toute modification d’un octet plus tôt dans cet ordre invalide tout ce qui suit. La modification des définitions des outils invalide également tout le cache. L’erreur classique consiste à placer un timestamp ou un identifiant d’exécution dans le prompt système : chaque requête contient alors un préfixe différent, écrit une nouvelle entrée à 1.25x et ne réutilise aucune entrée existante. Le signe révélateur est usage.cache_read_input_tokens à 0 sur des appels qui semblent identiques. Déplacez le texte variable dans le message utilisateur le plus récent.

Un préfixe trop court. Chaque modèle possède une longueur minimale pouvant être mise en cache. En dessous de cette longueur, la requête est traitée sans mise en cache et « aucune erreur n’est renvoyée ». Les valeurs sont de 512 tokens sur Claude Fable 5.1, Fable 5, Opus 5.5, Opus 5, Sonnet 5.5 et Haiku 5.5, de 1,024 sur Claude Opus 4.8 et Claude Sonnet 5, et de 4,096 sur Claude Haiku 4.5. Déplacer un job de Sonnet 5 vers Haiku 4.5 peut donc désactiver la mise en cache sans aucun message.

Une conversation qui dépasse la fenêtre de recherche. « La fenêtre de recherche contient 20 blocs. » Le système vérifie au maximum 20 positions pour chaque point de rupture, puis s’arrête. Dans l’exemple documenté, un tour contenant 35 blocs et un point de rupture sur le bloc 35 vérifie les blocs 35 à 16. L’entrée du tour précédent, située au bloc 15, est en dehors de la fenêtre et ne produit donc aucun hit. Un agent qui ajoute plusieurs blocs d’utilisation d’outil et de résultat d’outil à chaque tour dépasse 20 blocs en deux ou trois tours. Vous disposez de quatre points de rupture par requête : utilisez-en un pour les messages récents.

Envoyez à l’API Batches tout ce qui peut attendre

« Toutes les utilisations sont facturées à 50 % des tarifs standard de l’API », pour les entrées comme pour les sorties. Le traitement par lots est asynchrone, « la plupart des lots étant terminés en moins d’une heure ». Les résultats sont disponibles lorsque toutes les requêtes sont terminées ou après 24 heures, selon la première éventualité. Cette durée est indicative et non garantie.

Interrogez processing_status jusqu’à ce qu’il indique ended. Les requêtes qui renvoient errored, canceled ou expired ne sont pas facturées. Attention si vous utilisez un plafond de dépenses : « les lots peuvent dépasser légèrement la limite de dépenses configurée pour votre espace de travail ».

Les remises sont cumulables. Comme un lot peut durer plus de cinq minutes, la documentation recommande le cache d’une heure pour les lots qui partagent un contexte. Séparez donc les traitements : tout ce qu’une personne ou un webhook attend reste sur le chemin synchrone, tandis qu’un récapitulatif nocturne ou la classification des journaux de la veille passe par un lot à moitié prix.

Journaliser les champs d’utilisation de chaque réponse dans votre propre stockage

Vous ne pouvez pas attribuer une dépense que vous n’avez jamais enregistrée. Chaque réponse indique ce qu’elle vous a coûté.

u = resp.usage
row = {
    "job": job_name,
    "model": resp.model,
    "uncached_input": u.input_tokens,
    "cache_write": u.cache_creation_input_tokens,
    "cache_read": u.cache_read_input_tokens,
    "output": u.output_tokens,
    "stop_reason": resp.stop_reason,
}

Ajoutez une ligne par appel API dans un fichier JSON Lines, avec le nom de votre tâche. Une semaine plus tard, vous pourrez déterminer quelle tâche dépense réellement et laquelle donne seulement l’impression d’être active. Surveillez cache_read : une colonne remplie de zéros est le bug de coût le plus fréquent dans un agent auto-hébergé.

Un champ est facile à mal interpréter. input_tokens compte uniquement les tokens situés après le dernier point de cache. La taille réelle du prompt est donc total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens. Un agent qui indique input_tokens: 400 pour un prompt volumineux n’est pas peu coûteux : le reste provient du cache.

Comptez avant l’envoi. Le comptage des tokens est gratuit et ses limites de débit sont distinctes de celles de la création de messages. Utilisez donc count_tokens pour refuser une pièce jointe trop volumineuse au lieu de payer pour le découvrir. Le résultat est une estimation. Mesurez donc à nouveau pour chaque modèle et ne réutilisez jamais un comptage produit par le tokenizer d’un autre fournisseur. Les modèles Claude 4.7 et ultérieurs, notamment Claude Fable 5 et Claude Sonnet 5, utilisent un tokenizer plus récent qui « produit environ 30 % de tokens supplémentaires pour un même texte ». Claude Sonnet 4.6 et les modèles antérieurs, notamment Claude Haiku 4.5, utilisent le tokenizer précédent.

Pour obtenir les données de référence, l’Admin API fournit l’utilisation à https://api.anthropic.com/v1/organizations/usage_report/messages et le coût à https://api.anthropic.com/v1/organizations/cost_report. Les deux requièrent une clé d’administration (sk-ant-admin01-...) transmise comme x-api-key: $ANTHROPIC_ADMIN_KEY avec anthropic-version: 2023-06-01. Le rapport d’utilisation accepte bucket_width=1d, group_by[]=model et api_key_ids[]=. Le rapport des coûts est limité à une vue quotidienne et regroupe les données selon workspace_id ou description. Une limitation subsiste : « L’Admin API n’est pas disponible pour les comptes individuels. »

Le paramètre api_key_ids[] fournit une méthode simple d’attribution : attribuez à chaque tâche sa propre clé API, filtrez le rapport d’utilisation avec api_key_ids[], puis répartissez les données par clé avec group_by[]=api_key_id. Le filtre est au pluriel, tandis que la dimension de regroupement est au singulier. Conservez les clés dans l’environnement plutôt que dans le code, comme le fait une première application Claude API sur un VPS pour les gérer.

Limiter la boucle, car rien d’autre ne le fera

Un nombre maximal d’itérations est indispensable ici. La boucle vous appartient, donc le compteur aussi :

for step in range(MAX_STEPS):          # MAX_STEPS = 12, never "while True"
    resp = client.messages.create(...)
    if resp.stop_reason != "tool_use":
        break
else:
    log.warning("job %s hit MAX_STEPS=%d, giving up", job_name, MAX_STEPS)

Aucune des deux limites ci-dessus ne suffit : max_tokens limite une réponse, et le modèle est seulement informé d’un budget de tâches. Un produit hébergé arrêterait le traitement à ce stade, comme la limite de Claude sur les appels d’outils au cours d’un même tour met fin à une session qui en a effectué trop, mais une boucle que vous avez écrite vous-même n’a aucun mécanisme de protection de ce type tant que vous n’en ajoutez pas un.

Ajoutez un second garde-fou à l’extérieur du processus. Exécutez la tâche avec un timer systemd plutôt qu’avec un processus permanent, et définissez une limite de durée dans son service. Une tâche lancée par un timer est généralement Type=oneshot, et RuntimeMaxSec= « n’a aucun effet sur les services de type Type=oneshot », définissez donc TimeoutStartSec=600 à cet endroit. Utilisez RuntimeMaxSec=600 uniquement avec une unité Type=simple. Dans les deux cas, une exécution bloquée est arrêtée après dix minutes au lieu de continuer jusqu’à ce que vous le remarquiez. Exécuter un programme comme service et timer systemd présente les fichiers d’unité eux-mêmes. Consultez le résultat d’une exécution avec journalctl -u triage-agent.service --since "1 hour ago".

Limitez également les tentatives, car un gestionnaire qui réessaie indéfiniment facture chaque tentative. Une erreur 429 ou 500 justifie quelques tentatives avec un backoff. Une erreur 400 n’en justifie aucune, car la même requête échouera de la même façon.

Le contrôle des coûts d’un agent IA commence par la lecture de vos propres chiffres

Personne ne peut vous dire combien coûte un agent actif en permanence, car le coût correspond au nombre de tokens par exécution multiplié par le nombre d’exécutions par jour. Ces deux valeurs dépendent de votre configuration. Exécutez-le une fois, consultez la ligne d’utilisation que vous avez enregistrée, puis multipliez-la par votre planning. Deux jours plus tard, comparez le rapport des coûts avec ce calcul. En cas d’écart, la cause est presque toujours un cache défaillant ou une boucle qui a duré plus longtemps que prévu.

Cette méthode suppose l’utilisation d’une clé API, car l’agent est votre propre programme qui appelle l’API Messages. Pour votre travail interactif, quel forfait Claude correspond à votre façon de travailler couvre la partie abonnement. Tous les prix et toutes les limites indiqués ici ont été vérifiés dans la documentation d’Anthropic en octobre 2026. Consultez donc à nouveau la page des tarifs avant d’établir un budget.

FAQ

Combien coûte l’exécution permanente d’un agent IA sur un VPS ?

Il y a deux factures, mais une seule est prévisible. Le serveur a un prix mensuel fixe. L’API du modèle est facturée au token : le coût correspond donc à ce qu’une exécution consomme, multiplié par sa fréquence d’exécution. Anthropic ne publie aucun chiffre pour un agent permanent auto-hébergé. Considérez donc tout montant annoncé comme une estimation. Journalisez usage lors d’une exécution réelle, puis multipliez cette valeur par votre planification.

Quelle est la différence entre max_tokens et le budget d’une tâche ?

max_tokens est appliqué par le système et n’est pas visible par le modèle. Il limite la sortie d’une requête, réflexion comprise, et l’atteindre produit stop_reason: "max_tokens". Le budget d’une tâche fonctionne à l’inverse : le modèle connaît sa valeur et adapte la boucle agentique en conséquence. Cependant, « Les budgets de tâche sont une indication souple, pas une limite stricte », et la limite effectivement appliquée reste max_tokens.

Pourquoi cache_read_input_tokens vaut-il toujours 0 pour mon agent ?

Parce que le préfixe change entre les appels ou qu’il est trop court pour être mis en cache. La cause habituelle est un horodatage ou un identifiant d’exécution interpolé dans le prompt système : le cache est indexé sur le préfixe, donc toute modification d’un octet invalide tout ce qui le suit. La modification des définitions des outils ou de la valeur effort au niveau supérieur produit le même effet. Sinon, le problème vient de la taille : les prompts plus courts ne sont pas mis en cache et aucune erreur n’est renvoyée.

Comment empêcher un agent IA de boucler indéfiniment ?

Comptez les itérations dans le code de votre boucle et arrêtez-la après un maximum fixe, car max_tokens limite une réponse alors qu’un agent en produit plusieurs. Ajoutez une limite de temps réelle en dehors du processus : démarrez la tâche avec un timer systemd et définissez TimeoutStartSec= sur une unité oneshot, ou RuntimeMaxSec= sur une unité simple, afin qu’une exécution bloquée soit arrêtée comme prévu. Limitez également les retries, car chaque tentative est facturée.

Puis-je définir une limite de dépenses pour une seule clé d’API Claude ?

La limite de dépenses documentée s’applique à un workspace et non à une clé. Attribuez donc à l’agent son propre workspace et définissez-y une limite mensuelle. « Vous ne pouvez pas définir de limites sur le Default Workspace ». Activez des notifications de dépenses afin d’être alerté lorsqu’un seuil est atteint. Pour suivre les dépenses par tâche, attribuez une clé propre à chaque job, puis regroupez le rapport d’utilisation avec group_by[]=api_key_id.