Skip to content

API for developers / Reference

SunLend Energy API

Version 1 · REST · JSON

Buy TRON energy and bandwidth from your account balance: orders, address activation, automations and webhooks. Examples in cURL, JavaScript and Python.

Introduction

The SunLend Energy API buys TRON energy and bandwidth from your account balance. The resource is delegated to the address you pass for 15 minutes, 1 hour or 1 day; the price is charged to the balance.

  • JSON in UTF-8; responses are never cached.
  • All money is in sun: 1 TRX = 1,000,000 sun, always integers.
  • A successful response is in data, an error is in error.
  • A machine-readable contract is available as OpenAPI 3.1 — generate a client for any language from it.
Base URL
https://sunlend.io/api/v1/energy
Response200
{
  "data": {
    "balanceSun": 25000000,
    "depositAddress": {
      "network": "TRC20",
      "address": "TXyz…"
    }
  }
}

Authentication

  • Sign up and top up the balance in your account (the “Wallet” section).
  • In your account open “API” and issue a key. The full key is shown once — save it. You can have up to 5 active keys.
  • Send the key in Authorization: Bearer slk_… with every request.
The key can spend your balance. Keep it on the server only — never in a browser or a mobile app.

Rate limit: 60 requests per minute per key; above it — 429 rate_limited.

Scopes are nested:

ScopeAllows
readbalance, prices, orders, rules and webhooks — read only
purchaseplus buying (POST /orders) and activation (POST /activations)
fullplus automations and webhooks: create, pause, delete

A key can have a daily spending limit in TRX (UTC day): a purchase above it gets 403 daily_limit and the balance is untouched. Scope and limit can be changed in your account without reissuing the key.

Authenticated request
curl 'https://sunlend.io/api/v1/energy/account' \
  -H "Authorization: Bearer $SUNLEND_KEY"

Pricing

The energy price floats during the day together with the supplier price. Every energy order of any volume carries a fixed fee of 0.4 TRX.

Energy order total: volume × sunPerUnit + feeSun. Bandwidth: volume / 1000 × trxPer1k. Get the exact price before buying with GET /quote; the price at the moment of the order is charged.

USDT TRC-20 transferEnergy needed
to an address that already holds USDT~65 000
to an address with no USDT~131 000

Energy not used within the rental term is not refunded. Energy does not work on exchange deposit addresses.

65,000 energy for 1 hour
curl 'https://sunlend.io/api/v1/energy/quote?volume=65000&term=1h' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": {
    "resource": "energy",
    "volume": 65000,
    "term": "1h",
    "priceSun": 2090000,
    "sunPerUnit": 26,
    "feeSun": 400000,
    "trxPer1k": null,
    "balanceSun": 25000000
  }
}

Idempotency

Send an Idempotency-Key header with POST /orders — a unique order id on your side, for example a payout id. 1–100 characters: Latin letters, digits and _ . : -.

A retry with the same key never creates a second order or charges again — it returns the existing order (200 instead of 201). If the connection dropped before the answer, just send the same request again.

The key is bound to the order forever: a retry returns a failed order too. After a failure, retry with a new key.
Order with an idempotency key
curl -X POST 'https://sunlend.io/api/v1/energy/orders' \
  -H "Authorization: Bearer $SUNLEND_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: payout-000123' \
  -d '{"address":"TXyz…","volume":65000,"term":"1h"}'

Balance and top-up address

GET/accountKey scope: read

Account balance and the permanent top-up address on TRON. It accepts USDT TRC-20 and TRX; USDT is converted to TRX at the rate when credited.

Send only USDT TRC-20 and TRX, and only on TRON. If processing is temporarily unavailable, depositAddress is null.
Responses
200

Balance and address

GET /account
curl 'https://sunlend.io/api/v1/energy/account' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": {
    "balanceSun": 25000000,
    "depositAddress": {
      "network": "TRC20",
      "address": "TXyz…"
    }
  }
}

Order price

GET/quoteKey scope: read

