tonapi.io et rate limits : comment corriger l'erreur 429
tonapi.io renvoie un HTTP 429 au-delà du rate limit. Comment corriger l'erreur 429, pourquoi « 228 » est un mème et comment obtenir votre clé via TONNode MCP.
tonapi 429 : 20h30, la prod brûle et les logs affichent un mur de rouge
Votre bot TON vient d'être repris dans une sélection, les utilisateurs ont afflué, et l'application s'est mise d'un coup à renvoyer des soldes vides. Vous ouvrez les logs — des centaines de lignes :
HTTP 429 Too Many Requests
Ça vous parle ? Vous interrogez tonapi.io pour les soldes et l'historique des transactions, tout fonctionnait très bien avec dix utilisateurs, et à mille l'accès anonyme a atteint son plafond. C'est un grand classique : tant que le trafic reste modeste, une API publique paraît gratuite et infinie. Dès que la charge monte, elle devient un goulot d'étranglement et vous récoltez des tonapi 429 par paquets. Ce n'est ni un bug de votre code ni « le nœud qui est tombé » — c'est le rate limit d'une API publique, et ça se soigne de façon prévisible.
Voyons pourquoi le 429 apparaît, pourquoi la fameuse « 228 » n'a rien à voir là-dedans, quels correctifs aident réellement, et comment quitter le pool anonyme commun pour une limite personnelle en lecture TON — via le serveur MCP TONNode.
Pourquoi tonapi.io renvoie 429 Too Many Requests
tonapi.io est une API HTTP publique donnant accès aux données de TON. Comme tout service public, elle impose un tonapi rate limit — une limite de fréquence des requêtes, pour qu'un seul client n'avale pas toute la capacité.
Quand vous appelez sans clé, vous partagez le pool anonyme commun avec tous les autres anonymes de la planète. En pratique, cela représente une limite de l'ordre de ~1 requête par seconde. Pour un script isolé, c'est tolérable. Mais dès que vous avez un worker parallèle qui, pour chaque wallet, va chercher le solde, puis le solde jetton, puis l'historique, vous explosez instantanément la limite par seconde. C'est particulièrement douloureux au démarrage à froid, quand il faut indexer d'un coup un grand nombre d'adresses.
Le serveur répond ceci :
HTTP/1.1 429 Too Many Requests
Retry-After: 1
Un point important à comprendre : le 429 est un code HTTP standard issu de la spécification, et non un code propriétaire de tonapi. N'importe qui le renvoie en cas de dépassement de fréquence — GitHub, Stripe, Cloudflare, n'importe quel service rate-limité. Le mécanisme est exactement le même chez toncenter et chez la plupart des fournisseurs RPC. Il se soigne donc avec les techniques standard décrites ci-dessous.
L'« erreur 228 » n'est pas un code d'API, c'est un mème de la communauté (le vrai code, c'est 429)
Si vous avez cherché le problème sur les forums russophones, vous êtes forcément tombé sur l'« erreur 228 ». Mettons les points sur les i, parce que ça égare vraiment les débutants.
« 228 » n'est un code d'erreur officiel ni chez tonapi ni chez toncenter. C'est un nombre-mème qui vit depuis longtemps dans la communauté TON et qui ressurgit dans les chats et les blagues. Aucun statut HTTP 228 ne vous arrivera en réponse à un dépassement de limite — ce code de statut n'existe tout simplement pas en HTTP.
Le vrai code, celui que vous verrez dans vos logs et dans les en-têtes de réponse, c'est bien 429. Quand quelqu'un écrit dans un chat « je me suis pris un 228 de tonapi », il s'est en réalité pris un 429 (ou un simple timeout), et « 228 » n'est qu'une figure de style. Cherchez 429 dans vos logs, pas « 228 » — c'est comme ça que vous trouverez la vraie cause. Ça se vérifie en une ligne :
curl -s -o /dev/null -w "%{http_code}\n" https://tonapi.io/v2/blockchain/masterchain-head
# sous charge et sans clé, vous verrez : 429
Correctifs rapides : retries, backoff, cache et votre propre clé
Puisque le 429 est un problème standard, il existe pour lui un jeu de remèdes tout aussi standard. Allons du plus simple au plus important.
1. Backoff exponentiel, dans le respect de Retry-After
Ne martelez pas l'endpoint dans une boucle serrée dès le premier refus — vous ne feriez qu'aggraver la situation. Sur un 429, le serveur renvoie souvent l'en-tête Retry-After : le nombre de secondes à attendre. Respectez-le, et s'il est absent, augmentez la pause de façon exponentielle.
async function fetchWithBackoff(url, opts = {}, maxRetries = 5) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const res = await fetch(url, opts);
if (res.status !== 429) return res;
// On respecte Retry-After, sinon on augmente la pause exponentiellement
const retryAfter = Number(res.headers.get('retry-after'));
const waitMs = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: Math.min(1000 * 2 ** attempt, 30_000); // 1s, 2s, 4s… plafond à 30s
await new Promise(r => setTimeout(r, waitMs));
}
throw new Error('le 429 persiste : nombre de retries dépassé');
}
2. Limitez le nombre de requêtes simultanées
Souvent, le 429 ne vient pas du volume global, mais du fait que vous lancez 50 requêtes « en éventail » via Promise.all. Mettez en place un sémaphore avec une limite de parallélisme (1 à 4) — les pics seront lissés et ne perceront plus la limite par seconde.
3. Mettez en cache les données immuables
Les métadonnées d'un jetton (nom, symbole, decimals), le résultat d'une conversion d'adresse, les vieilles transactions : ce sont des données qui ne changent pas. Rangez-les dans un cache local avec un TTL raisonnable et cessez de les redemander à l'API. À lui seul, le cache des decimals des jettons supprime déjà une part notable des appels.
4. Le correctif principal : votre propre clé plutôt que l'anonyme
Les trois premiers points sont des antidouleurs. Le vrai traitement, c'est de quitter le pool anonyme pour votre propre clé. Une clé personnelle relève votre limite de plusieurs ordres de grandeur par rapport aux ~1 req/s de l'anonyme ; vous cessez de rivaliser avec l'internet entier, et le 429 disparaît simplement du fonctionnement normal. Le cas particulier du fournisseur voisin est traité dans le correctif du 429 chez toncenter — la logique est identique.
Que faire quand les limites de tonapi ne suffisent toujours pas
Admettons que vous ayez tout fait correctement : backoff, cache, file d'attente. Mais l'application grandit et, même avec une clé, vous butez soit sur le forfait, soit sur le modèle lui-même — « une couche HTTP posée par-dessus un nœud ». On envisage alors généralement deux voies.
La voie « mes propres liteservers ». La tentation est grande de « simplement prendre un liteserver public dans la config globale de TON et parler directement en ADNL ». Mise en garde honnête : ce n'est pas une solution miracle. Les liteservers publics de la config globale sont eux aussi partagés et limités — sous charge, ils répondent régulièrement not ready ou lâchent sur un timeout ADNL, et ils ne conservent pas l'historique profond des transactions. Le not ready est d'ailleurs une plaie classique à lui seul : l'analyse est dans comment réparer « liteserver not ready ». Autrement dit, « passer à la config publique » ne résout pas le problème des limites, et l'aggrave parfois.
La voie « changer de fournisseur / d'interface ». Le problème ne vient pas de tonapi.io en particulier, mais du fait que vous puisez dans une ressource partagée. Un tour d'horizon des alternatives HTTP-API est réuni dans les alternatives à tonapi en 2026. Et si vous construisez un agent IA, il est judicieux de ne pas bricoler des wrappers REST à la main, mais de brancher TON comme un ensemble d'outils via MCP — lire la blockchain devient alors un appel d'outil, et non une requête HTTP brute qu'il faut retenter.
TONNode MCP : une limite personnelle au lieu du pool anonyme commun
TONNode (site tonnode.io) est un serveur MCP hébergé pour TON. MCP (Model Context Protocol) est le standard par lequel les agents IA (Claude, Cursor, ChatGPT/Codex et n'importe quel client MCP) appellent des outils externes. Plutôt que d'apprendre à votre agent à taper https://tonapi.io/v2/... et à gérer les 429, vous lui donnez un jeu d'outils nommés pour lire TON.
Le point clé : le paquet @tonnode/mcp fonctionne via le protocole natif ADNL de TON, sans couche HTTP intermédiaire. Il est open source (MIT), publié sur npm et GitHub (tonnode/mcp). Ce n'est pas un énième wrapper REST par-dessus tonapi, mais une conversation directe avec le réseau.
Les requêtes typiques pour lesquelles vous passiez par tonapi trouvent leur équivalent direct dans les outils de lecture :
- get_balance — le solde GRAM d'une adresse.
- get_jetton_balance — le solde d'USDT ou de n'importe quel jetton ; le jetton-wallet est calculé on-chain, vous n'avez pas à en dériver l'adresse vous-même. Comment cela remplace tout un enchaînement de requêtes : voir la note le solde USDT sur TON en un seul appel.
- get_account_state — statut du compte, flags, dernière transaction.
- get_transactions — l'historique des transactions.
- run_get_method — n'importe quelle méthode get en lecture seule d'un contrat.
- get_masterchain_info — la tête du masterchain (bloc courant).
- get_jetton_info — les métadonnées d'un jetton : nom, symbole, offre totale et decimals (6 pour USDT, 9 pour la plupart des jettons ; sans eux, vous convertirez mal les unités raw).
- parse_address — conversion et vérification hors ligne des adresses EQ/UQ/raw, sans le moindre appel réseau.
Petite note sur le nommage : GRAM, c'est le Toncoin renommé en juin 2026. Le réseau s'appelle toujours TON, seul le nom de la pièce a changé. Les soldes de
get_balancesont en GRAM.
Voici un exemple de prompt que l'agent exécutera via des outils, et non via du HTTP écrit à la main :
Vérifiez le solde GRAM et le solde USDT de l'adresse
UQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XF_wm
et montrez les 5 dernières transactions de ce wallet.
L'agent appellera lui-même get_balance, puis get_jetton_balance (en calculant le jetton-wallet on-chain), puis get_transactions — sans une seule requête HTTP écrite par vous, sans Retry-After et sans backoff manuel dans votre code.
Brancher tout ça en une minute : en local pour le dev, ou une clé hosted pour la prod
Il y a deux méthodes, et les deux sont honnêtes. L'important est de ne pas les confondre : le npx local sans clé est pratique pour développer, mais il tourne sur la config publique de TON ; la limite personnelle qui supprime la douleur du 429 sous charge, c'est bien la clé hosted qui la donne.
Option A — en local, gratuit, sans clé (pour le développement)
L'ensemble complet des outils de lecture se lance avec une seule commande via npx — ni inscription ni carte bancaire. Ajoutez ceci dans la config de votre client MCP :
{
"mcpServers": {
"ton": {
"command": "npx",
"args": ["-y", "@tonnode/mcp"]
}
}
}
Le point de départ idéal pour développer et tester en local : jeu de lecture complet, zéro configuration, open source sous le capot. Mais mise en garde honnête : ce mode passe par la config publique de TON, il est donc soumis aux mêmes limites communes que n'importe quel liteserver public (not ready, timeouts ADNL sous charge). Pour du trafic de production, ce n'est pas un substitut à une limite personnelle — pour ça, direction l'option B.
Option B — endpoint hosted avec votre propre clé (pour la prod)
Quand il vous faut un débit garanti et une limite personnelle sous la charge de production, vous branchez l'endpoint hosted avec votre clé Bearer :
{
"mcpServers": {
"ton": {
"type": "http",
"url": "https://mcp.tonnode.io/mcp",
"headers": { "Authorization": "Bearer tn_live_…" }
}
}
}
Tarifs
Sur tous les forfaits, les 16 outils TONNode sont disponibles — vous ne payez que le débit :
- Hobby — gratuit pour toujours, 60 requêtes/min. La clé est délivrée dès la connexion, sans carte bancaire.
- Pro — 29 $/mois, 300 requêtes/min.
- Scale — 199 $/mois, 1200 requêtes/min.
La différence avec le tonapi anonyme saute aux yeux : là-bas, vous partagez ~1 req/s avec l'internet entier ; ici, vous avez un plafond personnel — dès la clé hosted gratuite Hobby, c'est 60 requêtes par minute rien que pour vous, sans gymnastique de backoff et sans 429 aléatoires. Ce n'est déjà plus la même voie gratuite que le npx local sans clé : la clé hosted Hobby a sa propre limite, pas un pool public partagé.
En résumé
Un 429 de tonapi.io n'est ni un bug ni la mythique « 228 », mais un signal honnête : vous puisez dans un pool anonyme commun et vous en avez atteint le plafond. Les retries avec backoff, le respect de Retry-After, la limitation du nombre de requêtes simultanées et le cache calment la douleur aiguë. Mais le problème ne disparaît vraiment que le jour où vous disposez de votre propre capacité — et si vous construisez sur des agents IA, il est de surcroît plus confortable de le faire via MCP, où la lecture de TON passe par le protocole natif et non par une couche HTTP intermédiaire.
Prenez la clé hosted Hobby gratuite (60 req/min, sans carte bancaire) et arrêtez de collectionner les 429 → tonnode.io/dashboard?plan=hobby
Besoin de marge pour la charge de production ? Comparez les forfaits Pro et Scale : tonnode.io/pricing.
Donnez à votre agent l'accès à TON
16 outils MCP : lecture, swaps non-custodial, cross-chain et wallets. Forfait gratuit — 60 req/min, sans carte.