tonapi.io і rate-ліміти: як прибрати помилку 429
tonapi.io віддає HTTP 429 при перевищенні rate-ліміту. Розбираємо, як прибрати помилку 429, чому «228» — це мем, і як отримати свій ключ через MCP TONNode.
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, без картки) і перестаньте ловити 429 → tonnode.io/dashboard?plan=hobby
Потрібен запас під прод-навантаження — порівняйте тарифи Pro і Scale: tonnode.io/pricing.
Дайте вашому агенту доступ до TON
16 MCP-інструментів: читання, некастодіальні свопи, кросчейн і гаманці. Безкоштовний тариф — 60 зап/хв, картка не потрібна.