Quota d'API : débiter chaque tentative, pas chaque appel

Un plafond d'API contourné par les relances automatiques : le compteur ne voyait qu'une tentative sur trois. Comment l'étalonner.

1. Contexte & Objectif de Terrain : un compteur est un instrument de mesure

Dans un atelier, un instrument de mesure qui dérive est un problème sérieux : il fait passer des pièces mauvaises, ou en refuse des bonnes. C’est pour cela que la norme ISO 9001 demande de maîtriser les équipements de surveillance et de mesure.

Mon bot interroge une API publique, celle de la place de marché eBay. Cette API impose une limite quotidienne : 5 000 appels. Je me suis fixé un plafond maison de 4 500, avec un compteur qui s’incrémente à chaque appel. Ce compteur est mon instrument de mesure.

L’objectif de la session : réduire la consommation, et surtout rendre le compteur fidèle à la réalité.

Un mot sur les relances. Une requête réseau échoue de temps en temps sans gravité : une coupure brève, un serveur occupé. Une bibliothèque de nouvelles tentatives réessaie alors automatiquement, après une courte pause. Dans mon cas, la pause grandit à chaque essai (de 2 à 10 secondes) et la bibliothèque abandonne après trois tentatives. Le mécanisme est utile. Il devient un piège dès qu’un compteur ne le voit pas.

2. Anatomie de l’Échec & Fausses Pistes : un compteur qui comptait une tentative sur trois

Le bug. La fonction de recherche est relancée automatiquement, jusqu’à trois fois, par une bibliothèque de nouvelles tentatives (tenacity). Mais le compteur n’était débité qu’une fois par appel de la fonction, et non à chaque tentative. Les tentatives réellement envoyées dépassaient donc le chiffre affiché.

Un compteur qui sous-compte n’est pas un compteur approximatif. C’est un compteur qui ne protège plus de la limite qu’il est censé garder.

Le plafond de 4 500 laisse 500 appels de marge sous la limite du fournisseur. Cette marge n’a de valeur que si le compteur est juste. Avec des relances jusqu’à trois fois, il pouvait, dans le pire cas, ne voir que le tiers des envois réels. Ce qui compte pour la limite, ce sont les requêtes réellement envoyées, pas celles que mon programme croit avoir comptées. Une marge de 500 appels peut alors fondre sans que rien ne l’indique.

Un second piège. Le bot relit les mêmes quelque 200 annonces à chaque cycle. Sans registre des annonces déjà vues, l’entonnoir de suivi aurait compté les mêmes annonces des dizaines de fois par jour.

La raison est simple : les annonces les plus récentes sont toujours en tête de liste, donc d’un cycle à l’autre, la quasi-totalité de la liste est identique. Un compteur d’annonces qui additionnerait chaque cycle gonflerait ses chiffres du seul fait du temps qui passe. Il fallait donc compter les annonces distinctes, et les appels à chaque tentative : deux compteurs, deux règles.

Un bonhomme tient un compteur qui affiche 1 pendant que trois pièces partent : trois envois, un seul compté

3. Décryptage Méthodologique par l’Analogie QSE : étalonner, c’est comparer à la réalité

Un instrument se vérifie contre une référence. On ne le règle pas « à l’œil » : on compare ce qu’il affiche à ce qui s’est réellement passé. Deux idées en découlent.

Vérifier avant de partir. Avant chaque envoi, on contrôle que le plafond n’est pas atteint. C’est le contrôle d’entrée, comme le calibre GO / NO-GO avant d’engager la pièce. C’est aussi le réflexe de vérifier qu’il reste du papier avant de lancer une longue impression, plutôt que de s’en apercevoir à mi-chemin.

Compter à chaque tentative. Chaque envoi réel est un événement à enregistrer, y compris la relance. Si trois tentatives partent, trois lignes entrent dans le registre.

Un instrument qui voit une tentative sur trois est hors tolérance. On ne l’ajuste pas : on le replace là où il voit tout.

Avant, une tentative sur trois est comptée ; après, les trois tentatives sont comptées

