tonapi.io وحدود المعدل: كيف تتخلّص من الخطأ 429
يعيد tonapi.io رمز HTTP 429 عند تجاوز حدّ المعدل. نشرح كيف تتخلّص من الخطأ 429، ولماذا «228» مجرد ميم، وكيف تحصل على مفتاحك الخاص عبر MCP من TONNode.
tonapi 429: الثامنة والنصف مساءً، الإنتاج يحترق، وفي السجلات جدار أحمر
بوتك على TON دخل للتوّ إحدى القوائم المختارة، فانهال المستخدمون، وفجأة صار التطبيق يعيد أرصدة فارغة. تفتح السجلات فتجد مئات الأسطر:
HTTP 429 Too Many Requests
مألوف؟ أنت تذهب إلى tonapi.io طلبًا للأرصدة وتاريخ المعاملات، وكان كل شيء يعمل مع عشرة مستخدمين، ثم عند الألف اصطدم الوصول المجهول بالسقف. والقصة كلاسيكية: ما دام الترافيك ضئيلًا يبدو الـ API العام مجانيًا ولا نهائيًا. وما إن يرتفع الحمل حتى يتحوّل إلى عنق زجاجة، فتلتقط tonapi 429 بالجملة. ليس هذا خللًا في كودك ولا «عقدةً سقطت» — بل هو حدّ معدل الـ API العام، وعلاجه معروف ومتوقَّع.
لنستعرض لماذا يظهر 429، ولماذا لا علاقة لـ «228» الشهيرة بالأمر، وأي الإصلاحات تنفع فعلًا، وكيف تغادر المجمّع المجهول المشترك إلى حدٍّ شخصي لقراءة TON — عبر خادم MCP التابع لـ TONNode.
لماذا يعيد tonapi.io الرمز 429 Too Many Requests
tonapi.io هو HTTP API عام لبيانات TON. وكأي خدمة عامة، له حدّ معدل (tonapi rate limit) — تقييد لتواتر الطلبات كي لا يلتهم عميلٌ واحد السعة كلها.
وحين تذهب بلا مفتاح، فأنت تتقاسم المجمّع المجهول المشترك مع كل مجهولي الكوكب. وعمليًا يقارب هذا الحدّ طلبًا واحدًا في الثانية. وهو محتمَل لسكربت واحد. لكن ما إن يصير لديك عاملٌ متوازٍ يسحب لكل محفظة الرصيد، ثم رصيد الجيتون، ثم التاريخ — حتى تخرج فورًا عن حدّ الثانية. والألم مضاعَف عند الإقلاع البارد، حين يلزمك فهرسة عناوين كثيرة دفعةً واحدة.
ويردّ الخادم هكذا:
HTTP/1.1 429 Too Many Requests
Retry-After: 1
ومن المهم أن تفهم: 429 رمز HTTP قياسي وارد في المواصفة، لا رمز خاص بـ tonapi. تعيده أي خدمة عند تجاوز التواتر — GitHub وStripe وCloudflare وأي خدمة عليها حدّ معدل. والآلية نفسها عند toncenter وعند معظم مزوّدي RPC. ومعنى ذلك أنه يُعالَج بالأساليب القياسية التي نذكرها أدناه.
«الخطأ 228» ليس رمز API بل ميم مجتمعي (الرمز الحقيقي هو 429)
إن كنت قد بحثت عن المشكلة في المنتديات الناطقة بالروسية، فلا شك أنك صادفت «الخطأ 228». لنضع النقاط على الحروف، لأن هذا يربك المبتدئين حقًا.
«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) هو خادم MCP مُستضاف لشبكة TON. وMCP (Model Context Protocol) معيارٌ يستدعي بموجبه وكلاء الذكاء الاصطناعي (Claude وCursor وChatGPT/Codex وأي عميل MCP) أدواتٍ خارجية. فبدل أن تعلّم الوكيل طرْق https://tonapi.io/v2/... ومعالجة 429، تعطيه مجموعة أدوات مسمّاة لقراءة TON.
والنقطة الجوهرية: حزمة @tonnode/mcp تعمل عبر بروتوكول ADNL الأصلي في TON، دون طبقات HTTP وسيطة. وهي مفتوحة المصدر (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 — أي get-method للقراءة فقط في العقد.
- get_masterchain_info — رأس الماسترتشين (الكتلة الحالية).
- get_jetton_info — بيانات الجيتون الوصفية: الاسم، والرمز، والكمية المُصدَرة، وdecimals (لدى USDT قيمتها 6، ولدى معظم الجيتونات 9؛ وبدونها ستحسب الوحدات الخام حسابًا خاطئًا).
- parse_address — تحويل عناوين EQ/UQ/raw والتحقق منها محليًا، دون أي اتصال بالشبكة إطلاقًا.
ملاحظة صغيرة عن التسمية: GRAM هو Toncoin بعد إعادة تسميته في يونيو 2026. فالشبكة ما زالت تُسمّى TON، ولم يتغيّر إلا اسم العملة. وأرصدة
get_balanceبالـ GRAM.
ومثال على مطالبة سينفّذها الوكيل عبر الأدوات، لا عبر HTTP يدوي:
تحقّق من رصيد GRAM ورصيد USDT على العنوان
UQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XF_wm
واعرض آخر 5 معاملات لهذه المحفظة.
سيستدعي الوكيل بنفسه get_balance، ثم get_jetton_balance (بعد حساب محفظة الجيتون على السلسلة)، ثم get_transactions — دون طلب HTTP واحد تكتبه أنت، ودون Retry-After، ودون تراجع يدوي في كودك.
كيف توصّله في دقيقة: محليًا للتطوير أو بمفتاح مُستضاف للإنتاج
هناك طريقتان، وكلتاهما صادقة. والمهم ألا تخلط بينهما: فـ npx المحلي بلا مفتاح مريح للتطوير، لكنه يعمل على إعدادات TON العامة؛ أما الحدّ الشخصي الذي يزيل ألم 429 تحت الحمل فيمنحه المفتاح المُستضاف تحديدًا.
الخيار A — محليًا، مجانًا، بلا مفتاح (للتطوير)
تُشغَّل مجموعة أدوات القراءة الكاملة بأمر واحد عبر npx — بلا تسجيل ولا بطاقة. أضِف إلى إعدادات عميل MCP:
{
"mcpServers": {
"ton": {
"command": "npx",
"args": ["-y", "@tonnode/mcp"]
}
}
}
بداية مثالية للتطوير المحلي والتجريب: مجموعة قراءة كاملة، وصفر إعدادات، ومصدر مفتوح تحت الغطاء. لكن التحفّظ الصادق: هذا الوضع يسير على إعدادات TON العامة، أي أنه معرّض للقيود المشتركة نفسها التي تخضع لها أي لايت-سيرفرات عامة (not ready، ومهلات ADNL تحت الحمل). وهو ليس بديلًا عن حدٍّ شخصي لترافيك الإنتاج — ولهذا اذهب إلى الخيار B.
الخيار B — نقطة نهاية مُستضافة بمفتاحك الخاص (للإنتاج)
حين تحتاج إلى سعة تمرير مضمونة وحدٍّ شخصي تحت حمل الإنتاج، توصّل نقطة النهاية المُستضافة بمفتاح Bearer خاص بك:
{
"mcpServers": {
"ton": {
"type": "http",
"url": "https://mcp.tonnode.io/mcp",
"headers": { "Authorization": "Bearer tn_live_…" }
}
}
}
الباقات
على جميع الباقات تتوفّر أدوات TONNode الـ16 كلها — ولا تدفع إلا مقابل سعة التمرير:
- Hobby — مجانية إلى الأبد، 60 طلبًا/دقيقة. ويُسلَّم المفتاح فور تسجيل الدخول، بلا بطاقة.
- Pro — 29$ شهريًا، 300 طلب/دقيقة.
- Scale — 199$ شهريًا، 1200 طلب/دقيقة.
والفرق عن tonapi المجهول واضح: هناك تتقاسم ~1 req/s مع الإنترنت كله، وهنا لديك سقف شخصي — فحتى مفتاح Hobby المُستضاف المجاني يمنحك 60 طلبًا في الدقيقة لك وحدك، بلا بهلوانيات تراجع وبلا 429 عشوائية. وهذا ليس المسار المجاني نفسه الذي يمثّله npx المحلي بلا مفتاح: فلمفتاح Hobby المُستضاف حدّه الخاص، لا مجمّع عام مشترك.
الخلاصة
الرمز 429 من tonapi.io ليس علّة ولا «228» أسطورية، بل إشارة صادقة: أنت جالس على مجمّع مجهول مشترك وقد اصطدمت بسقفه. إعادة المحاولة مع تراجع، واحترام Retry-After، وتحديد عدد الطلبات المتزامنة، والتخزين المؤقت — كلها تخفّف الألم الحاد. لكن المشكلة لا تزول حقًا إلا حين تصير لك سعتك الخاصة — وإن كنت تبني على وكلاء ذكاء اصطناعي، فالأمر أيسر عبر MCP أيضًا، حيث تمضي قراءة TON بالبروتوكول الأصلي لا عبر طبقة HTTP وسيطة.
خذ مفتاح Hobby المُستضاف المجاني (60 req/min، بلا بطاقة) وكُفَّ عن التقاط 429 ← tonnode.io/dashboard?plan=hobby
تحتاج إلى هامش لحمل الإنتاج — قارِن باقتَي Pro وScale: tonnode.io/pricing.
امنح وكيلك الوصول إلى TON
16 أداة MCP: قراءة ومبادلات غير وصائية وعبر السلاسل ومحافظ. الباقة المجانية — 60 طلب/دقيقة، بلا بطاقة.