L'erreur est apparue au milieu d'une session /mutant.
Pas un crash. Pas un timeout réseau. Un message sobre, presque poli : API Error: Rate limit reached. Et tous mes agents se sont tus.
J'ai d'abord pensé à un hook mal configuré. Mon infrastructure Claude Code comporte 55 hooks — il y en a forcément un qui intercepte les requêtes et crée des effets de bord. J'ai audité les PreToolUse, les PostToolUse, le agent-router. Rien. Le code était propre.
J'ai ensuite pensé à Ollama. Mon serveur tourne sur le port 11700 avec bge-m3 pour les embeddings RAG. Peut-être une saturation ? Non. Ollama répondait parfaitement. Gemini CLI aussi.
Alors j'ai lu le message plus attentivement. Rate limit reached. Pas Ollama. Pas Gemini. Anthropic.
Avant de continuer de lire la suite de l'article, je vous invite à vous inscrire à ma newsletter, pour connaître en avant première les futurs sujets traités chaque semaine.
Ce que « rate limit » veut vraiment dire
Il existe deux métriques de consommation distinctes avec l'API Anthropic, et beaucoup de développeurs les confondent.
La première, c'est le quota hebdomadaire — le nombre total de tokens que vous pouvez consommer sur sept jours. Quand votre tableau de bord affiche « 49 % utilisé », c'est ce quota-là. Il s'exprime en millions de tokens et se réinitialise chaque semaine.
La seconde, c'est le rate limit RPM/TPM — la limite de débit par minute. RPM signifie Requests Per Minute (requêtes par minute). TPM signifie Tokens Per Minute (tokens par minute). Ces deux limites opèrent en temps réel et sont indépendantes de votre quota hebdomadaire.
Autrement dit : vous pouvez avoir 800 000 tokens hebdomadaires disponibles et quand même recevoir un 429 si vous en consommez 300 000 en une seule minute.
C'est exactement ce qui m'est arrivé.
| Métrique | Portée | Réinitialisation | Erreur |
|---|---|---|---|
| Quota hebdomadaire | Total tokens 7 jours | Hebdomadaire | Quota épuisé |
| RPM (Requests Per Minute) | Requêtes / minute | Chaque minute | 429 Rate limit |
| TPM (Tokens Per Minute) | Tokens / minute | Chaque minute | 429 Rate limit |
Le code HTTP 429 — Too Many Requests — est le signal standard pour un dépassement de rate limit. Il ne dit pas que vous avez épuisé votre solde. Il dit que vous avez envoyé trop de requêtes trop vite.
Voici les limites officielles par tier Anthropic (au 15 mars 2026) :
| Tier | Dépôt requis | RPM (Sonnet/Opus) | ITPM (Sonnet/Opus) | ITPM (Haiku) |
|---|---|---|---|---|
| Tier 1 | 5 $ | 50 | 30 K | 50 K |
| Tier 2 | 40 $ | 1 000 | 450 K | 450 K |
| Tier 3 | 200 $ | 2 000 | 800 K | 1 M |
| Tier 4 | 400 $ | 4 000 | 2 M | 4 M |
Un détail important souvent ignoré : l'ITPM (Input Tokens Per Minute) ne compte que les tokens non mis en cache. Les cache_read_input_tokens sont exclus du calcul pour les modèles Claude 4.x. C'est une opportunité directe : plus vous utilisez le prompt caching, plus votre débit effectif augmente.
Pourquoi /mutant déclenche le 429
Quand j'ai analysé le volume de tokens par requête sur mes sessions /mutant, les chiffres m'ont mis face à l'évidence.
| Composant | Taille | Impact |
|---|---|---|
| mutant.md (command prompt) | ~53 Ko (~15-20 K tokens) | Chargé dans chaque requête API |
| CLAUDE.md + 7 fichiers rules | ~44 Ko (~12-15 K tokens) | System prompt permanent |
| Hook injections (agent-router, semantic-context...) | ~5-10 K tokens/tour | Injecté via console.error à chaque tool call |
| Total par requête API | ~35-45 K input tokens | × N tours/minute = TPM explosé |
/mutant est un orchestrateur intensif. Son mode de fonctionnement normal implique :
- Décomposition du problème en 3 à 5 questions atomiques
- Phone-call pattern : plusieurs sous-agents lancés en parallèle
- Traçabilité SQL : chaque étape génère des UPDATE dans agent_tasks
- Tool calls rapides : Read, Bash, Write, notebook JSON, Gemini CLI — 5 à 10 tool calls par minute
Chaque tool call déclenche une requête API. Avec ~40 K tokens d'input par requête et 5 à 10 tool calls par minute, on atteint facilement 200 000 à 400 000 TPM. Les rate limits Anthropic, selon le tier de compte, se situent entre 50 000 et 400 000 TPM. Dans mon cas, j'ai touché le plafond.
Ce n'est pas un bug. C'est de la physique.
Le diagnostic que j'aurais dû faire en premier
Avant d'auditer les hooks, avant de vérifier Ollama, la bonne question à se poser face à un 429 est simple :
Combien de tokens est-ce que j'injecte dans chaque requête ?
Il existe trois sources de contexte qui s'accumulent silencieusement dans une session Claude Code :
1. Les fichiers de configuration chargés automatiquement. CLAUDE.md, les fichiers rules, les commandes slash — tout ça compose le system prompt. Sur mon infrastructure, ça représente 44 Ko constants, soit environ 12 à 15 K tokens injectés dans chaque requête de la session.
2. Les prompts de commande. Quand vous lancez /mutant, le fichier mutant.md (~53 Ko) est chargé comme instruction. Ce fichier contient le workflow complet, les patterns de délégation, les templates de scoring. C'est nécessaire pour la qualité — mais c'est 15 à 20 K tokens supplémentaires.
3. Les hooks qui s'expriment dans le contexte. Sur mes 55 hooks, plusieurs injectent du contexte via console.error à chaque tool call : agent-router injecte les résultats de routing, semantic-context injecte les résultats RAG, context-budget-tracker envoie son état. Chaque injection ajoute 500 à 2 000 tokens par tour.
Additionnez tout ça, multipliez par le rythme d'un /mutant actif, et vous obtenez un TPM qui explose en quelques minutes.
Les solutions que j'ai appliquées
La solution n'est pas de ralentir les agents. La solution est de réduire ce qu'on leur donne à lire.
Activer le prompt caching en priorité
Le prompt caching est la solution à coût nul. Les tokens servis depuis le cache Anthropic (cache_read_input_tokens) ne comptent pas dans le calcul ITPM pour Claude 4.x. En pratique : un system prompt de 15 K tokens servi en cache coûte 0 TPM au lieu de 15 000 TPM. L'effet est immédiat.
Claude Code applique le cache_control automatiquement sur le system prompt. Pour les scripts SDK personnalisés, ajoutez cache_control: { type: "ephemeral" } sur les blocs stables.
Déplacer le contexte stable vers le RAG
Le principe : ce qui ne change pas d'une session à l'autre n'a pas besoin d'être dans le system prompt. Il peut vivre dans pgvector et être rappelé à la demande.
J'ai migré vers le RAG :
- Les instructions détaillées des agents (restent dans le fichier .md seulement le rôle et les commandes de chargement RAG)
- Les références de projet volumineuses
- Les historiques de sessions passées
Résultat : les fichiers .md des agents sont passés de 5-10 Ko à 1-2 Ko. Le system prompt ne contient que des pointeurs vers le RAG, pas le contenu lui-même.
Compresser les prompts de commande
mutant.md fait 53 Ko parce qu'il documente exhaustivement le workflow. J'ai créé une version slim qui contient uniquement :
- Le rôle (10 lignes)
- Les instructions de chargement RAG au démarrage
- Le workflow en pseudo-code (pas en prose)
La version slim : ~8 Ko au lieu de 53 Ko. Environ 2 000 tokens au lieu de 15 000.
Espacer les tool calls dans les workflows intensifs
Les agents peuvent s'emballer et enchaîner les tool calls sans pause. J'ai ajouté une règle dans les agents les plus actifs : regrouper les opérations de traçabilité SQL en une seule requête au lieu de trois UPDATE séparés.
Trois UPDATE séparés = trois requêtes API = trois fois 40 K tokens d'input. Un seul UPDATE groupé = une requête API.
Monitorer le TPM en temps réel
J'ai ajouté turn-tokens.ts dans mes hooks de session — un hook Stop qui affiche à chaque tour le nombre de tokens consommés avec décomposition cache_read / cache_write / input / output. Voir [cache: ↑Nrd ↓Nwr] à chaque tour permet d'identifier immédiatement les requêtes qui explosent le contexte.
Ce que ça révèle sur l'architecture multi-agent
L'erreur 429 est un symptôme, pas une cause. La cause, c'est une architecture où le contexte croît sans contrôle.
Dans un système à agent unique, le contexte grossit linéairement avec la conversation. Dans un système multi-agent, chaque sous-agent reçoit un contexte complet — system prompt, instructions, hooks. Le contexte ne grossit pas linéairement : il se multiplie par le nombre d'agents actifs simultanément.
C'est le paradoxe de la parallélisation : plus vous parallélisez, plus vous consommez de TPM, plus vite vous touchez le rate limit.
La réponse n'est pas de sérialiser les agents (vous perdez le bénéfice de la parallélisation). La réponse est de réduire le contexte de chaque agent individuel. Un agent qui fait son travail avec 5 K tokens d'input peut être parallélisé 8 fois sans problème. Un agent qui en consomme 40 K se parallélise 8 fois et vous envoie un 429 en 30 secondes.
Le RAG n'est pas seulement une solution de mémoire. C'est une solution de débit.
Qui suis je ?
Je suis Mathieu GRENIER, CTO d'Easystrat une startup de Montpellier, en France. Je manage une équipe d'une dizaine d'ingénieurs (Graphistes, IA, frontend, backend, devOps, AWS) en remote depuis le Japon.
J'ai aussi mon activité de freelance, où je conseille des entrepreneurs dans leurs projets d'application.
Avec mon expérience personnelle de plus de 15 ans en ESN, j'ai pu travailler pour un large panel d'entreprises de différentes tailles. Ma compréhension des problèmes métiers est une de mes grandes forces et permet à mes clients de pouvoir se projeter plus facilement.
L'essentiel de mon travail consiste à canaliser l'énergie des entrepreneurs sur l'essence même de leur projet.
La technologie, les méthodes, le management sont le cœur de mes compétences.
Vous pouvez me faire confiance sur ces points là.
Si vous voulez me parler d'un de vos projets, n'hésitez pas à m'envoyer un email avec vos disponibilités à : contact@mathieugrenier.fr
Tous les articles de ce blog sont écrits par moi, même si je peux m'aider de l'IA pour illustrer mes propos. Mais jamais je ne fournis d'articles 100 % IA.
Mon système multi-agent s'est bloqué — ce n'était pas un bug, c'était du débit