4. Le Pivot Architectural & L’Implémentation Déterministe : le contrôle à l’intérieur de la boucle

La correction déplace le contrôle et le débit à l’intérieur de la fonction relancée. Chaque tentative est donc vérifiée avant de partir, puis facturée. Le plafond est revérifié entre deux tentatives.

_retry_search = retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=10),
    retry=retry_if_exception_type(httpx.HTTPError),
    reraise=True,
)

# à l'intérieur de la fonction relancée :
if quota is not None:
    enforce_quota_guard(quota)   # plafond vérifié à chaque tentative
    quota.record_api_call(1)     # et débité à chaque tentative

Pourquoi deux contrôles, avant et entre les tentatives ? Avant le premier envoi, on évite de partir quand on est déjà au plafond. Entre deux tentatives, on évite de relancer quand la tentative précédente vient de consommer les derniers appels disponibles. Chaque contrôle couvre une situation que l’autre laisse passer.

Pour réduire la consommation, les requêtes sont aussi groupées avec la syntaxe OU : jusqu’à 6 mots-clés par requête, avec 200 résultats par réponse. J’ai vérifié cette syntaxe par un appel réel, en dehors de la suite de tests.

Le groupement économise des appels, il ne règle pas le comptage : les deux corrections sont indépendantes. Sans la première, la consommation restait élevée ; sans la seconde, on ne saurait pas la mesurer.

5. Données Chiffrées, Benchmarks & Résultats Mesurés : de 36 à 6 appels par cycle

  • Mesure réelle (29 septembre 2026) : 200 annonces d’un groupe de 6 mots-clés remontent à au moins 34 heures. Un cycle de 10 minutes est donc largement couvert.
  • Appels de recherche par cycle : environ 36 avant, environ 6 après.
  • Appels par jour : environ 3 900 avant, environ 650 après, avec environ 500 appels restants pour l’inspection unitaire des annonces.
  • Tests : 494 sur 494 au vert à la clôture de cette tranche.

Ces chiffres sont des ordres de grandeur issus de mesures réelles, pas de calculs théoriques. Ce qui reste non mesuré : la fréquence réelle des relances en production.

36 appels de recherche par cycle avant, 6 après, soit environ 3 900 puis environ 650 appels par jour

6. Déploiement Zéro-Terminal (Le Mode d’Emploi pour Marc) : une feuille d’étalonnage dans Excel

Vous pouvez vérifier n’importe quel compteur de votre activité (appels, heures consommées, lots expédiés) avec une feuille d’étalonnage.

Colonnes : date, nombre réel (relevé du fournisseur, s’il en fournit un), nombre compté par votre outil, écart, tolérance, décision.

  • Écart : =B2-C2
  • Décision : =SI(ABS(D2)>E2;"hors tolérance";"conforme")

Exemple de lecture : si la colonne « écart » affiche +2, +1, +3, +2 sur quatre jours, l’écart est toujours du même signe, donc l’outil compte moins que la réalité. Cherchez l’événement qu’il ne voit pas, par exemple les relances.

Si l’écart n’est jamais nul, ce n’est pas grave. S’il est toujours du même signe, c’est qu’un événement n’est pas compté : cherchez-le. Un compteur qui sous-compte systématiquement est un instrument à étalonner, pas à ignorer.

7. Les 3 Règles d’Or Actionnables & Conclusion : compter ce qui part, pas ce qu’on a demandé

  1. Une relance automatique est un nouvel envoi. Placez le contrôle de plafond et le débit à l’intérieur de la fonction relancée.
  2. Revérifiez le plafond entre deux tentatives, pas seulement avant la première.
  3. Étalonnez vos compteurs contre la réalité avant de leur faire garder une limite.

Un plafond n’a de valeur que si l’instrument qui le garde voit tout. La question à poser à chaque limite que vous gérez est donc simple : qui la compte, et que ne voit-il pas ? Les relances, les doublons, les reprises après erreur sont les endroits habituels où un compteur perd des événements. Le reste, regrouper les requêtes, réduire les cycles, optimise une consommation que l’on mesure enfin correctement.