GET /apiv2/usdt/{sender}&
Calculate TRON USDT transfer cost (public endpoint, no API key required).
Returns a detailed analysis of the sender and receiver accounts, resource requirements (energy/bandwidth) and recommended cost path.
Low rate limit — intended for occasional / testing use
This endpoint is shared globally and rate-limited at 1 req/sec and 60 req/min. When your application is behind Cloudflare or another reverse proxy, the limit may effectively be shared between all clients reaching Netts through the same edge, so you may see 429 Too Many Requests sooner than 60 requests/minute from a single user.
For anything beyond sporadic calls, use the authenticated POST /apiv2/usdt/analyze endpoint — it has a much higher per-key limit (50 req/sec).
Endpoint URL
GET https://netts.io/apiv2/usdt/{sender}&{receiver}URL parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| sender | string | Yes | TRON address of the sender |
| receiver | string | Yes | TRON address of the receiver |
Addresses are passed in the path, separated by an ampersand (&). Both must be valid base58 TRON addresses (34 characters, start with T, valid checksum).
Request examples
cURL
curl "https://netts.io/apiv2/usdt/TFLit1TFohBtT2f8UVCLFVPmZxawxqByYe&TTKR9aQdJWTgXLK9cmzaDitT5VXE497thL"Python
import requests
sender = "TFLit1TFohBtT2f8UVCLFVPmZxawxqByYe"
receiver = "TTKR9aQdJWTgXLK9cmzaDitT5VXE497thL"
url = f"https://netts.io/apiv2/usdt/{sender}&{receiver}"
response = requests.get(url, timeout=15)
if response.status_code == 200:
payload = response.json()
data = payload["data"]
print(f"Can transfer: {data['can_transfer']}")
print(f"Energy needed: {data['requirements']['energy_needed']}")
print(f"Bandwidth needed: {data['requirements']['bandwidth_needed']}")
print(f"Total cost (TRX): {data['costs']['total_cost_trx']}")
print(f"Recommended method: {data['costs']['recommended_method']}")
elif response.status_code == 429:
print("Rate-limited — retry after:", response.headers.get("Retry-After"), "s")
else:
print("Error:", response.json())Response
Success (200 OK)
Top-level envelope:
{
"status": "success",
"data": { /* TransferAnalysis — see below */ },
"current_utc_time": "2026-04-23 11:54:13",
"processing_time_ms": 19.27
}data (TransferAnalysis)
| Field | Type | Description |
|---|---|---|
sender | AddressInfo | Full account info for the sender (balance, staking, delegation, activation). |
receiver | AddressInfo | Full account info for the receiver. |
requirements | Requirements | Energy / bandwidth the transfer will need (raw + with safety buffer). |
costs | Costs | Burn vs. rent cost breakdown and the recommended method. |
can_transfer | boolean | true if the transfer can be executed with current resources/prices. |
issues | string[] | Concerns detected during analysis (e.g. insufficient bandwidth). |
recommendations | string[] | Human-readable suggestions for the client. |
variation_id | string | null | Matched scenario ID (e.g. "CUSTOM") from the internal variations catalogue. |
AddressInfo
Typical fields you will use in integrations: address, is_activated, trx_balance, usdt_balance, has_usdt, energy_balance, bandwidth_balance. Additional low-level fields for advanced use: trx_balance_sun, energy_total, bandwidth_total, bandwidth_free, bandwidth_staked, energy_used, bandwidth_used, create_time, latest_operation_time, staked_for_energy, staked_for_bandwidth, delegated_for_energy, delegated_for_bandwidth, delegated_out_energy, delegated_out_bandwidth, votes.
Requirements
| Field | Type | Description |
|---|---|---|
energy_needed | int | Raw energy units required for the transfer. |
bandwidth_needed | int | Raw bandwidth units required. |
energy_with_buffer | int | Energy rounded up to a safe rental tier (e.g. 131 000). |
bandwidth_with_buffer | int | Bandwidth with a small safety buffer. |
receiver_has_usdt | boolean | Whether the receiver already holds USDT (affects energy). |
Costs
| Field | Type | Description |
|---|---|---|
energy_burn_trx | decimal | TRX burned if energy is paid via direct burn. |
bandwidth_burn_trx | decimal | TRX burned to cover bandwidth if it is not available for free. |
total_burn_trx | decimal | energy_burn_trx + bandwidth_burn_trx. |
total_burn_sun | int | total_burn_trx expressed in SUN (10⁻⁶ TRX). |
energy_rental_trx | decimal | Cost to rent the required energy from Netts for the period below. |
energy_rental_sun | int | Same as above but in SUN. |
rental_time_period | string | e.g. "1h", "5m", or "not_needed" when rental is not the best path. |
rental_price_per_unit | int | Rental price per energy unit in SUN for the chosen period. |
savings_trx | decimal | How much cheaper rent is vs. burn (can be negative if burn is best). |
savings_percentage | float | Same as a percentage. |
recommended_method | string | "burn" or "rent" — the cheaper option for the current request. |
total_cost_trx | decimal | null | Actual cost if you follow recommended_method. |
sender_activation_cost | decimal | null | Extra cost if the sender account needs activation, else null. |
Example real response (abbreviated)
{
"status": "success",
"data": {
"sender": { "address": "TFLit1...", "is_activated": true, "trx_balance": 191.943, "usdt_balance": 24410.499, "energy_balance": 195297, "bandwidth_balance": 148, "has_usdt": true, "...": "..." },
"receiver": { "address": "TTKR9a...", "is_activated": true, "trx_balance": 18.656, "usdt_balance": 0.0, "energy_balance": 0, "bandwidth_balance": 263, "has_usdt": false, "...": "..." },
"requirements": {
"energy_needed": 130285,
"bandwidth_needed": 345,
"energy_with_buffer": 131000,
"bandwidth_with_buffer": 360,
"receiver_has_usdt": false
},
"costs": {
"energy_burn_trx": 0.0,
"bandwidth_burn_trx": 0.345,
"total_burn_trx": 0.345,
"total_burn_sun": 345000,
"energy_rental_trx": 0.0,
"energy_rental_sun": 0,
"rental_time_period": "not_needed",
"rental_price_per_unit": 0,
"savings_trx": 0.0,
"savings_percentage": 0.0,
"recommended_method": "burn",
"total_cost_trx": 0.345,
"sender_activation_cost": null
},
"can_transfer": true,
"issues": [
"Insufficient bandwidth: have 148, need 345. Network will burn 0.345 TRX for full amount"
],
"recommendations": [
"Insufficient bandwidth: have 148, need 345. Full amount of 0.345 TRX will be burned",
"💰 Total cost: 0.345 TRX (burn for all resources)"
],
"variation_id": "CUSTOM"
},
"current_utc_time": "2026-04-23 11:54:13",
"processing_time_ms": 19.27
}Errors
| HTTP | Body (example) | When |
|---|---|---|
| 400 | {"code": -1, "msg": "Invalid sender address format: Txyz..."} | Address fails TRON base58 / length / checksum validation. |
| 400 | {"code": -1, "msg": "Expected at least 2 parameters: sender&receiver"} | URL does not contain two addresses separated by &. |
| 400 | {"code": -1, "msg": "Sender and receiver cannot be the same address"} | Sender and receiver addresses are identical. |
| 429 | {"message": "API rate limit exceeded"} | Rate limit exceeded (see the warning at the top of this page). |
| 500 | {"code": -1, "msg": "Internal server error"} | Unexpected server-side failure. |
Rate-limit headers
On every response (including 429) the following headers are returned:
| Header | Meaning |
|---|---|
X-RateLimit-Limit-Second | Maximum requests allowed per second (currently 1). |
X-RateLimit-Remaining-Second | How many you may still send this second. |
X-RateLimit-Limit-Minute | Maximum requests allowed per minute (currently 60). |
X-RateLimit-Remaining-Minute | How many you may still send this minute. |
Retry-After | On a 429 — seconds to wait before retrying. |
Debug headers
Every response also carries identifiers useful when opening a support ticket — please include them verbatim so we can find the request in our logs within seconds:
| Header | Meaning |
|---|---|
X-Request-ID | Application-side request ID (generated by the calculator). |
X-Process-Time | Application processing time in milliseconds (upstream, excluding Kong). |
X-Kong-Request-Id | Kong-side request ID (present in Kong access logs). |
Client-side timeout and retry
The calculator performs live on-chain queries to TRON nodes for each request, so under load or slow upstream nodes a single call can take several seconds. Short client timeouts will fail even on healthy responses.
Recommended settings:
- Timeout ≥ 15 seconds (30 s is safer). The default 10 s used by many HTTP clients is too short.
- On HTTP 429, honour the
Retry-Afterheader (seconds). Add small jitter (e.g. 0–200 ms) before retrying, then use exponential backoff if you still hit the limit. - On HTTP 5xx or network errors, retry at most 2–3 times with exponential backoff; do not hammer the endpoint.
- Cache the result client-side for 30–60 seconds per
(sender, receiver)pair — the underlying resource prices and on-chain state rarely change fast enough to warrant more frequent recalculation.
Example 429 response
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
RateLimit-Limit: 1
RateLimit-Remaining: 0
RateLimit-Reset: 1
Retry-After: 1
X-RateLimit-Limit-Second: 1
X-RateLimit-Remaining-Second: 0
X-RateLimit-Limit-Minute: 60
X-RateLimit-Remaining-Minute: 0
{"message":"API rate limit exceeded"}Notes
- Anonymous access: no
X-API-KEY, noAuthorizationheader, no IP whitelist. - The response is always wrapped in
{status, data, current_utc_time, processing_time_ms}— integrations should read pricing fromdata.costsand requirements fromdata.requirements. - The response is computed in real time — it reflects the current TRON resource prices from Netts and the current on-chain state of both addresses, so expect small variations between consecutive calls.
- If your application needs to call the calculator more than a few times per minute (per-IP / per-CF-edge), switch to
POST /apiv2/usdt/analyzewith your API key.