The fastest way to buy TRON energy in PHP is the official TronSave SDK: composer require tronsave/sdk, construct a client with your API key, estimate the cost, place the order, poll it to 100%. Four typed calls, no raw HTTP, no hand-rolled retry loop. This guide covers the full integration — install, defaults, error handling, and how the order actually gets paid.
Why a PHP backend needs this
PHP runs an enormous share of the merchant web — WooCommerce stores, Laravel payment services, custom billing panels. The moment one of those starts paying out or accepting USDT (TRC-20) on TRON, it hits the same arithmetic everyone else does: every transfer consumes energy, and energy you do not have is paid for by burning TRX at protocol rates. That is how a stablecoin payout ends up costing several dollars instead of a few cents.
Renting the energy just before a payout run typically costs a fraction of burning it. Doing that from code means the service tops itself up — no operator watching a dashboard at 3am, no batch stalling because a wallet ran dry. If you want the endpoint-level view first, read our API automation guide; this post is the same flow with PHP types instead of raw requests.

What is the TronSave PHP SDK?
It is the official PHP client for the TronSave v2 API, published to Packagist as tronsave/sdk (version 2.0.0, MIT licensed, PHP 8.1+). It sits alongside official SDKs for TypeScript, Python, Java, and Rust — all wrapping the same endpoints with the same behaviour, which is verified by a shared cross-language conformance suite rather than by hand.
The PHP client is small but shaped for production:
- Typed results. Responses come back as readonly classes —
UserInfo,EstimateBuyResource,Order,OrderBookLevel— not associative arrays you have to guess your way through. - Real enums.
ResourceType,PriceTier,OrderType,OrderStatus, andNetworkare PHP 8.1 backed enums, so a typo is a fatal error at the call site instead of a 400 from the server. - PSR-18 transport. Guzzle by default, but you can inject any PSR-18 client — useful if your framework already has a configured, instrumented HTTP stack.
- Retries and rate limiting built in. Both are on by default, and both are described below.
Install and configure
composer require tronsave/sdk
Then construct a client. Every constructor argument is optional and named, so you only set what you need:
use Tronsave\TronsaveClient;
use Tronsave\Network;
$sdk = new TronsaveClient(
apiKey: getenv('TRONSAVE_API_KEY'),
network: Network::Testnet, // Network::Mainnet to go live
timeoutMs: 30000, // default
maxRetries: 3, // default
);
Network carries its own base URL, so you never hardcode a hostname: Network::Testnet points at the Nile test API and Network::Mainnet at production. Build against the testnet, where nothing costs real TRX, then flip the enum.
The same enum also exposes fundAddress(), the address your prepaid balance is topped up at. Reading it from the enum rather than pasting a string into your config is the safer habit — a deposit address copied from a chat message or a search result is a classic way to lose a top-up.
Buying energy, end to end
Four calls. The first is optional but worth wiring into a health check.
use Tronsave\TronsaveClient;
use Tronsave\Network;
$sdk = new TronsaveClient(apiKey: getenv('TRONSAVE_API_KEY'), network: Network::Mainnet);
// 1. Who am I, and can I afford this run?
$me = $sdk->getUserInfo();
// $me->id, $me->balance, $me->representAddress, $me->depositAddress
// 2. What will it cost? (no API key required for this call)
$est = $sdk->estimateBuyResource([
'receiver' => 'TFwUFWr3QV376677Z8VWXxGUAMFSrq1MbM',
'resourceType' => 'ENERGY',
'resourceAmount' => 65000,
'durationSec' => 3600,
]);
// $est->estimateTrx, $est->unitPrice, $est->durationSec, $est->availableResource
// 3. Place the order.
$order = $sdk->buyResource(
['receiver' => 'TFwUFWr3QV376677Z8VWXxGUAMFSrq1MbM', 'resourceAmount' => 65000, 'durationSec' => 3600],
['allowPartialFill' => true, 'maxPriceAccepted' => 100],
);
// 4. Poll until the energy is actually delegated.
$status = $sdk->getOrder($order->orderId);
echo $status->fulfilledPercent; // 100 = fully delegated
echo $status->remainAmount; // 0 when nothing is left to fill
foreach ($status->delegates as $d) {
// $d->delegator, $d->amount, $d->txid
}
Two details in that snippet earn their keep. estimateBuyResource is unauthenticated — you can price a purchase before you have a key, and a pricing call in a hot path never burns your rate limit budget on auth. And the second argument to buyResource is the options bag: allowPartialFill lets the order fill from several providers instead of waiting for one big one, and maxPriceAccepted is your ceiling in SUN per unit, which is the line between a resilient integration and one that overpays during a price spike.
The order is not done when buyResource returns — it returns an orderId. Delegation lands a few seconds later. Poll getOrder until fulfilledPercent reaches 100 before you broadcast the transfer that needs the energy.
The defaults and limits worth knowing
| Behaviour | Value |
|---|---|
| Default resource type | ENERGY |
| Default price tier | MEDIUM (also FAST, SLOW, or a raw SUN price) |
| Default duration | 259200 seconds — 3 days |
| Minimum energy order | Must be greater than 64,000 |
| Client-side rate limit | 15 requests/second; 1/second for getExtendableDelegates |
| Default timeout / retries | 30,000 ms / 3 retries |
extendTo format |
Unix timestamp in seconds |
The 64,000 minimum is enforced client-side: an undersized order throws before an HTTP request is made, so a bad loop cannot spend its rate limit discovering the same rejection over and over. The rate limiter is client-side too — it paces your own calls rather than letting the server 429 you.
Error handling that survives production
Every failure is a typed exception extending Tronsave\TronsaveException, which carries ->code, ->status, and ->raw:
| Exception | When | What to do |
|---|---|---|
TronsaveAuthException |
401 | Fix the key. Never retry. |
TronsaveValidationException |
400, bad params | Fix the call. Never retry. |
TronsaveBusinessException |
400, rejected on the merits | Read ->code and branch. |
TronsaveRateLimitException |
429 | Back off for ->retryAfterMs. |
TronsaveNetworkException |
Transport failure | Safe to retry. |
use Tronsave\{TronsaveBusinessException, TronsaveRateLimitException};
try {
$order = $sdk->buyResource(['receiver' => $addr, 'resourceAmount' => 65000]);
} catch (TronsaveRateLimitException $e) {
usleep($e->retryAfterMs * 1000);
} catch (TronsaveBusinessException $e) {
$log->warning('order rejected', ['code' => $e->code, 'raw' => $e->raw]);
}
The split matters because it tells you what is retryable without guessing. The SDK already retries the transient cases for you — 408, 425, 429, 500, 502, 503 and 504 — with exponential backoff and jitter, honouring a Retry-After header when the server sends one. What reaches your catch block has already been retried, so the exceptions above are the ones your code genuinely has to decide about.
How the order gets paid
There are two payment paths, and the SDK picks between them based on what you pass:
- Prepaid balance (recommended for servers). Call
buyResourcewith nosignedTxand the order authenticates with your API key and settles against your TronSave balance. Your TRON private key never touches the integration — which is exactly what you want on a web server. - On-chain per order. Pass a
signedTxyou built and signed yourself, and the order is paid by that transaction instead.
The client also exposes getSignedTransaction(), which signs server-side. Its own docblock tells you to prefer local signing, and you should listen: that call sends a private key over the wire. For a PHP backend, the prepaid-balance path is both simpler and safer — use it.
Extending instead of re-buying
If a delegation you already hold is about to expire, extending it is cheaper than buying a fresh order. Call getExtendableDelegates() with a receiver and an extendTo Unix timestamp; it returns isAbleToExtend, totalEstimateTrx, and an extendData payload you hand straight to extendRequest(). Note the tighter 1-per-second limit on that first call — it is the one endpoint the SDK paces harder than the rest.
Our guide to the Extend feature explains when extending beats re-buying.
FAQ
What PHP version do I need?
PHP 8.1 or later. The SDK leans on backed enums, readonly properties, and named arguments, all of which landed in 8.0 and 8.1.
Do I need Guzzle?
It is the default, so it works out of the box. But the transport is PSR-18, so you can inject any compliant client — handy if your application already has one configured with its own logging, proxy, or timeout policy.
Can I test without spending real TRX?
Yes. Construct the client with Network::Testnet and you are on the Nile test network with a separate API host. Nothing costs real money there. Switch the single enum value to go live.
How much energy should I buy for a USDT transfer?
Roughly 65,000 for a recipient who already holds USDT, and about 130,000 for one who never has — see how much energy a USDT transfer needs. Remember the order minimum is greater than 64,000, so 65,000 is the smallest practical energy order.
Is the PHP SDK behind the TypeScript one?
No. All five clients target the same v2 API at the same version and are held to a shared conformance suite, so behaviour does not drift between languages. If you are polyglot, the TypeScript, Java, and Rust SDK guides describe the same flow.
What happens if an order only partially fills?
With allowPartialFill enabled, you get what the market could supply and fulfilledPercent reports how much. Check it before broadcasting the transaction that depends on the energy — a partially filled order may not cover your transfer.
Bottom line
If your PHP service moves USDT on TRON, energy is a running cost you can cut by most of its value, and the SDK makes that a four-call integration rather than a project. Install it, price the order without a key, buy against your prepaid balance so no private key touches your web server, and poll to 100% before you send.
Start integrating: run composer require tronsave/sdk, grab a key from the TronSave dashboard, and price your first order against the live energy market.
The TronSave SDK is published by TronSave, the publisher of this blog. Package versions, defaults, and limits described here reflect v2.0.0 and can change in later releases — check the official SDK documentation before you pin a version. This is not financial advice.
