Skip to content

POST /apiv2/bandwidth

Rent TRON bandwidth and delegate it to a recipient address for a fixed period (5 minutes or 1 hour).

⚠️ Access by request only. This endpoint is not enabled for all accounts. To obtain access (an API key and inclusion in the bandwidth rental whitelist), please contact Netts support. Requests from non-whitelisted users are rejected.

Endpoint URL

POST https://netts.io/apiv2/bandwidth

Request Headers

HeaderRequiredDescription
Content-TypeYesapplication/json
X-API-KEYYesYour API key from Netts dashboard
X-Real-IPYesIP address from your whitelist
X-Idempotency-KeyNoOptional client-generated key (base64) to safely retry without double-ordering. If omitted, the server generates one automatically

Request Body

json
{
    "amount": 1500,
    "receiveAddress": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "period": "5m"
}

Parameters

ParameterTypeRequiredDescription
amountintegerYesBandwidth units to rent (minimum: 400, maximum: 5000)
receiveAddressstringYesTRON address that will receive the bandwidth (T…, 34 chars, base58)
periodstringYesRental duration: "5m" (5 minutes) or "1h" (1 hour)
trx_sendbooleanNoGuaranteed transaction: if no bandwidth is available, send TRX to the address instead so the transaction still goes through. Works only when amount = 400 (ignored otherwise). Default false
checkbooleanNoIf true and the recipient already has more than 400 bandwidth, the order is not delegated and no funds are charged (status enough). Default false
testbooleanNoDry run. If true, the full order flow is simulated — the response tells you the outcome that would occur and the price that would be charged — without any on-chain action and without charging. Default false

Example Requests

The examples below also build and send the X-Idempotency-Key so an accidental repeat does not create a second order. See Idempotency for the full rules.

cURL

bash
API_KEY="your_api_key"
ADDR="TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
AMOUNT=1500
PERIOD="5m"
NONCE=$(( $(date +%s) / 2 ))   # stable for retries within a 2s window; or your own order UUID

# X-Idempotency-Key = base64( HMAC-SHA256( API_KEY, "addr:amount:period:nonce" ) )
IDEMP=$(printf '%s' "${ADDR}:${AMOUNT}:${PERIOD}:${NONCE}" \
  | openssl dgst -sha256 -hmac "$API_KEY" -binary | base64)

curl -X POST https://netts.io/apiv2/bandwidth \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: $API_KEY" \
  -H "X-Real-IP: your_whitelisted_ip" \
  -H "X-Idempotency-Key: $IDEMP" \
  -d "{\"amount\": $AMOUNT, \"receiveAddress\": \"$ADDR\", \"period\": \"$PERIOD\"}"

Python

python
import time, hmac, hashlib, base64, requests

API_KEY = "your_api_key"
url = "https://netts.io/apiv2/bandwidth"
payload = {
    "amount": 1500,
    "receiveAddress": "TXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "period": "5m",
}

