Tất cả bài viết
12 phút đọc

toncenter 429 "Too Many Requests" — cách khắc phục triệt để

Lỗi 429 "Too Many Requests" trên toncenter: vì sao AI agent dính nhiều nhất, cách fix nhanh bằng backoff và giải pháp thật — key riêng với throughput đảm bảo.

toncenterlỗi 429rate limit TONTON MCPTONNodeAI agent TON

Agent token trên TON sập đúng vào thời điểm tệ nhất: người dùng nhờ "cho xem số dư và các giao dịch gần nhất của tôi", agent ngoan ngoãn gọi sang toncenter — và thứ bay về không phải JSON chứa dữ liệu, mà là dòng HTTP 429 Too Many Requests khô khốc. Chỉ trong một tick, agent đã cố gom số dư ví, trạng thái account, lịch sử giao dịch rồi gọi thêm vài get-method — và toncenter công cộng đóng sầm cửa ngay ở request thứ hai. Người dùng thấy "có gì đó không ổn", còn bạn thì nhìn vào một bức tường log đỏ toàn một mã status. Đây không phải bug trong code của bạn. Đây là rate limit công cộng của toncenter, thứ mà bất kỳ agent nào cũng đâm phải ngay khi nó bắt đầu chạy nhanh hơn một request mỗi giây.

Cùng mổ xẻ xem toncenter 429 từ đâu ra, vì sao chính các AI agent lại dính Too Many Requests trên TON nhiều hơn ai hết, cách hạ nhanh tần suất lỗi bằng backoff — và cách dỡ bỏ hẳn cái trần đó bằng key riêng với throughput được đảm bảo.

429 "Too Many Requests" trên toncenter nghĩa là gì

Mã 429 là phản hồi HTTP chuẩn cho "quá nhiều request". Server không hỏng, cũng không từ chối dữ liệu của bạn: nó chỉ đang nói rằng bạn đã vượt quá tần suất gọi được phép và đang bị ghìm lại (rate limiting). Với toncenter (v2), phần body của phản hồi khi đó trông đại khái thế này:

{ "ok": false, "error": "Rate limit exceeded", "code": 429 }

Điểm mấu chốt: "ok": false"code": 429 — đây là HTTP status được lặp lại trong body, chứ không phải mã lỗi nội bộ của contract. Và để ý kỹ: ở đây hoàn toàn không có trường result chứa dữ liệu account — nội dung về giới hạn nằm trong trường error. Vì vậy nếu code của bạn cứ chăm chăm lấy dữ liệu ra từ result, nó sẽ nhận undefined và nhiều khả năng sẽ chết ở đâu đó sâu hơn trong stack với một lỗi parse khó hiểu — trong khi nguyên nhân thật sự nằm ở error. Quy tắc rất đơn giản: khi gặp ok: false, hãy kiểm tra code và đọc error trước, đừng cố parse cái result không tồn tại.

Nói riêng về phần giai thoại: trong cộng đồng TON vẫn lưu truyền chuyện "lỗi 228". Đó không phải mã API, mà là một meme — chẳng server nào trả về 228 cho bạn cả. Mã giới hạn thật sự chính là 429, và đó mới là thứ cần tra trong tài liệu. 429 là lỗi tạm thời: chính lệnh gọi đó sẽ đi qua nếu bạn gọi chậm hơn hoặc gọi kèm một key hợp lệ.

Vì sao không có key thì toncenter chỉ cho ~1 request/giây

toncenter công cộng khi không có API key sẽ giới hạn bạn ở khoảng một request mỗi giây. Đây không phải bug, cũng không phải chuyện keo kiệt — đó là cách bảo vệ một tài nguyên miễn phí dùng chung mà hàng nghìn người đang xài cùng lúc. Vài chi tiết mà ngay cả lập trình viên có kinh nghiệm cũng vấp phải:

  • Pool dùng chung là chuyện của traffic ẩn danh. Không có key, bạn chia sẻ một hạn mức công cộng duy nhất khoảng 1 rps với mọi request vô danh từ khắp thế giới. Vào giờ cao điểm, tần suất thực tế bạn nhận được thậm chí có thể còn thấp hơn.
  • Key trả phí nâng trần lên — nhưng chỉ khi nó thật sự được gửi kèm trong request. Cái bẫy kinh điển: key thì có, nhưng header X-API-Key lại thất lạc trong lúc refactor HTTP client, thế là bạn lại rơi về hạn mức công cộng, hàng giờ liền không hiểu vì sao gói trả phí "không chạy".

