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 tiers.

  • Accredited accounts rent any amount (up to 5000) within the pool size and maximum limits, with multiple concurrent orders. Accreditation is granted by Netts support.
  • Without accreditation you can rent 400 units once — the next order is allowed only after the previous rental ends. Requests for amounts other than 400, or a second order while the first is still active, 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
5007Without accreditation — only one rental at a time; previous order still active (wait until it ends)503
5008Without accreditation — only 400-unit orders allowed; accreditation required for larger amounts503
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.

Postpaid Accounts ​

Accounts on postpaid billing use the same endpoint, URL, headers and request body as everyone else. The difference is how the order is paid:

  • Nothing is deducted from your deposit. Each delivered order is added to your account balance due in the settlement currency agreed in your contract. You pay it by invoice, according to your agreement.
  • Your account has a credit limit. While the amount due is within the limit, orders are accepted even with a zero deposit. Rentals for 5 minutes and for 1 hour count against the same bandwidth limit.
  • Only delivered services are billed: bandwidth delegated to the address, or TRX sent instead (fulfilledBy = trx). The amount is the same as for prepaid orders.
  • An order that is rejected, not executed, a test run (test: true) or answered with enough (check: true) costs nothing.
  • A voluntary early reclaim does not reduce the amount due, the same as for prepaid orders.
  • Access tiers, amounts, periods and all other rules of this endpoint are the same as for prepaid accounts.

Postpaid billing is enabled by Netts for an account on request. Every response tells you how the order is paid: paymentMode is postpaid or prepaid.

Price check (test: true) ​

json
{
    "amount": 1000,
    "receiveAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
    "period": "5m",
    "test": true
}
json
{
    "detail": {
        "code": 10003,
        "status": "test",
        "msg": "Test run — no on-chain action, no charge",
        "data": {
            "orderId": "B5M1a2b3c4d5e6f70",
            "testAction": "would_delegate",
            "wouldCostTRX": 0.7,
            "paidTRX": 0,
            "bandwidth": 1000,
            "period": "5m",
            "receiverFreeBandwidth": 600,
            "paymentMode": "postpaid"
        }
    }
}

wouldCostTRX is the amount that would be added to your balance due, in TRX. Nothing is delegated or billed.

Successful order ​

json
{
    "detail": {
        "code": 10000,
        "status": "completed",
        "msg": "Successful, 0.700000 TRX, postpaid",
        "data": {
            "orderId": "B5M0f9e8d7c6b5a41",
            "paidTRX": 0.7,
            "fulfilledBy": "bandwidth",
            "hash": ["<txid>"],
            "bandwidth": 1000,
            "period": "5m",
            "paymentMode": "postpaid",
            "totalTRX": "0.700000"
        }
    }
}
FieldDescription
paidTRXCost of the service in TRX. For postpaid accounts it is not deducted from the deposit
paymentModepostpaid
totalTRXExact amount, 6 decimal places

All other fields, including the TRX branch (fulfilledBy = trx, trxSendHash), are the same as for prepaid orders.

Credit limit reached (402) ​

json
{
    "detail": {
        "code": 1004,
        "msg": "Postpaid credit limit reached.",
        "paymentMode": "postpaid",
        "reason": "limit_reached"
    }
}

The order is not executed and nothing is billed. New orders are accepted again once your payment is registered or your limit is increased.

Postpaid error codes ​

CodeHTTPDescription
1004402Postpaid credit limit reached
5032503Price temporarily unavailable
5034503Billing temporarily unavailable — the order was not accepted
5036503Credit check temporarily unavailable — retry later

For all responses above nothing is ordered and nothing is billed. All other codes are the same as for prepaid orders.

Notes ​

  • Access tiers: accredited accounts rent any amount within pool/maximum limits with concurrent orders; without accreditation — 400 units once (next order only after the previous rental ends). Contact Netts support for accreditation.
  • 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.