Все статьи
7 мин чтения

tonapi.io и rate-лимиты: как убрать ошибку 429

tonapi.io отдаёт HTTP 429 при превышении rate-лимита. Разбираем, как убрать ошибку 429, почему «228» — это мем, и как получить свой ключ через MCP TONNode.

tonapiошибка 429rate limitTON APIMCPTONNode

tonapi 429: полдевятого вечера, прод горит, а в логах — стена красного

Ваш бот на TON только что попал в подборку, пользователи повалили, и приложение вдруг начало отдавать пустые балансы. Открываете логи — там сотни строк:

HTTP 429 Too Many Requests

Знакомо? Вы ходите в tonapi.io за балансами и историей транзакций, всё работало на десяти пользователях, а на тысяче анонимный доступ упёрся в потолок. Это классика: пока трафик небольшой, публичный API кажется бесплатным и бесконечным. Как только нагрузка растёт — он превращается в бутылочное горлышко, и вы ловите tonapi 429 пачками. Это не баг вашего кода и не «нода легла» — это rate limit публичного API, и лечится он предсказуемо.

Разберём, почему возникает 429, почему пресловутая «228» тут ни при чём, какие фиксы реально помогают, и как уйти с общего анонимного пула на персональный лимит для чтения TON — через MCP-сервер TONNode.

Почему tonapi.io возвращает 429 Too Many Requests

tonapi.io — публичный HTTP-API к данным TON. Как у любого публичного сервиса, у него есть tonapi rate limit — ограничение частоты запросов, чтобы один клиент не съел всю ёмкость.

Когда вы ходите без ключа, вы делите общий анонимный пул со всеми остальными анонимами планеты. Практически это лимит порядка ~1 запроса в секунду. Для одного скрипта это терпимо. Но как только у вас параллельный воркер, который для каждого кошелька дёргает баланс, потом джеттон-баланс, потом историю, — вы мгновенно вылетаете за секундный лимит. Особенно больно при холодном старте, когда нужно проиндексировать сразу много адресов.

Сервер отвечает так:

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

Важно понимать: 429 — это стандартный код HTTP из спецификации, а не проприетарный код tonapi. Его отдаёт кто угодно при превышении частоты — GitHub, Stripe, Cloudflare, любой rate-limited сервис. Тот же самый механизм у toncenter и у большинства RPC-провайдеров. Значит, и лечится он стандартными приёмами, о которых ниже.

«Ошибка 228» — это не код API, а мем комьюнити (реальный код — 429)

Если вы гуглили проблему на русскоязычных форумах, вам наверняка попадалась «ошибка 228». Расставим точки над i, потому что это реально сбивает новичков с толку.

«228» — не официальный код ошибки ни tonapi, ни toncenter. Это мем-число, давно живущее в TON-комьюнити и всплывающее в чатах и шутках. Никакого HTTP-статуса 228 в ответ на превышение лимита вам не прилетит — такого статус-кода в HTTP попросту нет.

Реальный код, который вы увидите в логах и заголовках ответа, — именно 429. Когда кто-то в чате пишет «поймал 228 от тонапи», по факту он поймал 429 (или обычный таймаут), а «228» использует как фигуру речи. Ищите в своих логах 429, а не «228» — так вы найдёте настоящую причину. Проверяется в одну строку:

curl -s -o /dev/null -w "%{http_code}\n" https://tonapi.io/v2/blockchain/masterchain-head
# под нагрузкой без ключа увидите: 429

Быстрые фиксы: ретраи, бэкофф, кэш и свой ключ

Раз 429 — стандартная история, у неё есть стандартный набор лечений. Идём от простого к главному.

1. Экспоненциальный бэкофф с уважением к Retry-After