Một request mỗi giây thì vẫn ổn với việc debug thủ công trên trình duyệt, hay một script mỗi phút kiểm tra số dư một lần. Nhưng với bất kỳ hệ thống tự động nào — và nhất là với một agent — ngần đó ít đến mức chết người.

Vì sao AI agent dính 429 nhiều nhất: burst pattern

Đây mới chính là mấu chốt. Một ứng dụng thông thường gửi request tương đối đều đặn. AI agent thì hoạt động khác — theo burst (những đợt bùng nổ). Nó chạy theo tick: ở một bước suy luận, nó cần gom đủ ngữ cảnh, thế là nó bắn hàng chục lệnh gọi liên tiếp, gần như đồng thời.

Hãy hình dung một bước: "người dùng nhờ kiểm tra xem tiền đã về chưa". Để trả lời, trong tích tắc agent gọi liên tiếp:

  1. get_masterchain_info — biết head hiện tại của blockchain;
  2. get_balance — số dư ví;
  3. get_account_state — trạng thái và các flag của account;
  4. get_transactions — các giao dịch gần nhất;
  5. run_get_method — đọc một get-method của contract;
  6. get_jetton_balance — và tiện thể cả số dư USDT.

Sáu lệnh gọi trong một tick. Hạn mức là một lệnh mỗi giây. Lệnh đầu tiên đi lọt, số còn lại trả về 429, và agent hoặc là kẹt cứng, hoặc bắt đầu bịa chuyện trên dữ liệu thiếu. Mà đây không phải "agent viết ẩu" — đây là kiến trúc bình thường của suy luận tự chủ: gom ngữ cảnh trước, rồi mới nghĩ. Tệ hơn nữa, sau khi ăn lỗi, agent thường cố "sửa" chúng bằng cách gọi lại — và đập nốt phần quota vốn đã cạn. Hạn mức công cộng đơn giản là không được thiết kế cho kiểu tải này.

Câu chuyện tương tự cũng xảy ra với các public liteserver trong global config của TON — dưới tải, chúng trả not ready hoặc rụng vì ADNL timeout. Về chuyện này có bài riêng: vì sao liteserver trả "not ready" và phải làm gì. Còn tonapi.io cũng mắc đúng căn bệnh 429 — phân tích ở đây.

Fix nhanh: retry với exponential backoff và Retry-After

Việc đầu tiên nên làm ngay lúc này là ngừng lao vào tường ở tốc độ tối đa. Nếu 429 vẫn bay tới, đừng nện lại server ngay lập tức: làm vậy chỉ kéo dài thời gian bị chặn. Đây là giải pháp tạm — nó giảm tần suất 429, nhưng không nâng được trần. Một giải pháp tạm làm cho đúng gồm bốn phần.

1. Exponential backoff kèm jitter. Mỗi lần thử lại hãy chờ lâu hơn lần trước, cộng thêm một lượng ngẫu nhiên, để các worker chạy song song không bị đồng bộ với nhau rồi cùng dồn vào một giây.

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;

    // Ưu tiên Retry-After từ server, nếu không thì dùng hàm mũ kèm jitter
    const retryAfter = res.headers.get("Retry-After");
    const baseMs = retryAfter
      ? Number(retryAfter) * 1000
      : Math.min(1000 * 2 ** attempt, 16000);
    const jitter = Math.random() * 300;
    await new Promise((r) => setTimeout(r, baseMs + jitter));
  }
  throw new Error("toncenter: 429 vẫn chưa buông sau các lần retry");
}

2. Hãy tôn trọng header Retry-After nếu nó xuất hiện. toncenter thường không gửi kèm header này trong phản hồi 429, nên hãy đặt cược chính vào công thức backoff của riêng bạn. Nhưng nếu server có nói rõ phải chờ bao lâu — hãy chờ đúng chừng đó, đừng đoán mò (nhánh if (retryAfter) trong code phía trên làm đúng việc này).

3. Hãy giới hạn mức đồng thời. Đặt trước toncenter một hàng đợi hoặc semaphore chỉ thả ra không quá ~1 request mỗi giây. Khi đó burst của agent sẽ được kéo giãn theo thời gian: đúng danh sách sáu lệnh gọi đó sẽ chạy tuần tự, hết ~6 giây, nhưng không dính một cái 429 nào.

