Todos los artículos
9 min de lectura

tonapi.io y rate limits: cómo eliminar el error 429

tonapi.io devuelve HTTP 429 al superar el rate limit. Cómo eliminar el error 429, por qué «228» es un meme y cómo conseguir tu propia clave vía MCP de TONNode.

tonapierror 429rate limitTON APIMCPTONNode

tonapi 429: ocho y media de la noche, producción en llamas y los logs, un muro de rojo

Tu bot en TON acaba de aparecer en una lista de proyectos destacados, los usuarios entran en avalancha y, de pronto, la app empieza a devolver balances vacíos. Abres los logs y ahí están, cientos de líneas:

HTTP 429 Too Many Requests

¿Te suena? Consultas tonapi.io para balances e historial de transacciones; todo funcionaba con diez usuarios, pero con mil el acceso anónimo tocó techo. Es un clásico: mientras el tráfico es pequeño, la API pública parece gratuita e infinita. En cuanto la carga crece, se convierte en un cuello de botella y empiezas a recibir tonapi 429 a montones. No es un bug de tu código ni «se cayó el nodo»: es el rate limit de la API pública, y se cura de forma predecible.

Veamos por qué aparece el 429, por qué el famoso «228» no tiene nada que ver, qué fixes ayudan de verdad y cómo pasar del pool anónimo compartido a un límite personal para leer TON — a través del servidor MCP de TONNode.

Por qué tonapi.io devuelve 429 Too Many Requests

tonapi.io es una API HTTP pública para los datos de TON. Como cualquier servicio público, tiene su tonapi rate limit: una restricción de frecuencia de peticiones para que un solo cliente no se coma toda la capacidad.

Cuando accedes sin clave, compartes el pool anónimo con todos los demás anónimos del planeta. En la práctica, el límite ronda ~1 petición por segundo. Para un script suelto es tolerable. Pero en cuanto tienes un worker paralelo que por cada wallet pide el balance, luego el balance del jetton y luego el historial, te sales del límite por segundo al instante. Duele especialmente en el arranque en frío, cuando hay que indexar muchas direcciones de golpe.

El servidor responde así:

HTTP/1.1 429 Too Many Requests
Retry-After: 1

Es importante entenderlo: 429 es un código HTTP estándar de la especificación, no un código propietario de tonapi. Lo devuelve cualquiera al superar la frecuencia: GitHub, Stripe, Cloudflare, cualquier servicio con rate limit. El mismo mecanismo existe en toncenter y en la mayoría de proveedores RPC. Y por eso mismo se arregla con las técnicas estándar que vienen a continuación.

El «error 228» no es un código de la API, sino un meme de la comunidad (el código real es 429)

Si buscaste el problema en foros rusoparlantes, seguro que te topaste con el «error 228». Pongamos los puntos sobre las íes, porque esto confunde de verdad a los recién llegados.

«228» no es un código de error oficial ni de tonapi ni de toncenter. Es un número meme que lleva años circulando en la comunidad de TON y que reaparece en chats y bromas. Al superar el límite no vas a recibir ningún estado HTTP 228: ese código de estado simplemente no existe en HTTP.

El código real que verás en los logs y en las cabeceras de la respuesta es precisamente 429. Cuando alguien escribe en un chat «me llegó un 228 de tonapi», en realidad recibió un 429 (o un timeout común y corriente) y usa «228» como figura retórica. Busca 429 en tus logs, no «228»: así encontrarás la causa real. Se comprueba en una línea:

curl -s -o /dev/null -w "%{http_code}\n" https://tonapi.io/v2/blockchain/masterchain-head
# bajo carga y sin clave verás: 429

Fixes rápidos: reintentos, backoff, caché y tu propia clave

Como el 429 es un problema estándar, tiene un arsenal de curas igual de estándar. Vamos de lo más simple a lo que de verdad importa.

1. Backoff exponencial respetando Retry-After

No martillees el endpoint en un bucle cerrado tras el primer rechazo: solo empeorarás la situación. Con un 429 el servidor suele devolver la cabecera Retry-After: cuántos segundos esperar. Respétala; y si no viene, aumenta la pausa exponencialmente.

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;

    // Respetamos Retry-After; si no está, aumentamos la pausa exponencialmente
    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… tope de 30s

    await new Promise(r => setTimeout(r, waitMs));
  }
  throw new Error('el 429 sigue ahí: se agotaron los reintentos');
}