Не долбите эндпоинт в тугой цикл после первого же отказа — вы только усугубите ситуацию. При 429 сервер часто отдаёт заголовок Retry-After — сколько секунд подождать. Уважайте его, а если его нет — растите паузу экспоненциально.

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;

    // Уважаем Retry-After, иначе растим паузу экспоненциально
    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… потолок 30s

    await new Promise(r => setTimeout(r, waitMs));
  }
  throw new Error('429 не ушёл: превышено число ретраев');
}

2. Ограничьте число одновременных запросов

Часто 429 прилетает не от общего объёма, а от того, что вы запускаете 50 запросов «веером» через Promise.all. Поставьте семафор с лимитом параллелизма (1–4) — и всплески сгладятся, не пробивая секундный лимит.

3. Кэшируйте неизменные данные

Метаданные жетона (имя, символ, decimals), результат конвертации адреса, старые транзакции — это данные, которые не меняются. Положите их в локальный кэш с разумным TTL и не спрашивайте API повторно. Один только кэш decimals жетонов убирает заметную долю обращений.

4. Главный фикс — свой ключ вместо анонима

Первые три пункта — обезболивающее. Настоящее лечение — уйти с анонимного пула на собственный ключ. Персональный ключ поднимает ваш лимит на порядки по сравнению с ~1 req/s на аноним; вы перестаёте конкурировать со всем интернетом, и 429 просто уходит из штатной работы. Частный случай для соседнего провайдера разобран в фиксе 429 у toncenter — логика идентична.

Что делать, когда лимитов tonapi всё равно не хватает

Допустим, вы всё сделали правильно: бэкофф, кэш, очередь. Но приложение растёт, и даже с ключом вы упираетесь либо в тариф, либо в саму модель «HTTP-прослойка поверх ноды». Тут обычно рассматривают два пути.

Путь «свои лайтсерверы». Возникает соблазн «просто взять публичный лайтсервер из глобального конфига TON и ходить напрямую по ADNL». Честная оговорка: это не серебряная пуля. Публичные лайтсерверы из глобального конфига тоже общие и лимитированные — под нагрузкой они регулярно отвечают not ready или отваливаются по ADNL-таймауту, и глубокой истории транзакций не хранят. Отдельная типовая боль — именно not ready, разбор в как чинить «liteserver not ready». То есть просто «уйти на публичный конфиг» проблему лимитов не решает, а иногда усугубляет.

Путь «сменить провайдера/интерфейс». Проблема не в конкретном tonapi.io, а в том, что вы сидите на общем ресурсе. Обзор альтернатив HTTP-API собран в альтернативах tonapi в 2026. А если вы строите ИИ-агента, есть смысл не оборачивать REST руками, а подключить TON как набор инструментов через MCP — тогда чтение блокчейна становится вызовом инструмента, а не сырым HTTP-запросом, который надо ретраить.

TONNode MCP: персональный лимит вместо общего анонимного пула

TONNode (сайт tonnode.io) — это hosted MCP-сервер для TON. MCP (Model Context Protocol) — стандарт, по которому ИИ-агенты (Claude, Cursor, ChatGPT/Codex и любой MCP-клиент) вызывают внешние инструменты. Вместо того чтобы учить агента дёргать https://tonapi.io/v2/... и обрабатывать 429, вы даёте ему набор именованных инструментов для чтения TON.

Ключевой момент: пакет @tonnode/mcp работает по нативному ADNL-протоколу TON, без HTTP-прослоек. Он open source (MIT), лежит на npm и GitHub (tonnode/mcp). Это не ещё один REST-враппер поверх tonapi, а прямой разговор с сетью.

Типовые запросы, ради которых вы ходили в tonapi, покрываются инструментами чтения один-в-один:

  • get_balance — баланс GRAM на адресе.
  • get_jetton_balance — баланс USDT или любого жетона; джеттон-кошелёк вычисляется он-чейн, вам не нужно самому выводить его адрес. Как это заменяет связку из нескольких запросов — в заметке баланс USDT на TON одним вызовом.
  • get_account_state — статус аккаунта, флаги, последняя транзакция.
  • get_transactions — история транзакций.
  • run_get_method — любой read-only get-метод контракта.
  • get_masterchain_info — голова мастерчейна (актуальный блок).
  • get_jetton_info — метаданные жетона: имя, символ, эмиссия и decimals (у USDT — 6, у большинства жетонов — 9; без них вы неправильно пересчитаете raw-единицы).
  • parse_address — офлайн-конвертация и проверка адресов EQ/UQ/raw, вообще без обращения к сети.