Order price at the current rate, before buying. For energy priceSun = volume × sunPerUnit + feeSun.

Query parameters
resource"energy" | "bandwidth"

Defaults to energy

volumeintegerrequired

Volume: energy 65,000–5,000,000, bandwidth 1,000–300,000

term"15m" | "1h" | "1d"required

Rental term

Responses
200

Price

422

invalid_volume, invalid_term, invalid_resource

GET /quote
curl 'https://sunlend.io/api/v1/energy/quote?volume=65000&term=1h' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": {
    "resource": "energy",
    "volume": 65000,
    "term": "1h",
    "priceSun": 2090000,
    "sunPerUnit": 26,
    "feeSun": 400000,
    "trxPer1k": null,
    "balanceSun": 25000000
  }
}

Buy energy or bandwidth

POST/ordersKey scope: purchase

Charges the balance and delegates the resource to the address. Orders usually complete in seconds and come back as filled with the on-chain txId.

For longer than a day use auto-renewal: POST /automations with kind: "renewal".

Headers
Idempotency-Keystring

Recommended. A retry with the same key returns the same order without a second charge — see Idempotency

Request body
addressstringrequired

TRON address that receives the resource (T…, 34 characters)

volumeintegerrequired

Volume: energy 65,000–5,000,000, bandwidth 1,000–300,000

term"15m" | "1h" | "1d"required

Rental term

resource"energy" | "bandwidth"

Defaults to energy

Responses
201

Order created

200

Replay with the same Idempotency-Key — the existing order

402

insufficient_funds

409

idempotency_conflict

422

Validation error

502

provider_failed — the money is refunded

504

provider_unknown — the order is pending, do not create a new one

POST /orders
curl -X POST 'https://sunlend.io/api/v1/energy/orders' \
  -H "Authorization: Bearer $SUNLEND_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: payout-000123' \
  -d '{"address":"TXyz…","volume":65000,"term":"1h"}'
Response201
{
  "data": {
    "order": {
      "id": 1042,
      "apiKeyId": 3,
      "resource": "energy",
      "address": "TXyz…",
      "volume": 65000,
      "term": "1h",
      "priceSun": 2090000,
      "status": "filled",
      "txId": "a1b2c3…",
      "createdAt": "2026-10-25T12:00:00.000Z"
    },
    "balanceSun": 22910000
  }
}

Order statuses

statusMeaning
filledThe resource is delegated, txId is the TRON transaction
pendingThe supplier did not confirm in time. The order is checked automatically for up to 30 minutes and becomes filled or failed with a refund
failedThe order failed and the money is back on the balance
Do not replace a pending order with a new one. Poll GET /orders/{id} every 30–60 seconds or subscribe to the order.filled and order.failed webhooks.

List orders

GET/ordersKey scope: read

Account orders, newest first, paginated.

Query parameters
limitinteger

1 to 100, default 20

offsetinteger

How many orders to skip, default 0

Responses
200

Orders and pagination

400

invalid_query

GET /orders
curl 'https://sunlend.io/api/v1/energy/orders?limit=20&offset=0' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": [
    {
      "id": 1042,
      "apiKeyId": 3,
      "resource": "energy",
      "address": "TXyz…",
      "volume": 65000,
      "term": "1h",
      "priceSun": 2090000,
      "status": "filled",
      "txId": "a1b2c3…",
      "createdAt": "2026-10-25T12:00:00.000Z"
    }
  ],
  "pagination": {
    "total": 57,
    "limit": 20,
    "offset": 0
  }
}

Get an order

GET/orders/{id}Key scope: read

An order by ID — use it to wait for a pending order to complete.

Path parameters
idintegerrequired

Order ID

Responses
200

Order

404

not_found

GET /orders/{id}
curl 'https://sunlend.io/api/v1/energy/orders/1042' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": {
    "id": 1042,
    "apiKeyId": 3,
    "resource": "energy",
    "address": "TXyz…",
    "volume": 65000,
    "term": "1h",
    "priceSun": 2090000,
    "status": "filled",
    "txId": "a1b2c3…",
    "createdAt": "2026-10-25T12:00:00.000Z"
  }
}

