TRON Energy Rental API Limits and Error Codes: What Your Integration Must Handle
Oliver BentleyTRON Research EditorYou won't be handling one set of responses but three levels. The first is the HTTP API of the rental service itself: request rate, daily quota, key binding, plus its own order error codes (insufficient balance, invalid address, energy amount too small). The second is the node or TronGrid through which the delegation transaction is sent. The third is the TRON blockchain: broadcast errors and TVM execution errors. A retry that makes sense on one level will cause double payment on another, so parse responses by class rather than by message text.
Level 1: HTTP API limits
Quotas are set by the service itself: requests per second per key, daily order count, minimum lot. The exact numbers come from its documentation, but the order error classes are the same everywhere:
- Insufficient balance (
INSUFFICIENT_BALANCEand equivalents) — retrying is pointless, you need to top up the TRX deposit; watch the threshold in advance, otherwise delivery stalls right at the peak of USDT TRC-20 transfers. - Invalid recipient address — usually hex instead of base58, or a broken checksum. Validate the format (
T…, 34 characters) before ordering: energy is delegated to the address you specify, and the mistake can't be undone. - Amount or duration too small — services have a minimum lot, often around 32,000 energy, roughly one USDT TRC-20 transfer to an address with a non-zero balance; round your calculated amount up to the lot size.
The node that broadcasts the delegation produces the same classes: TronGrid answers a rate limit breach with 429, 403, or 503, and the body differs — the V1 API returns "success": false and "statusCode": 429, the node's HTTP proxy returns only an Error field, and the per-IP limit arrives with a Retry-After header. The documentation is explicit: you must not detect a limit from error text — look at the HTTP status, the body fields, and whether a key was passed.
- 429/503 — exponential backoff with jitter. Without jitter, several instances retry in sync and hit the limit again.
- 403 — don't hammer it with retries: first check the key, the security settings, and a possible temporary block.
- Cache read methods. Account state, network parameters, and block statistics don't need millisecond freshness: a TRON block appears roughly every 3 seconds, so polling more often is usually pointless.
Level 2: broadcast error codes
Energy delegation is an ordinary TRON transaction, so it gets the same set of responses as any other.
| Code | Cause | What to do |
|---|---|---|
SIGERROR | signature failed validation | verify the private key against the sender address and check the key's hex format; do not retry |
BANDWITH_ERROR | not enough Bandwidth or TRX for bytes, account creation, multisig, memo | this is not Energy exhaustion; check /wallet/getaccountresource, add Bandwidth or TRX |
DUP_TRANSACTION_ERROR | the same txID is already in the pending pool, in a block, or in the node's RPC cache | don't rebuild it; re-broadcast the same signed transaction to another synchronized node |
TAPOS_ERROR | the TAPOS block number and hash didn't match a recent block | rebuild against a fresh block, or more safely against the latest solidified one |
TRANSACTION_EXPIRATION_ERROR | the lifetime expired (60 seconds by default) | rebuild and sign again; the window can't be extended on public APIs |
SERVER_BUSY | the node's pending pool is full (node.maxTransactionPendingSize, 2000 by default) | retry with backoff or switch endpoints |
NOT_ENOUGH_EFFECTIVE_CONNECTION | too few effective P2P peers, or the transaction wasn't relayed to anyone | retry through another healthy endpoint |
The main trap is DUP_TRANSACTION_ERROR: it does not prove the transaction made it onto the blockchain. The rule: a transaction may only be rebuilt after its expiration has passed and you have confirmed it is not in the chain — otherwise it's easy to pay twice.
Level 3: energy errors and fee_limit
These errors relate to execution and occur after the transaction is already included in a block.
OUT_OF_ENERGY— execution consumed all the energy allowed byfee_limit; the energy spent is not refunded. Re-estimate consumption, raisefee_limit, and check the contract'sconsume_user_resource_percent.OUT_OF_TIME— the transaction's CPU budget was exceeded (chain parameter #13getMaxCpuTimeOfOneTx, currently 80 ms on Mainnet).
Two fee_limit mistakes show up in integrations constantly. The first is a value in TRX instead of sun: fee_limit = 100 authorizes 0.0001 TRX, and the call fails immediately with OUT_OF_ENERGY. The second is believing the limit is charged in full: it's a ceiling, and on normal execution — and on REVERT — only the energy actually used is charged. The node checks the condition 0 ≤ fee_limit ≤ MaxFeeLimit and returns ContractValidateException if it's exceeded — the transaction never reaches the VM and no energy is spent (details).
Don't hardcode the values: the fee_limit maximum (parameter #47, currently 15,000 TRX) and the energy price (getEnergyFee, currently 100 sun) are chain parameters changed by SR voting, and they are read via wallet/getchainparameters. The same goes for the dynamic model: with getAllowDynamicEnergy enabled, the multiplier for popular contracts reaches 3.4, and an amount rented "exactly per the estimate" may fall short (resource payment model).
What to build into the integration
- Estimate before ordering.
wallet/triggerconstantcontractreturnsenergy_used,wallet/estimateenergyreturnsenergy_required(not available on all public RPCs). Add a buffer to the estimate for the dynamic multiplier. - An idempotency key for every order. A timeout or a 5xx doesn't mean the order wasn't created: repeat the request with the same key.
- Classify responses in code: "limit" — backoff, "credentials" — alert with no retries, "state/network" — retry under the idempotency key, "business error" — log it and send it for manual review.
- Confirm by fact, not by HTTP 200. Cross-check the delivery webhook against
wallet/getaccountresourcefor the target address, and keep a backup endpoint:SERVER_BUSY,NOT_ENOUGH_EFFECTIVE_CONNECTION, and rate limiting are all cured by switching nodes, provided you planned for it.
Numeric boundaries — quotas, energy price, the fee_limit maximum, the CPU limit — should be read from the network and from the specific service's documentation, not from constants in your code: they change, and an energy delivery pipeline breaks silently.