2. Limita el número de peticiones simultáneas

A menudo el 429 no llega por el volumen total, sino porque disparas 50 peticiones «en abanico» con Promise.all. Pon un semáforo con un límite de concurrencia (1–4) y los picos se suavizarán sin romper el límite por segundo.

3. Cachea los datos que no cambian

Los metadatos de un jetton (nombre, símbolo, decimals), el resultado de convertir una dirección, las transacciones antiguas: son datos inmutables. Mételos en una caché local con un TTL razonable y no se los vuelvas a pedir a la API. Solo con cachear los decimals de los jettons ya eliminas una parte notable de las llamadas.

4. El fix principal: tu propia clave en lugar del acceso anónimo

Los tres primeros puntos son analgésicos. La cura de verdad es dejar el pool anónimo y pasarte a tu propia clave. Una clave personal sube tu límite en órdenes de magnitud frente al ~1 req/s del anónimo; dejas de competir con todo internet y el 429 simplemente desaparece del funcionamiento normal. El caso concreto de otro proveedor está desglosado en el fix del 429 de toncenter: la lógica es idéntica.

Qué hacer cuando los límites de tonapi aun así no bastan

Supongamos que hiciste todo bien: backoff, caché, cola. Pero la aplicación crece y, aun con clave, chocas o bien con el plan o bien con el propio modelo de «capa HTTP encima de un nodo». Aquí normalmente se plantean dos caminos.

El camino «mis propios liteservers». Surge la tentación de «simplemente tomar un liteserver público del config global de TON e ir directo por ADNL». Aclaración honesta: no es una bala de plata. Los liteservers públicos del config global también son compartidos y limitados: bajo carga responden con regularidad not ready o se caen por timeout de ADNL, y no guardan historial profundo de transacciones. Un problema aparte, muy habitual, es justamente el not ready; el desglose está en cómo arreglar «liteserver not ready». Es decir, «irse al config público» sin más no resuelve el problema de los límites, y a veces lo agrava.

El camino «cambiar de proveedor/interfaz». El problema no está en tonapi.io en concreto, sino en que dependes de un recurso compartido. Hay un repaso de alternativas de API HTTP en alternativas a tonapi en 2026. Y si estás construyendo un agente de IA, tiene sentido no envolver REST a mano, sino conectar TON como un conjunto de herramientas vía MCP: entonces leer la blockchain se convierte en la llamada a una herramienta, y no en una petición HTTP cruda que hay que reintentar.

TONNode MCP: límite personal en lugar del pool anónimo compartido

TONNode (web: tonnode.io) es un servidor MCP hosted para TON. MCP (Model Context Protocol) es el estándar con el que los agentes de IA (Claude, Cursor, ChatGPT/Codex y cualquier cliente MCP) invocan herramientas externas. En lugar de enseñarle a tu agente a llamar a mano a https://tonapi.io/v2/... y a gestionar los 429, le das un conjunto de herramientas con nombre para leer TON.

El punto clave: el paquete @tonnode/mcp funciona sobre el protocolo nativo ADNL de TON, sin capas HTTP intermedias. Es open source (MIT) y está en npm y en GitHub (tonnode/mcp). No es otro wrapper REST encima de tonapi, sino una conversación directa con la red.

Las consultas típicas por las que ibas a tonapi quedan cubiertas una a una por las herramientas de lectura:

  • get_balance — el balance de GRAM de una dirección.
  • get_jetton_balance — el balance de USDT o de cualquier jetton; la jetton wallet se calcula on-chain, no necesitas derivar su dirección tú mismo. Cómo esto sustituye a una cadena de varias peticiones lo explicamos en la nota balance de USDT en TON con una sola llamada.
  • get_account_state — estado de la cuenta, flags, última transacción.
  • get_transactions — historial de transacciones.
  • run_get_method — cualquier get-method read-only de un contrato.
  • get_masterchain_info — la cabeza de la masterchain (el bloque actual).
  • get_jetton_info — metadatos del jetton: nombre, símbolo, emisión y decimals (USDT tiene 6; la mayoría de los jettons, 9; sin ellos convertirás mal las unidades raw).
  • parse_address — conversión y validación offline de direcciones EQ/UQ/raw, sin tocar la red en absoluto.

