Without an API key, TronGrid allows 3 requests per second. Go above that and it answers HTTP 429 with a JSON body that names allowed_rps(3), and it suspends you for 5 seconds. The response carries no rate-limit headers and no Retry-After, so the body is the only signal you get. A free API key sent in the TRON-PRO-API-KEY header raises the limit: TronGrid’s own plan configuration lists 15 requests per second per key and 100,000 requests per day on the free Basic plan.
We measured all of this on 24 October 2026, between 12:00 and 12:35 UTC. One caveat on the keyed figures: developers.tron.network, TronGrid’s documentation host, answered HTTP 403 to every client we tried, so our tools could not read its rate-limit pages. The keyed numbers below come from TronGrid’s own site (its release notes, dashboard text and plan configuration). TronGrid also announced paid plans two days before our check, so confirm current plan limits in the TronGrid dashboard before you size anything.
What the limit is and how it fails
TronGrid’s release note v1.10.0 reduced the default rate for unauthenticated requests “from 10 to 3”. We saw the 3 on the live endpoint. This is what keyless https://api.trongrid.io did when we pushed it:
| What we checked | Result on 24 October 2026 |
|---|---|
| Keyless limit | 3 requests per second (allowed_rps(3)) |
| Status code | 429 |
| Body | JSON with an Error key, shown below |
| Suspension | “suspended for 5 s” |
| Rate-limit headers | none, and no Retry-After |
| Bad API key | 401 with {"Error":"ApiKey not exists"} |
The 429 body, with its JSON escaping removed:
{"Error":"request rate exceeded the allowed_rps(3), and the query server is suspended for 5 s. To obtain higher request quotas and a more stable service, it is recommended to authenticate with an API Key. Please refer to the documentation: https://developers.tron.network/reference/select-network#how-to-get-an-api-key"}
The /jsonrpc door is under the same keyless limit as the HTTP routes. A run of 30 sequential eth_blockNumber calls got 3 answers and then 27 429s in 5.6 seconds. If you use both interfaces, our Tron JSON-RPC vs HTTP API guide shows which calls belong where. We could not tell whether /wallet, /jsonrpc and /v1 calls share one counter, so budget as if they do.
How long the suspension lasts
We polled every 0.5 seconds after a 429. The number in the body counted down from 5, and the first 200 came 5.4 seconds after the first 429. Polling during the suspension did not extend it.
The suspension is short, but retrying wrongly turns it into a longer outage. We sent 10 concurrent requests with no limiter and a plain retry. All 10 eventually succeeded, but it took 22 requests (12 of them 429s), and the last one finished after 17.3 seconds. They completed in waves: three at 0.7 s, three at about 6 s, three at about 12 s and one at 17.3 s. Each wave ran into the limit again. A client that spaces its own requests never lands in that loop.
What older documentation says
Other descriptions differ. Search summaries of developers.tron.network pages (which our tools could not open) mention a 403 alongside the 429, and Chainstack’s TronGrid comparison (updated 15 October 2026), describes a 403 and a block of about 30 seconds. Chainstack’s wording is “a 30-second access block returning 403 errors”.
We did not see that. In about 250 keyless requests we got 200s, 429s and the 401 above, and never a 403. We cannot say whether that behaviour applies to other keys, plans or regions. Treat the two descriptions as both possible: our code below retries on 429, and if you see a 403 with a block that outlasts 5 seconds, back off longer and log the body.
One more case we could not measure: what a key returns when it hits its daily request cap. TronGrid says that on its new plan system further requests are blocked until the limit resets, but we did not have a key at its cap to see the status code.
API keys and the TRON-PRO-API-KEY header
You get a key by signing up at the TronGrid dashboard and choosing to generate an API key. Send it in a header named TRON-PRO-API-KEY on every request:
curl -s -X POST https://api.trongrid.io/wallet/getchainparameters \
-H 'TRON-PRO-API-KEY: YOUR_TRONGRID_KEY'
TronWeb takes the same header in its constructor (headers: { "TRON-PRO-API-KEY": ... }) or through tronWeb.setHeader(...). The sample further down does the first.
We proved the server reads that header with a control: a key of all zeros returned 401 ApiKey not exists, and so did a malformed one. A wrong key is worse than no key, because the keyless path would have answered. If you load the key from an environment variable, make the client omit the header when the variable is empty instead of sending an empty value. We did not have a valid key, so the keyed limit below is TronGrid’s figure and not our measurement.
What the keyed limits are
TronGrid publishes its plan configuration as JSON that its own pricing page reads. We fetched it at 12:11 UTC on 24 October 2026:
- Basic (free): 15 requests per second per key, 100,000 requests per day, 3 keys per account
- Trial: 15 requests per second per key, 500,000 requests per day, 3 keys
Two rules from TronGrid’s release notes matter when you add keys. The requests-per-second limit applies to each key, and the daily quota is shared by every key on the account. A second key therefore gives you a second 15 requests per second, but not a second 100,000 requests per day.
TronGrid’s v1.12.0 release note, dated October 22 (the page prints no year), announces paid API plans that differ by requests per second per key, daily requests per account and number of keys. It says existing Basic free accounts keep their current daily limit of 100,000 in that release, and dashboard text describes a new Free plan of 10,000 requests per day and 15 requests per second per key. As of our check the plan configuration still showed billing disabled and no paid plans, and we could not retrieve plan names or prices. So which of these numbers applies to your account depends on when you read this. TronGrid’s terms also say it can change rate limits and plan terms at any time. Read the dashboard, not a blog post, for the figure you plan around.
Testnets: Nile and Shasta
TronGrid’s Documents page says that accessing the Shasta and Nile testnets through TronGrid does not require an API key. We tested both without one:
- Nile (
nile.trongrid.io): bursts of 40/jsonrpcrequests at concurrency 10 never drew a 429, at 14 to 23 requests per second in two runs. - Shasta (
api.shasta.trongrid.io): 30 sequential/wallet/getnowblockcalls all succeeded, but 30/jsonrpccalls at concurrency 5 got 16 answers and 14 429s. The body was plain text,Too Many Requests - Rate Limit Exceeded, not the JSONErrorobject mainnet returns.
So keyless Nile did not throttle at the rates we tried, and keyless Shasta did, at a threshold we did not pin down. If your client parses the mainnet 429 body for the suspension time, expect it to find nothing on Shasta and fall back to plain backoff. BoltRPC lists Tron mainnet only on the Tron network page, so keep your test runs on a TronGrid testnet.
Handling the limit in code
Three rules cover it: stay under the limit on the client side, read the suspension time out of the 429 body, and cap your retries. This client does all three with a token bucket. It uses 2.5 requests per second with a burst of 1 against the keyless limit of 3. Set rate below the QPS of your plan when you add a key.
// Tron HTTP client with a client-side token bucket and retry/backoff on 429.
// Works against any Tron full-node HTTP API base URL. Only BASE (and the key) change per provider.
const BASE = process.env.TRON_BASE ?? "https://api.trongrid.io";
const API_KEY = process.env.TRONGRID_API_KEY; // optional; sent as TRON-PRO-API-KEY
// Token bucket: `rate` tokens per second, holds at most `burst` tokens.
export class TokenBucket {
constructor(rate, burst = 1) {
this.rate = rate;
this.burst = burst;
this.tokens = burst;
this.last = Date.now();
}
async take() {
for (;;) {
const now = Date.now();
this.tokens = Math.min(this.burst, this.tokens + ((now - this.last) / 1000) * this.rate);
this.last = now;
if (this.tokens >= 1) {
this.tokens -= 1;
return;
}
await new Promise((r) => setTimeout(r, ((1 - this.tokens) / this.rate) * 1000));
}
}
}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
export function makeClient({ base = BASE, apiKey = API_KEY, rate = 2.5, burst = 1, maxRetries = 5 } = {}) {
const bucket = new TokenBucket(rate, burst);
const stats = { sent: 0, retries: 0, status429: 0 };
async function post(path, body) {
for (let attempt = 0; ; attempt++) {
await bucket.take();
stats.sent++;
const res = await fetch(base + path, {
method: "POST",
headers: {
"content-type": "application/json",
...(apiKey ? { "TRON-PRO-API-KEY": apiKey } : {}),
},
body: body === undefined ? undefined : JSON.stringify(body),
});
const text = await res.text();
if (res.status === 429) {
stats.status429++;
if (attempt >= maxRetries) throw new Error(`429 after ${attempt} retries: ${text.slice(0, 120)}`);
// TronGrid names the suspension in the body: "suspended for 5 s". Honor it, else back off exponentially.
const hinted = Number(/suspended for (\d+) s/.exec(text)?.[1] ?? 0) * 1000;
const backoff = Math.min(30000, 500 * 2 ** attempt);
const wait = Math.max(hinted, backoff) + Math.random() * 250; // jitter
stats.retries++;
await sleep(wait);
continue;
}
if (!res.ok) throw new Error(`${path}: HTTP ${res.status} ${text.slice(0, 120)}`);
return JSON.parse(text);
}
}
return { post, stats };
}
We ran 20 sequential /wallet/getblockbylatestnum calls through it on 24 October 2026: 20 requests in 8.3 seconds, no retries and no 429. With the bucket switched off (rate 1000) and 10 concurrent calls, it needed the retry path: 22 requests sent, 12 of them 429s, all 10 calls succeeded. Pointing TRON_BASE at Nile and at PublicNode also ran clean. The API key branch is implemented but we did not run it, because we had no key.
Two details. The bucket is per process, so five workers with 2.5 requests per second each add up to 12.5 against one client address. Share one bucket or divide the rate between them. And the retry loop waits for whichever is longer, the suspension named in the body or the exponential backoff, which starts at 500 ms and caps at 30 seconds, plus jitter so parallel callers do not retry in step.
The same idea with TronWeb
TronWeb 6.5.1 throws on a non-2xx response. A TronGrid 429 arrives as an AxiosError with response.status 429 and the TronGrid JSON in response.data. Wrap calls in a retry that reads it:
import { TronWeb } from "tronweb";
const apiKey = process.env.TRONGRID_API_KEY;
const tronWeb = new TronWeb({
fullHost: process.env.TRON_BASE ?? "https://api.trongrid.io",
headers: apiKey ? { "TRON-PRO-API-KEY": apiKey } : {},
});
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// TronWeb throws on a non-2xx. Wrap any call in a retry that backs off when the error text carries a 429.
async function withRetry(fn, retries = 5) {
for (let attempt = 0; ; attempt++) {
try {
return await fn();
} catch (e) {
const msg = String(e?.response?.data?.Error ?? e?.message ?? e);
const status = e?.response?.status ?? (/\b429\b/.test(msg) ? 429 : undefined);
if (status !== 429 || attempt >= retries) throw e;
const hinted = Number(/suspended for (\d+) s/.exec(msg)?.[1] ?? 0) * 1000;
await sleep(Math.max(hinted, 500 * 2 ** attempt) + Math.random() * 250);
}
}
}
// 1. a normal call
const block = await withRetry(() => tronWeb.trx.getCurrentBlock());
console.log("block:", block.block_header.raw_data.number);
// 2. 8 concurrent calls with no limiter: see how TronWeb reports a 429 without the wrapper
const raw = await Promise.allSettled(Array.from({ length: 8 }, () => tronWeb.trx.getCurrentBlock()));
const failed = raw.filter((r) => r.status === "rejected");
console.log("raw concurrent calls: ok =", raw.length - failed.length, "failed =", failed.length);
if (failed[0]) {
const e = failed[0].reason;
console.log("sample error:", e?.constructor?.name, "|", String(e?.message).slice(0, 200), "| response.status =", e?.response?.status);
}
// 3. the same 8 calls through the wrapper
await sleep(6000);
const wrapped = await Promise.allSettled(Array.from({ length: 8 }, () => withRetry(() => tronWeb.trx.getCurrentBlock())));
console.log("wrapped concurrent calls: ok =", wrapped.filter((r) => r.status === "fulfilled").length, "of 8");
Keyless, the run printed:
block: 87390534
raw concurrent calls: ok = 2 failed = 6
sample error: AxiosError | Request failed with status code 429 | response.status = 429
wrapped concurrent calls: ok = 8 of 8
The wrapper retries but does not space requests out, so it suits occasional bursts. For a scanner that runs all day, put a bucket in front of it. Our RPC rate limits explained article covers batching, caching and failover for the cases a bucket does not fix.
The /v1 trap when you leave TronGrid
Before you point your code at a different provider, grep it for /v1/. TronGrid serves an extension API under /v1 next to the standard node routes: account lookups, TRC-20 transaction history for an address, contract events. TronGrid’s key configuration names routes such as getTrc20TransactionsByAccount, getEventsByContractAddress and getTransactionByAccount. We called the TRC-20 transactions route under /v1/accounts on TronGrid and got data back.
That API is TronGrid-specific. We called four /v1 paths on PublicNode’s public Tron endpoint and each one returned a 404, while the /wallet, /walletsolidity and /jsonrpc routes on the same host answered normally. We then read the Tron pages of QuickNode, Chainstack, Ankr, GetBlock, NOWNodes, dRPC, Alchemy and PublicNode. None lists /v1 routes. That means “not listed”, which is not proof of absence. We did not test those providers’ live endpoints either. Chainstack’s own comparison page calls the TronGrid event API “TronGrid’s unique strength”.
/v1 calls also have their own lower limits. In the 429 bodies we collected, some /v1 routes reported allowed_rps(1), while /wallet/getnowblock and /walletsolidity/getnowblock reported 3. The same TRC-20 route reported 1 in one run and 3 in another, so read that as “some /v1 routes are limited harder”, not as an exact table.
Moving off TronGrid means replacing each /v1 call. For deposit scanning that usually means reading blocks directly, one /walletsolidity call per block or an eth_getLogs filter, which is the approach in our Tron USDT deposit guide. For wide history queries it means an indexer. Plain /wallet and /walletsolidity calls and /jsonrpc calls move by changing the base URL. /v1 calls do not.
When 15 requests per second is not enough
Your options, from least to most change:
- One key per service. Each account can hold several keys, and the requests-per-second limit applies to each key. Giving every service its own key keeps one runaway job from starving the rest. The daily quota is still shared across the account.
- A different provider. Each line below is what the provider’s own Tron page says, and none is a ranking:
- QuickNode: JSON-RPC,
/wallet,/walletsolidityand gRPC on mainnet and Nile. Rate limits vary by plan tier. - Chainstack:
/jsonrpc(read only, withouteth_sendRawTransactionandeth_getTransactionCount),/wallet,/walletsolidityand gRPC. Every TRON request is billed as 1 RU. - Ankr: JSON-RPC (26
eth_,web3_andnet_methods, includingeth_getLogs) and REST routes under/walletsolidityandwallet. - GetBlock: REST
/wallet/{method}and/walletsolidity/{method}, JSON-RPC and gRPC, on mainnet and testnet. - NOWNodes: Tron RPC plus BlockBook APIs for indexed address, balance, transaction and block lookups. Its page says there are no predefined RPS limits on paid plans.
- dRPC: documents a Tron JSON-RPC API and a Tron HTTP API. On 24 October 2026 its public endpoint
tron.drpc.orgrefused our/wallet/getnowblockcall with “method is not available on free plan, please upgrade to paid plan”, so we cannot say what its free plan allows. - Alchemy: the key goes in the URL path, with the
eth_*JSON-RPC set and/walletroutes such as/wallet/getnowblock. - PublicNode: a free public endpoint,
tron-rpc.publicnode.com./wallet,/walletsolidityand/jsonrpcall answered, and a burst of 30 requests at concurrency 5 got no 429. Its page states no limits.
- QuickNode: JSON-RPC,
- BoltRPC. Details below. For the full list of routes and examples, see the Tron network page.
Whichever you choose, the provider needs to cover what you actually call. Check the list of methods against your code before you move traffic, and check /v1 first.
Using BoltRPC for Tron
BoltRPC serves Tron’s full-node HTTP API (wallet/* and walletsolidity/*) and /jsonrpc. It has no /v1 routes, so code that calls them needs the replacement described above. Your key travels in the URL path, so the TRON-PRO-API-KEY header is not part of it. The JSON-RPC endpoint looks like this:
https://endpoints.boltrpc.io/rpc/YOUR_API_KEY/tron/jsonrpc
On rate limits: monthly plans have no per-second rate limit, and usage is metered in request units instead. The free trial and on-demand usage without a plan are capped at 100 requests per second. Per-method costs are on pricing. The details in this section come from our published allowlist and plan data. We ran this article’s rate-limit tests against TronGrid and the public endpoints above, not against BoltRPC.
One difference to check before you migrate: some wallet/* methods TronGrid lists are not on our allowlist. Comparing TronGrid’s key configuration with ours, the missing ones are mostly deprecated or key-handling routes such as easytransfer and createaddress, the shielded TRC-20 routes, and node routes such as getnodeinfo and listnodes. A few ordinary ones are in the same group, for example getaccountbyid and gettransactionreceiptbyid. The Tron network page and the docs list what is allowed.
FAQ
What is the TronGrid rate limit without an API key?
3 requests per second per client on the mainnet endpoint api.trongrid.io, measured on 24 October 2026. Above that, TronGrid returns HTTP 429 with a body naming allowed_rps(3), and it suspends you for 5 seconds. TronGrid’s v1.10.0 release note describes the same change, from 10 to 3. A free API key raises the limit.
How long does a TronGrid 429 last?
5 seconds when we measured it on 24 October 2026. The body says “suspended for 5 s” and counts down, and our first successful request came 5.4 seconds after the first 429. Polling during the suspension did not extend it. Older documentation describes a 403 and a block of about 30 seconds, which we did not reproduce in about 250 keyless requests.
How do I use a TronGrid API key?
Generate a key in the TronGrid dashboard and send it in the TRON-PRO-API-KEY request header. In TronWeb, pass headers: { "TRON-PRO-API-KEY": key } to the constructor or call tronWeb.setHeader. A wrong key returns 401 ApiKey not exists, so omit the header entirely when you have no key. Keyed limits per TronGrid’s plan configuration were 15 requests per second per key on 24 October 2026, but confirm yours in the dashboard.
Do the Nile and Shasta testnets need an API key?
No. TronGrid’s Documents page says accessing them through TronGrid does not require one. In our test keyless Nile did not throttle at about 23 requests per second, while keyless Shasta returned 429 with a plain-text body when we sent /jsonrpc calls at concurrency 5.
Do other Tron providers support TronGrid’s /v1 API?
None of the providers’ own Tron pages that we read lists /v1 routes, and PublicNode returned 404 on four /v1 paths while its other Tron routes worked. Plan to replace /v1 calls with /wallet or /walletsolidity calls, eth_getLogs or an indexer before you move.
Move your Tron traffic to BoltRPC
If TronGrid’s limits are what holds your Tron service back, try the same /wallet, /walletsolidity and /jsonrpc calls on a BoltRPC endpoint. The Tron network page has the URLs and examples.