Небольшая заметка про нейминг: GRAM — это переименованный в июне 2026 Toncoin. Сеть по-прежнему называется TON, поменялось только имя монеты. Балансы get_balance — в GRAM.

Пример промпта, который агент выполнит через инструменты, а не через ручной HTTP:

Проверь баланс GRAM и баланс USDT на адресе
UQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XF_wm
и покажи последние 5 транзакций этого кошелька.

Агент сам вызовет get_balance, затем get_jetton_balance (вычислив джеттон-кошелёк он-чейн), затем get_transactions — без единого написанного вами HTTP-запроса, без Retry-After и без ручного бэкоффа в вашем коде.

Как подключить за минуту: локально для разработки или hosted-ключ под прод

Есть два способа, и оба честные. Важно не путать их: локальный npx без ключа удобен для разработки, но работает на публичном конфиге TON; персональный лимит, который снимает боль 429 под нагрузкой, даёт именно hosted-ключ.

Вариант A — локально, бесплатно, без ключа (для разработки)

Полный набор инструментов чтения поднимается одной командой через npx — регистрация и карта не нужны. Добавьте в конфиг MCP-клиента:

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

Идеальный старт для локальной разработки и проверки: полный набор чтения, ноль конфигурации, open source под капотом. Но честная оговорка: этот режим ходит по публичному конфигу TON, то есть подвержен тем же общим ограничениям, что и любые публичные лайтсерверы (not ready, ADNL-таймауты под нагрузкой). Для прод-трафика это не замена персональному лимиту — за ним идите в вариант B.

Вариант B — hosted-эндпоинт со своим ключом (для прода)

Когда нужна гарантированная пропускная способность и персональный лимит под нагрузкой прода, подключаете hosted-эндпоинт со своим Bearer-ключом:

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

Тарифы

На всех тарифах доступны все 16 инструментов TONNode — платите вы только за пропускную способность:

  • Hobby — бесплатно навсегда, 60 запросов/мин. Ключ выдаётся сразу после входа, без карты.
  • Pro — $29/мес, 300 запросов/мин.
  • Scale — $199/мес, 1200 запросов/мин.

Разница с анонимным tonapi очевидна: там вы делите ~1 req/s со всем интернетом, здесь у вас персональный потолок — уже на бесплатном hosted-ключе Hobby это 60 запросов в минуту только для вас, без плясок с бэкоффом и без случайных 429. Это уже не тот же самый бесплатный путь, что локальный npx без ключа: у hosted-ключа Hobby свой лимит, а не общий публичный пул.

Итог

429 от tonapi.io — это не баг и не мифическая «228», а честный сигнал: вы сидите на общем анонимном пуле и упёрлись в его потолок. Ретраи с бэкоффом, уважение к Retry-After, ограничение числа одновременных запросов и кэш снимают острую боль. Но по-настоящему проблема уходит только когда у вас появляется собственная ёмкость — а если вы строите на ИИ-агентах, ещё и удобнее делать это через MCP, где чтение TON идёт по нативному протоколу, а не через HTTP-прослойку.

Возьмите бесплатный hosted-ключ Hobby (60 req/min, без карты) и перестаньте ловить 429tonnode.io/dashboard?plan=hobby

Нужен запас под прод-нагрузку — сравните тарифы Pro и Scale: tonnode.io/pricing.

Дайте вашему агенту доступ к TON

16 MCP-инструментов: чтение, некастодиальные свапы, кроссчейн и кошельки. Бесплатный тариф — 60 запр/мин, карта не нужна.