# X-Idempotency-Key = base64( HMAC-SHA256( API_KEY, "addr:amount:period:nonce" ) )
# Generate ONCE per order and resend the same value on every retry.
nonce = str(int(time.time() // 2))   # 2s bucket; or your own order UUID
message = f"{payload['receiveAddress']}:{payload['amount']}:{payload['period']}:{nonce}"
idem_key = base64.b64encode(
    hmac.new(API_KEY.encode(), message.encode(), hashlib.sha256).digest()
).decode()

headers = {
    "Content-Type": "application/json",
    "X-API-KEY": API_KEY,
    "X-Real-IP": "your_whitelisted_ip",
    "X-Idempotency-Key": idem_key,
}

response = requests.post(url, headers=headers, json=payload)
data = response.json()
detail = data.get("detail", {})

if response.status_code == 200 and detail.get("status") == "completed":
    d = detail["data"]
    print(f"Order ID: {d['orderId']}")
    print(f"Hashes:   {d['hash']}")          # array of delegation tx hashes
    print(f"Bandwidth: {d['bandwidth']} for {d['period']}")
    print(f"Cost:     {d['paidTRX']} TRX")
else:
    print(f"Code: {detail.get('code')} | {detail.get('msg', detail)}")

A full client example (Python + cURL) is provided with the service package (handler_bandwidth/doc/client_example/).

Response

Success — bandwidth delegated (200 OK)

json
{
    "detail": {
        "code": 10000,
        "status": "completed",
        "msg": "Successful",
        "data": {
            "orderId": "B5M<key14>",
            "paidTRX": "<amount charged in TRX>",
            "fulfilledBy": "bandwidth",
            "hash": ["a1b2c3...", "d4e5f6..."],
            "bandwidth": 1500,
            "period": "5m"
        }
    }
}

Success — TRX sent instead of bandwidth (200 OK, only amount=400 + trx_send=true)

When the pool has no bandwidth and trx_send is enabled, TRX is sent to the address so the transaction still passes. A fixed charge applies in this case, regardless of the requested period.

json
{
    "detail": {
        "code": 10000,
        "status": "completed",
        "msg": "Successful (sent TRX, bandwidth unavailable)",
        "data": {
            "orderId": "B5M<key14>",
            "paidTRX": "<amount charged in TRX>",
            "fulfilledBy": "trx",
            "trxSendHash": ["<txid>"],
            "hash": [],
            "bandwidth": 400,
            "period": "5m"
        }
    }
}

Already enough — not charged (200 OK, only with check=true)

json
{
    "detail": {
        "code": 10002,
        "status": "enough",
        "msg": "enough band for 1 transfer",
        "data": { "orderId": "B5M<key14>", "paidTRX": 0, "bandwidth": 400, "period": "5m" }
    }
}

Processing — external provider (202 Accepted)

Returned when the order is handed to an external provider asynchronously. Poll the status endpoint (below) using orderId until it completes.

json
{
    "detail": {
        "code": 10001,
        "status": "processing",
        "msg": "Order accepted, processed by an external provider. Poll the status endpoint.",
        "data": { "orderId": "B5M<key14>", "bandwidth": 1500, "period": "5m" }
    }
}

Test run (200 OK, only with test=true)

The whole order flow is simulated. testAction tells you what would happen and wouldCostTRX what would be charged. Nothing is delegated, no TRX is sent, nothing is charged (paidTRX: 0).

json
{
    "detail": {
        "code": 10003,
        "status": "test",
        "msg": "Test run — no on-chain action, no charge",
        "data": {
            "orderId": "B5M<...>",
            "testAction": "would_delegate",
            "wouldCostTRX": "<amount that would be charged in TRX>",
            "paidTRX": 0,
            "bandwidth": 400,
            "period": "5m",
            "receiverFreeBandwidth": 600
        }
    }
}

testAction values: would_delegate (bandwidth would be delegated), would_trx_send (no bandwidth, amount=400 + trx_send → TRX would be sent), enough (recipient already has enough, with check=true), or would_error:<reason> (e.g. no_bandwidth, not_whitelisted).

Response Fields

FieldTypeDescription
detail.codeinteger10000 delegated/TRX, 10002 enough, 10001 processing
detail.statusstringcompleted / enough / processing / failed
detail.data.orderIdstringOrder ID, format B5M… (5m) / B1H… (1h) — use it for the status endpoint
detail.data.paidTRXnumberAmount charged in TRX (0 when enough)
detail.data.fulfilledBystringbandwidth (delegated) / trx (TRX sent)
detail.data.hasharrayDelegation transaction hashes (up to 10). Always an array (empty for the TRX branch)
detail.data.trxSendHasharrayTRX transfer hash(es), present only when fulfilledBy = trx
detail.data.bandwidthintegerBandwidth units delegated
detail.data.periodstringRental period (5m / 1h)

Status Endpoint

GET https://netts.io/apiv2/bandwidth/status/{orderId}

Headers: X-API-KEY + X-Real-IP (the order must belong to the authenticated user).

Order stateHTTPcodestatus
Completed20010000completed (with hash / trxSendHash)
In progress20010001processing
Already enough20010002enough
Failed2005003failed
Not found / not yours404-1

Reclaim Endpoint

Voluntarily reclaim (undelegate) the bandwidth of one of your delegated orders before its period expires. The bandwidth is undelegated automatically and the transaction hash is returned.

POST https://netts.io/apiv2/bandwidth/reclaim/{orderId}

Headers: X-API-KEY + X-Real-IP (the order must belong to the authenticated user).

Order stateHTTPcodestatusResult
Delegated → reclaimed now20010004reclaimedreclaimHash (undelegate tx hashes)
Already reclaimed20010004reclaimedreclaimHash + msg "already reclaimed"
Not in a delegated state (nothing to reclaim)4005005failed
Reclaim did not complete yet5035003failedretry shortly
Not found / not yours404-1
bash
curl -X POST https://netts.io/apiv2/bandwidth/reclaim/B5M<...> \
  -H "X-API-KEY: your_api_key" -H "X-Real-IP: your_whitelisted_ip"
json
{
    "detail": {
        "code": 10004,
        "status": "reclaimed",
        "msg": "Bandwidth reclaimed",
        "data": { "orderId": "B5M<...>", "reclaimHash": ["<txid>"] }
    }
}
python
import requests

order_id = "B5M..."   # the orderId from your rental response
url = f"https://netts.io/apiv2/bandwidth/reclaim/{order_id}"
headers = {"X-API-KEY": "your_api_key", "X-Real-IP": "your_whitelisted_ip"}

resp = requests.post(url, headers=headers)
detail = resp.json()["detail"]

if resp.status_code == 200 and detail["status"] == "reclaimed":
    print(f"Reclaimed: {detail['data']['reclaimHash']} ({detail['msg']})")
else:
    print(f"Code {detail.get('code')}: {detail.get('msg', detail)}")

The rental charge is not refunded on a voluntary early reclaim — reclaiming only returns the delegated bandwidth to the pool ahead of the period.

Error Responses

Authentication Error (401)

json
{ "detail": { "code": -1, "msg": "Invalid API key or IP not in whitelist" } }

Insufficient Balance (403)

json
{ "detail": { "code": 1004, "status": "failed", "msg": "Insufficient funds" } }

Validation Error (400)

json
{ "detail": { "code": 5004, "status": "failed", "msg": "Bandwidth amount out of range (400..5000)" } }

Delegation Failed / Service Unavailable (503)

json
{ "detail": { "code": 5003, "status": "failed", "msg": "Bandwidth delegation failed" } }

Error Code Reference

CodeDescriptionHTTP Status
10000Success (delegated, or TRX sent)200
10000Success (cached response)208
10001Accepted, processing by external provider202
10002Recipient already has enough bandwidth (not charged)200
10003Test run — outcome + price preview, nothing charged (test=true)200
10004Bandwidth reclaimed (voluntary undelegate) — reclaimHash returned200
-Duplicate request still processing409
-1Invalid API key / IP not in whitelist401
1004Insufficient balance403
1005No payer address for user400
5004Invalid amount/period (validation)400
5005Nothing to reclaim (order is not in a delegated state)400
5003Bandwidth delegation failed / unavailable503
5000Internal server error500

Rate Limits

PeriodLimitDescription
1 second50 requestsMaximum 50 requests per second per IP

Rate Limit Exceeded (429)

json
{ "message": "API rate limit exceeded" }

Idempotency

Send the optional X-Idempotency-Key header so an accidental repeat does not create a second order — the original response is returned with HTTP 208. If you do not send the header, the server derives a key automatically from your request parameters within a short time window.

How to form the key

The key is base64( HMAC-SHA256( secret, message ) ) — a 44-character base64 string, where:

  • secret = your API key (X-API-KEY);
  • message = the fields joined with :receiveAddress:amount:period:nonce.

nonce is any value that is stable across retries of the same logical order but different between distinct orders — e.g. a UUID you keep for that order, or a coarse timestamp bucket. Generate the key once per order and resend the exact same value on every retry.

python
import hmac, hashlib, base64, time

def make_idempotency_key(api_key, receive_address, amount, period, nonce=None):
    if nonce is None:
        nonce = str(int(time.time() // 2))   # 2-second bucket; or your own order UUID
    message = f"{receive_address}:{amount}:{period}:{nonce}"
    digest = hmac.new(api_key.encode(), message.encode(), hashlib.sha256).digest()
    return base64.b64encode(digest).decode()  # 44-char base64
bash
# then send it as a header:
-H "X-Idempotency-Key: <base64_key>"

Including period in the message is important: renting the same address for 5m and for 1h are different orders and must produce different keys.

Validation. A supplied X-Idempotency-Key must be a base64 string of 16–64 characters (charset A–Z a–z 0–9 + / = _ -). A malformed or over-long key is rejected with HTTP 400 (code 5004).

Status CodeMeaning
200Processed successfully (first request)
208Already processed successfully — cached response returned (no second charge)
409The same request is currently being processed — wait, do not retry yet

Retrying after a failure. Only successful results (completed / enough) are cached. If the previous attempt failed or timed out (no funds were charged), you may safely retry with the same X-Idempotency-Key — the order is attempted again rather than returning the old error. While an attempt is still in progress you get 409; wait and retry.

Notes

  • Access is granted by request only — contact Netts support to be whitelisted.
  • Minimum: 400 units. Maximum: 5000 units per order (current configuration).
  • Periods: 5m (300 s) and 1h (3600 s). The bandwidth is automatically reclaimed when the period expires.
  • No buffer: exactly the requested amount is delegated.
  • hash is an array: a single order may produce up to 10 delegation hashes — all are returned.
  • Pricing: charged in TRX, based on the requested amount and period; rates may vary by time of day. Contact support for current pricing.
  • Small-order compensation (delegation): for orders under 1000 units, a fixed 0.372 TRX is added to the price as compensation for the on-chain delegation and reclaim. Orders of 1000 units or more have no such addition.
  • TRX-send compensation: when the order is fulfilled by sending TRX (fulfilledBy = trx), a fixed 0.268 TRX is added instead (compensation for the on-chain TRX transfer).
  • trx_send: only for amount = 400; if no bandwidth is available, TRX is sent to the address so the transaction still passes.
  • check: skips delegation (and charging) when the recipient already has more than 400 bandwidth.
  • Order ID format: B5M… (5 minutes) / B1H… (1 hour).
  • Response timeout: up to ~12 seconds while waiting for delegation; typically 1–2 seconds.