Una nota rápida sobre el naming: GRAM es el Toncoin renombrado en junio de 2026. La red se sigue llamando TON; solo cambió el nombre de la moneda. Los balances de get_balance van en GRAM.

Un ejemplo de prompt que el agente ejecutará con herramientas, y no con HTTP manual:

Comprueba el balance de GRAM y el balance de USDT en la dirección
UQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XF_wm
y muéstrame las últimas 5 transacciones de esa wallet.

El agente llamará por sí solo a get_balance, luego a get_jetton_balance (calculando la jetton wallet on-chain) y después a get_transactions — sin una sola petición HTTP escrita por ti, sin Retry-After y sin backoff manual en tu código.

Cómo conectarlo en un minuto: local para desarrollo o clave hosted para producción

Hay dos vías, y las dos son honestas. Lo importante es no confundirlas: el npx local sin clave es cómodo para desarrollar, pero funciona sobre el config público de TON; el límite personal que de verdad elimina los 429 bajo carga lo da precisamente la clave hosted.

Opción A — local, gratis y sin clave (para desarrollo)

El set completo de herramientas de lectura se levanta con un solo comando vía npx: no hacen falta ni registro ni tarjeta. Añade a la config de tu cliente MCP:

{
  "mcpServers": {
    "ton": {
      "command": "npx",
      "args": ["-y", "@tonnode/mcp"]
    }
  }
}

Un arranque ideal para desarrollo local y pruebas: set de lectura completo, cero configuración y open source por debajo. Pero, aclaración honesta: este modo va por el config público de TON, así que sufre las mismas limitaciones compartidas que cualquier liteserver público (not ready, timeouts de ADNL bajo carga). Para tráfico de producción no sustituye a un límite personal: para eso está la opción B.

Opción B — endpoint hosted con tu propia clave (para producción)

Cuando necesitas capacidad garantizada y un límite personal bajo la carga de producción, conectas el endpoint hosted con tu clave Bearer:

{
  "mcpServers": {
    "ton": {
      "type": "http",
      "url": "https://mcp.tonnode.io/mcp",
      "headers": { "Authorization": "Bearer tn_live_…" }
    }
  }
}

Planes

En todos los planes están disponibles las 16 herramientas de TONNode; solo pagas por la capacidad:

  • Hobby — gratis para siempre, 60 peticiones/min. La clave se emite nada más iniciar sesión, sin tarjeta.
  • Pro — $29/mes, 300 peticiones/min.
  • Scale — $199/mes, 1200 peticiones/min.

La diferencia con el tonapi anónimo salta a la vista: allí compartes ~1 req/s con todo internet; aquí tienes tu propio techo — ya con la clave hosted gratuita de Hobby son 60 peticiones por minuto solo para ti, sin malabares con el backoff y sin 429 aleatorios. Y no es el mismo camino gratuito que el npx local sin clave: la clave hosted de Hobby tiene su propio límite, no el pool público compartido.

Conclusión

El 429 de tonapi.io no es un bug ni el mítico «228», sino una señal honesta: estás en el pool anónimo compartido y tocaste su techo. Los reintentos con backoff, respetar Retry-After, limitar las peticiones simultáneas y la caché alivian los síntomas agudos. Pero el problema solo desaparece de verdad cuando tienes capacidad propia — y si construyes sobre agentes de IA, además es más cómodo hacerlo vía MCP, donde la lectura de TON va por el protocolo nativo y no por una capa HTTP intermedia.

Consigue la clave hosted gratuita de Hobby (60 req/min, sin tarjeta) y deja de coleccionar 429tonnode.io/dashboard?plan=hobby

¿Necesitas margen para la carga de producción? Compara los planes Pro y Scale: tonnode.io/pricing.

Dale a tu agente acceso a TON

16 herramientas MCP: lectura, swaps no custodiales, cross-chain y billeteras. Plan gratuito — 60 req/min, sin tarjeta.