Activation price

GET/activationsKey scope: read

A new TRON address (one that has never received anything) must be activated before a resource can be delegated to it. This returns the activation price.

Responses
200

Price and balance

GET /activations
curl 'https://sunlend.io/api/v1/energy/activations' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": {
    "priceSun": 1500000,
    "balanceSun": 25000000
  }
}

Activate an address

POST/activationsKey scope: purchase

Activates a new address and charges the activation price. An already active address returns 409 already_active and nothing is charged.

status: done — activated; pending — the answer is delayed, checking continues automatically; failed — the money is refunded.

Request body
addressstringrequired

TRON address to activate

Responses
201

Activation created

402

insufficient_funds

409

already_active, already_requested

POST /activations
curl -X POST 'https://sunlend.io/api/v1/energy/activations' \
  -H "Authorization: Bearer $SUNLEND_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"address":"TNew…"}'
Response201
{
  "data": {
    "activation": {
      "id": 7,
      "address": "TNew…",
      "priceSun": 1500000,
      "status": "done",
      "createdAt": "2026-10-25T12:00:00.000Z"
    },
    "balanceSun": 23500000
  }
}

How automations work

A rule watches the address and delivers energy on its own. Each delivery is charged to the balance at the current price — once per delivery, with the same per-order fee.

kindWhat it does
refillBuys volume when energy on the address drops below minimal
renewalRenews volume of energy once a day
smartTron Charger: the address is fueled before every transfer

When the balance cannot cover the next delivery, the rule is paused (status: "paused", pauseReason: "system") and resumes by itself after a top-up. A manual pause (pauseReason: "user") stays until you resume.

List rules

GET/automationsKey scope: read

All rules with the price of one delivery at the current rate (firePriceSun).

Responses
200

Rules

GET /automations
curl 'https://sunlend.io/api/v1/energy/automations' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": [
    {
      "id": 12,
      "kind": "refill",
      "address": "TXyz…",
      "volume": 65000,
      "minimal": 20000,
      "duration": "1h",
      "status": "active",
      "pauseReason": null,
      "autoResume": true,
      "lastFireAt": null,
      "createdAt": "2026-10-25T12:00:00.000Z",
      "firePriceSun": 2090000
    }
  ]
}

Create a rule

POST/automationsKey scope: full

The balance must cover at least one delivery, otherwise 402. An address can have one rule of each kind.

Request body
kind"refill" | "renewal" | "smart"required

Rule kind

addressstringrequired

TRON address

volumeinteger

Volume per delivery, 65,000–5,000,000. Not used for smart

minimalinteger

refill only: threshold, at least 1,000 and below volume

duration"1h" | "1d"

refill only: term of each delivery

autoResumeboolean

Resume after a top-up, default true

Responses
201

Rule created

402

insufficient_funds

409

already_exists

422

Validation error

POST /automations
curl -X POST 'https://sunlend.io/api/v1/energy/automations' \
  -H "Authorization: Bearer $SUNLEND_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"kind":"refill","address":"TXyz…","volume":65000,"minimal":20000,"duration":"1h"}'
Response201
{
  "data": {
    "id": 12,
    "kind": "refill",
    "address": "TXyz…",
    "volume": 65000,
    "minimal": 20000,
    "duration": "1h",
    "status": "active",
    "pauseReason": null,
    "autoResume": true,
    "lastFireAt": null,
    "createdAt": "2026-10-25T12:00:00.000Z",
    "firePriceSun": 2090000
  }
}

Get a rule

GET/automations/{id}Key scope: read

A rule by ID.

Path parameters
idintegerrequired

Rule ID

Responses
200

Rule

404

not_found