4. Hãy cache những lần đọc lặp lại. get_masterchain_info chỉ cần gọi một lần trong phạm vi một bước rồi dùng lại. Metadata của jetton (decimals, ký hiệu) thì không đổi — đọc một lần là đủ. Số dư mà bạn đã đọc cách đây 300 ms thì khó mà kịp thay đổi.

Cách này có tác dụng và làm agent lịch sự hơn, nhưng hãy thành thật: backoff không nâng được trần. Bạn vẫn bị nhốt trong 1 rps, chỉ khác là giờ bạn xếp hàng một cách lịch sự thay vì đổ gục. Người dùng phải chờ hàng giây ở chỗ lẽ ra chỉ cần chờ vài mili giây. Với một agent cần độ phản hồi nhanh, đây là chữa triệu chứng chứ không chữa bệnh. Những đường vòng khác quanh các API công cộng nằm trong tuyển tập các lựa chọn thay thế toncenter cho năm 2026.

Giải pháp thật sự: key riêng và TON qua MCP

Gốc rễ của vấn đề nằm ở chỗ bạn đang chia sẻ một kênh công cộng chật hẹp với hàng nghìn request ẩn danh. Cách duy nhất để dẹp 429 một cách thật sự là có được throughput riêng, được đảm bảo thay vì một hạn mức dùng chung. Khi đó burst sáu lệnh gọi của agent đi trọn vẹn thay vì đâm tường ở lệnh thứ năm, và agent có thể gom ngữ cảnh ở tốc độ tối đa. Retry kèm backoff vẫn được giữ lại như một lớp bảo hiểm trước sự cố mạng, nhưng không còn là cơ chế sinh tồn chính nữa.

Và có một con đường còn dẹp luôn cả chuyện phải loay hoay với HTTP status. Nếu dữ liệu từ TON là để phục vụ đúng một AI agent, thì hợp lý hơn cả là đưa nó ra dưới dạng những công cụ dựng sẵn qua MCP, thay vì một phản hồi REST thô mà bạn phải tự parse rồi tự xử lý 429.

MCP (Model Context Protocol) là chuẩn để các AI agent (Claude, Cursor, ChatGPT/Codex, mọi MCP client) gọi công cụ bên ngoài. Thay vì dạy agent cách dựng đúng một HTTP request tới toncenter, bắt 429, đọc Retry-After rồi parse JSON, bạn trao cho nó những công cụ đã được định kiểu sẵn, còn toàn bộ phần việc nặng nhọc với mạng thì server gánh.

TONNode là MCP server hosted dành cho TON, đúng 16 công cụ. Chính sáu lệnh đọc mà agent thường dùng để chọc thủng hạn mức của toncenter, ở đây là những lệnh gọi dựng sẵn:

  • get_masterchain_info — head của masterchain;
  • get_balance — số dư GRAM;
  • get_account_state — trạng thái, các flag, giao dịch cuối cùng;
  • get_transactions — lịch sử giao dịch;
  • run_get_method — bất kỳ get-method read-only nào của contract;
  • get_jetton_balance — số dư jetton hoặc USDT (địa chỉ jetton wallet được tính on-chain).

(Nhân tiện, GRAM chính là Toncoin được đổi tên hồi tháng 6/2026. Mạng vẫn gọi là TON, chỉ có tên đồng coin là thay đổi.)

Bên dưới lớp vỏ, gói @tonnode/mcp chạy theo giao thức gốc của TON — ADNL, không có lớp HTTP trung gian. Nghĩa là bạn không chỉ đơn thuần đổi một REST endpoint này lấy một cái khác: agent nói chuyện trực tiếp với mạng, còn bạn thì không còn phải tự tay dọn dẹp ngữ nghĩa HTTP của các hạn mức nữa. Gói này là open source (MIT), nằm trên npm và GitHub (tonnode/mcp).

Khác biệt trên thực tế: agent không còn cần biết 429, Retry-After hay ADNL timeout là gì nữa. Nó nói "cho tôi số dư và các giao dịch gần nhất của địa chỉ này" — và nhận về một phản hồi có cấu trúc. Vẫn burst sáu lệnh gọi đó, nhưng giờ nó đi tới một server có sẵn throughput gắn với key của bạn, chứ không đâm vào hạn mức công cộng dùng chung.

Cách kết nối và bắt đầu miễn phí

Có hai con đường, và bạn có thể bắt đầu mà hoàn toàn không cần đăng ký.

Miễn phí và chạy local

Trọn bộ công cụ đọc qua config công khai — chỉ cần thêm vào phần cài đặt của MCP client:

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