GET /automations/{id}
curl 'https://sunlend.io/api/v1/energy/automations/12' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": {
    "id": 12,
    "kind": "refill",
    "address": "TXyz…",
    "volume": 65000,
    "minimal": 20000,
    "duration": "1h",
    "status": "active",
    "pauseReason": null,
    "autoResume": true,
    "lastFireAt": null,
    "createdAt": "2026-10-25T12:00:00.000Z",
    "firePriceSun": 2090000
  }
}

Pause and resume

PATCH/automations/{id}Key scope: full

Pause, resume or change autoResume. Resuming requires enough balance for one delivery.

Path parameters
idintegerrequired

Rule ID

Request body
action"pause" | "resume"

Pause or resume

autoResumeboolean

Whether to resume after a top-up

Responses
200

Rule

402

insufficient_funds on resume

404

not_found

PATCH /automations/{id}
curl -X PATCH 'https://sunlend.io/api/v1/energy/automations/12' \
  -H "Authorization: Bearer $SUNLEND_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"action":"pause"}'
Response200
{
  "data": {
    "id": 12,
    "kind": "refill",
    "address": "TXyz…",
    "volume": 65000,
    "minimal": 20000,
    "duration": "1h",
    "status": "paused",
    "pauseReason": "user",
    "autoResume": true,
    "lastFireAt": null,
    "createdAt": "2026-10-25T12:00:00.000Z",
    "firePriceSun": 2090000
  }
}

Delete a rule

DELETE/automations/{id}Key scope: full

Deletes the rule — energy stops arriving at the address.

Path parameters
idintegerrequired

Rule ID

Responses
204

Deleted

404

not_found

DELETE /automations/{id}
curl -X DELETE 'https://sunlend.io/api/v1/energy/automations/12' \
  -H "Authorization: Bearer $SUNLEND_KEY"

Events and signature

We send a JSON POST to your https URL when an event happens:

EventWhen
order.filledAn order is filled
order.failedAn order failed and was refunded
deposit.creditedA top-up is credited
automation.firedA rule delivered energy
automation.pausedA rule was paused
automation.resumedA rule resumed
activation.doneAn address is activated
pingTest event — sent only on POST /webhooks/{id}/ping; you do not subscribe to it

Request headers: X-SunLend-Event, X-SunLend-Event-Id, X-SunLend-Timestamp (unix seconds) and X-SunLend-Signature: sha256=<hex> — HMAC-SHA256 of "{timestamp}.{body}" with the webhook secret. Verify it and reject requests older than 5 minutes.

A 2xx answer means delivered. Otherwise we retry after 1 min, 5 min, 30 min, 2 h, 6 h and 24 h; after 20 failures in a row the webhook is switched off. An event may arrive twice — deduplicate by id. Up to 5 webhooks per account.

Verifying the signature
import crypto from 'node:crypto';
import express from 'express';

const app = express();

// The signature covers the raw body — do not parse JSON before checking
app.post('/sunlend', express.raw({ type: 'application/json' }), (req, res) => {
  const ts = req.get('X-SunLend-Timestamp');
  const sig = (req.get('X-SunLend-Signature') || '').replace('sha256=', '');
  const expected = crypto
    .createHmac('sha256', process.env.SUNLEND_WEBHOOK_SECRET)
    .update(`${ts}.${req.body}`)
    .digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  if (!fresh || sig.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.sendStatus(400);
  }
  const event = JSON.parse(req.body);
  // An event may arrive twice — handle each event.id once
  res.sendStatus(200);
});
Event body
{
  "id": "evt_…",
  "type": "order.filled",
  "createdAt": "2026-10-25T12:00:00.000Z",
  "data": {
    "orderId": 1042,
    "resource": "energy",
    "address": "TXyz…",
    "volume": 65000,
    "term": "1h",
    "priceSun": 2090000,
    "txId": "a1b2c3…",
    "apiKeyId": 3
  }
}

List webhooks

GET/webhooksKey scope: read

Account webhooks with delivery stats.

Responses
200

Webhooks

GET /webhooks
curl 'https://sunlend.io/api/v1/energy/webhooks' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": [
    {
      "id": 3,
      "url": "https://example.com/sunlend",
      "events": [
        "order.filled",
        "order.failed"
      ],
      "isActive": true,
      "failures": 0,
      "lastSuccessAt": null,
      "lastFailureAt": null,
      "createdAt": "2026-10-25T12:00:00.000Z"
    }
  ]
}

Create a webhook

POST/webhooksKey scope: full

The response contains the signing secret. It is shown once; issue a new one with rotate.

Request body
urlstringrequired

https URL, not on a private network

eventsstring[]required

Events from the list above except ping

Responses
201

Webhook and secret

409

webhook_limit

422

invalid_url, invalid_events

POST /webhooks
curl -X POST 'https://sunlend.io/api/v1/energy/webhooks' \
  -H "Authorization: Bearer $SUNLEND_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/sunlend","events":["order.filled","order.failed"]}'
Response201
{
  "data": {
    "webhook": {
      "id": 3,
      "url": "https://example.com/sunlend",
      "events": [
        "order.filled",
        "order.failed"
      ],
      "isActive": true,
      "failures": 0,
      "lastSuccessAt": null,
      "lastFailureAt": null,
      "createdAt": "2026-10-25T12:00:00.000Z"
    },
    "secret": "whsec_…"
  }
}

Update a webhook

PATCH/webhooks/{id}Key scope: full

Changes the URL or events, or re-enables a switched-off webhook (isActive: true).

Path parameters
idintegerrequired

Webhook ID

Request body
urlstring

New URL

eventsstring[]

New event list

isActiveboolean

Enable or disable

Responses
200

Webhook

404

not_found

422

invalid_url, invalid_events

PATCH /webhooks/{id}
curl -X PATCH 'https://sunlend.io/api/v1/energy/webhooks/3' \
  -H "Authorization: Bearer $SUNLEND_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"isActive":true}'
Response200
{
  "data": {
    "id": 3,
    "url": "https://example.com/sunlend",
    "events": [
      "order.filled",
      "order.failed"
    ],
    "isActive": true,
    "failures": 0,
    "lastSuccessAt": null,
    "lastFailureAt": null,
    "createdAt": "2026-10-25T12:00:00.000Z"
  }
}

Delete a webhook

DELETE/webhooks/{id}Key scope: full

Deletes the webhook; no more events are sent.

Path parameters
idintegerrequired

Webhook ID

Responses
204

Deleted

404

not_found

DELETE /webhooks/{id}
curl -X DELETE 'https://sunlend.io/api/v1/energy/webhooks/3' \
  -H "Authorization: Bearer $SUNLEND_KEY"

Rotate the secret

POST/webhooks/{id}/rotateKey scope: full

Issues a new signing secret. The old one stops working immediately.

Path parameters
idintegerrequired

Webhook ID

Responses
200

New secret

404

not_found

POST /webhooks/{id}/rotate
curl -X POST 'https://sunlend.io/api/v1/energy/webhooks/3/rotate' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": {
    "secret": "whsec_…"
  }
}

Send a test event

POST/webhooks/{id}/pingKey scope: full

Queues a ping event — handy to test your endpoint and signature check.

Path parameters
idintegerrequired

Webhook ID

Responses
202

Queued

404

not_found

POST /webhooks/{id}/ping
curl -X POST 'https://sunlend.io/api/v1/energy/webhooks/3/ping' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response202
{
  "data": {
    "queued": true
  }
}

Delivery log

GET/webhooks/{id}/deliveriesKey scope: read

Recent deliveries, newest first: status, attempts, your server’s response code and error.

Path parameters
idintegerrequired

Webhook ID

Responses
200

Deliveries

404

not_found