Không cần key nào cả, npx sẽ tự kéo package về. Chừng đó là đủ để dùng thử và tự thấy rằng agent đọc TON mà không dính một cái 429 nào. Phân tích chi tiết nằm trong hướng dẫn dùng MCP cho TON miễn phí.

Endpoint hosted với key riêng

Khi bạn cần throughput được đảm bảo cho tải thật, hãy kết nối endpoint hosted với key riêng của mình:

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

Sau đó bạn chỉ việc nói chuyện với agent bằng ngôn ngữ đời thường — nó sẽ tự chọn đúng công cụ cần dùng:

"Kiểm tra địa chỉ EQC…: lấy số dư qua get_balance, trạng thái account qua get_account_state và 10 giao dịch gần nhất qua get_transactions. Sau đó dùng get_jetton_balance xem số dư USDT."

Agent sẽ gọi đúng những công cụ cần thiết theo đúng thứ tự — chứ không mù quáng đâm đầu vào rate limit.

Các gói cước

Trên mọi gói cước, cả 16 công cụ đều khả dụng — bạn chỉ trả tiền cho throughput:

Gói Giá Throughput
Hobby miễn phí vĩnh viễn 60 request/phút
Pro $29/tháng 300 request/phút
Scale $199/tháng 1200 request/phút

Ngay cả gói Hobby miễn phí với 60 request/phút cũng đã là một chế độ hoàn toàn khác so với toncenter công cộng và cái ~1 rps của nó: vẫn là ~60 request mỗi phút, nhưng giờ chúng chắc chắn là của bạn, chứ không dùng chung với đám đông ẩn danh. Burst cả chục lệnh gọi của agent đi trọn vẹn, không dính một cái 429 nào. Key Hobby được cấp ngay sau khi đăng nhập, không cần thẻ.

Các gói trả phí có thể thanh toán bằng GRAM hoặc USDT trên mạng TON qua TonConnect, hoặc bằng BTC/ETH/SOL và nhiều đồng khác qua hoá đơn xRocket trong Telegram.

Vài lời nói thẳng để tránh kỳ vọng quá đà: TONNode hiện phục vụ các tác vụ đọc và dựng giao dịch, nhưng không cung cấp dưới dạng sản phẩm hoàn chỉnh những thứ như pay-per-request, REST API v2, webhook, SSE stream hay SLA bằng văn bản kèm cam kết phần trăm uptime. Archive node với lịch sử sâu vẫn đang đồng bộ — đó là một mục trong roadmap, chứ không phải cam kết của hôm nay. Còn tất cả những gì mô tả ở trên thì đã chạy được ngay bây giờ.

Kế hoạch chuyển đổi thực tế

  1. Nhận key Hobby miễn phí (60 request/phút, không cần thẻ): tonnode.io/dashboard?plan=hobby.
  2. Ghi config hosted phía trên vào MCP client của bạn.
  3. Thay các lệnh gọi toncenter thủ công bằng những công cụ get_balance, get_account_state, get_transactions, run_get_method, get_jetton_balance.
  4. Ngay khi agent chạm trần 60 request/phút dưới tải — hãy chuyển lên Pro giá $29/tháng (300 request/phút): tonnode.io/pricing.

Kết luận

  • 429 "Too Many Requests" trên toncenter là chuyện đụng trần hạn mức công cộng ~1 rps, chứ không phải code của bạn hỏng. (Và chắc chắn nó không phải "228" — mã đó không tồn tại.)
  • Các AI agent dính nó nhiều hơn ai hết vì burst pattern: hàng chục lệnh gọi trong một tick suy luận.
  • Retry với exponential backoff, Retry-After, semaphore và cache giúp giảm tần suất 429, nhưng không dịch được cái trần — và phải trả giá bằng tốc độ.
  • Lối ra thật sự là key riêng với throughput được đảm bảo. Còn nếu dữ liệu là để phục vụ đúng một agent, TONNode đưa ra chính những lệnh đọc TON đó dưới dạng công cụ MCP, và câu hỏi "xử lý 429 thế nào" đơn giản là biến mất khỏi code của bạn.

Hãy bắt đầu miễn phí: nhận key Hobby — 60 request/phút, không cần thẻ. Nếu agent của bạn có tải thật và các burst dày đặc — hãy lấy luôn Pro giá $29/tháng, 300 request/phút.

Cho agent của bạn quyền truy cập TON

16 công cụ MCP: đọc, swap phi lưu ký, cross-chain và ví. Gói miễn phí — 60 req/phút, không cần thẻ.