GET /webhooks/{id}/deliveries
curl 'https://sunlend.io/api/v1/energy/webhooks/3/deliveries' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response200
{
  "data": [
    {
      "id": 88,
      "eventId": "evt_…",
      "event": "order.filled",
      "status": "ok",
      "attempts": 1,
      "lastStatus": 200,
      "lastError": null,
      "createdAt": "2026-10-25T12:00:00.000Z",
      "deliveredAt": "2026-10-25T12:00:01.000Z"
    }
  ]
}

Resend a delivery

POST/webhooks/{id}/deliveries/{deliveryId}Key scope: full

Sends a failed delivery again.

Path parameters
idintegerrequired

Webhook ID

deliveryIdintegerrequired

Delivery ID

Responses
202

Queued

404

not_found

POST /webhooks/{id}/deliveries/{deliveryId}
curl -X POST 'https://sunlend.io/api/v1/energy/webhooks/3/deliveries/88' \
  -H "Authorization: Bearer $SUNLEND_KEY"
Response202
{
  "data": {
    "queued": true
  }
}

Errors

Errors always have the same shape. Rely on code — message is for humans and may change.

HTTPcodeWhen
400invalid_jsonThe body is not valid JSON or not an object
400invalid_queryInvalid limit / offset
400invalid_idempotency_keyIdempotency-Key has the wrong format
401unauthorizedNo key, wrong or revoked key, or a blocked account
402insufficient_fundsNot enough funds on the balance
403insufficient_scopeThe key scope is too low for this method
403daily_limitThe purchase would exceed the key’s daily limit
404not_foundNot found or belongs to another account
409idempotency_conflictIdempotency-Key was already used for an order with other parameters
409already_existsThe address already has a rule of this kind
409already_activeThe address is already active — nothing charged
409already_requestedActivation of this address is already in progress
409webhook_limitWebhook limit reached (5)
409inactiveThe webhook is disabled — enable it (isActive: true) to send a test event
415unsupported_media_typeMissing Content-Type: application/json
422validation_failedUnknown body fields (listed in details)
422invalid_addressA TRON address starts with T and has 34 characters
422invalid_volumeVolume is out of range or not an integer
422invalid_resourceresource is neither energy nor bandwidth
422invalid_kindkind is not refill, renewal or smart
422invalid_termTerm is not 15m, 1h or 1d
422invalid_minimalAuto top-up threshold is out of range
422invalid_urlWebhook URL is not https or points to a private network
422invalid_eventsUnknown webhook event
429rate_limitedRate limit per key exceeded
502provider_failedThe supplier rejected the order and the money is back on the balance. Retry with a new Idempotency-Key
503provider_offPurchasing is temporarily unavailable
503provider_unavailableThe supplier is temporarily unavailable (automations), retry in a minute
503price_unavailablePrice is temporarily unavailable
503check_failedCould not check the address on-chain, try again later
504provider_unknownThe supplier did not answer in time: the order is pending. Do not create a new one — poll this one
Response402
{
  "error": {
    "code": "insufficient_funds",
    "message": "Not enough funds. Top up the balance."
  }
}

MCP for AI agents

The same API is available as an MCP server at https://sunlend.io/api/mcp (Streamable HTTP) — AI agents such as Claude or Cursor buy energy with your key.

Tools: get_balance, get_price, buy_energy, list_orders, get_order, activate_address, list_automations, create_automation, pause_automation, resume_automation, delete_automation. Each requires its own key scope.

Give the agent a purchase key with a daily limit — it cannot spend more than the limit or create rules and webhooks.
Connect
npx mcp-remote https://sunlend.io/api/mcp \
  --header "Authorization: Bearer $SUNLEND_KEY"

Common scenarios

Energy before every transfer. Before sending USDT buy 65,000 energy (the recipient holds USDT) or 131,000 (no USDT) for 15 minutes, wait for filled and send. For a steady flow of transfers a Tron Charger rule is simpler and cheaper.

Dropped connection. Send every order with an Idempotency-Key. No answer — repeat the same request with the same key: there will be no second charge.

Spending control. Give the payout server its own purchase key with a daily limit: even a leaked key cannot spend more than that.