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 inerror. - A machine-readable contract is available as OpenAPI 3.1 — generate a client for any language from it.
https://sunlend.io/api/v1/energy
{
"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.
Rate limit: 60 requests per minute per key; above it — 429 rate_limited.
Scopes are nested:
| Scope | Allows |
|---|---|
read | balance, prices, orders, rules and webhooks — read only |
purchase | plus buying (POST /orders) and activation (POST /activations) |
full | plus 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.
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 transfer | Energy 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.
curl 'https://sunlend.io/api/v1/energy/quote?volume=65000&term=1h' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"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.
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
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.
depositAddress is null.Responses
Balance and address
curl 'https://sunlend.io/api/v1/energy/account' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"data": {
"balanceSun": 25000000,
"depositAddress": {
"network": "TRC20",
"address": "TXyz…"
}
}
}Order price
Order price at the current rate, before buying. For energy priceSun = volume × sunPerUnit + feeSun.
Query parameters
resource"energy" | "bandwidth"Defaults to energy
volumeintegerrequiredVolume: energy 65,000–5,000,000, bandwidth 1,000–300,000
term"15m" | "1h" | "1d"requiredRental term
Responses
Price
invalid_volume, invalid_term, invalid_resource
curl 'https://sunlend.io/api/v1/energy/quote?volume=65000&term=1h' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"data": {
"resource": "energy",
"volume": 65000,
"term": "1h",
"priceSun": 2090000,
"sunPerUnit": 26,
"feeSun": 400000,
"trxPer1k": null,
"balanceSun": 25000000
}
}Buy energy or bandwidth
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-KeystringRecommended. A retry with the same key returns the same order without a second charge — see Idempotency
Request body
addressstringrequiredTRON address that receives the resource (T…, 34 characters)
volumeintegerrequiredVolume: energy 65,000–5,000,000, bandwidth 1,000–300,000
term"15m" | "1h" | "1d"requiredRental term
resource"energy" | "bandwidth"Defaults to energy
Responses
Order created
Replay with the same Idempotency-Key — the existing order
insufficient_funds
idempotency_conflict
Validation error
provider_failed — the money is refunded
provider_unknown — the order is pending, do not create a new one
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"}'
{
"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
| status | Meaning |
|---|---|
filled | The resource is delegated, txId is the TRON transaction |
pending | The supplier did not confirm in time. The order is checked automatically for up to 30 minutes and becomes filled or failed with a refund |
failed | The order failed and the money is back on the balance |
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
Account orders, newest first, paginated.
Query parameters
limitinteger1 to 100, default 20
offsetintegerHow many orders to skip, default 0
Responses
Orders and pagination
invalid_query
curl 'https://sunlend.io/api/v1/energy/orders?limit=20&offset=0' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"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
An order by ID — use it to wait for a pending order to complete.
Path parameters
idintegerrequiredOrder ID
Responses
Order
not_found
curl 'https://sunlend.io/api/v1/energy/orders/1042' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"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
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
Price and balance
curl 'https://sunlend.io/api/v1/energy/activations' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"data": {
"priceSun": 1500000,
"balanceSun": 25000000
}
}Activate an address
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
addressstringrequiredTRON address to activate
Responses
Activation created
insufficient_funds
already_active, already_requested
curl -X POST 'https://sunlend.io/api/v1/energy/activations' \ -H "Authorization: Bearer $SUNLEND_KEY" \ -H 'Content-Type: application/json' \ -d '{"address":"TNew…"}'
{
"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.
| kind | What it does |
|---|---|
refill | Buys volume when energy on the address drops below minimal |
renewal | Renews volume of energy once a day |
smart | Tron 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
All rules with the price of one delivery at the current rate (firePriceSun).
Responses
Rules
curl 'https://sunlend.io/api/v1/energy/automations' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"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
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"requiredRule kind
addressstringrequiredTRON address
volumeintegerVolume per delivery, 65,000–5,000,000. Not used for smart
minimalintegerrefill only: threshold, at least 1,000 and below volume
duration"1h" | "1d"refill only: term of each delivery
autoResumebooleanResume after a top-up, default true
Responses
Rule created
insufficient_funds
already_exists
Validation error
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"}'
{
"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
A rule by ID.
Path parameters
idintegerrequiredRule ID
Responses
Rule
not_found
curl 'https://sunlend.io/api/v1/energy/automations/12' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"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
Pause, resume or change autoResume. Resuming requires enough balance for one delivery.
Path parameters
idintegerrequiredRule ID
Request body
action"pause" | "resume"Pause or resume
autoResumebooleanWhether to resume after a top-up
Responses
Rule
insufficient_funds on resume
not_found
curl -X PATCH 'https://sunlend.io/api/v1/energy/automations/12' \ -H "Authorization: Bearer $SUNLEND_KEY" \ -H 'Content-Type: application/json' \ -d '{"action":"pause"}'
{
"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
Deletes the rule — energy stops arriving at the address.
Path parameters
idintegerrequiredRule ID
Responses
Deleted
not_found
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:
| Event | When |
|---|---|
order.filled | An order is filled |
order.failed | An order failed and was refunded |
deposit.credited | A top-up is credited |
automation.fired | A rule delivered energy |
automation.paused | A rule was paused |
automation.resumed | A rule resumed |
activation.done | An address is activated |
ping | Test 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.
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); });
{
"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
}
}curl 'https://sunlend.io/api/v1/energy/webhooks' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"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
The response contains the signing secret. It is shown once; issue a new one with rotate.
Request body
urlstringrequiredhttps URL, not on a private network
eventsstring[]requiredEvents from the list above except ping
Responses
Webhook and secret
webhook_limit
invalid_url, invalid_events
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"]}'
{
"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
Changes the URL or events, or re-enables a switched-off webhook (isActive: true).
Path parameters
idintegerrequiredWebhook ID
Request body
urlstringNew URL
eventsstring[]New event list
isActivebooleanEnable or disable
Responses
Webhook
not_found
invalid_url, invalid_events
curl -X PATCH 'https://sunlend.io/api/v1/energy/webhooks/3' \ -H "Authorization: Bearer $SUNLEND_KEY" \ -H 'Content-Type: application/json' \ -d '{"isActive":true}'
{
"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
Deletes the webhook; no more events are sent.
Path parameters
idintegerrequiredWebhook ID
Responses
Deleted
not_found
curl -X DELETE 'https://sunlend.io/api/v1/energy/webhooks/3' \ -H "Authorization: Bearer $SUNLEND_KEY"
Rotate the secret
Issues a new signing secret. The old one stops working immediately.
Path parameters
idintegerrequiredWebhook ID
Responses
New secret
not_found
curl -X POST 'https://sunlend.io/api/v1/energy/webhooks/3/rotate' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"data": {
"secret": "whsec_…"
}
}Send a test event
Queues a ping event — handy to test your endpoint and signature check.
Path parameters
idintegerrequiredWebhook ID
Responses
Queued
not_found
curl -X POST 'https://sunlend.io/api/v1/energy/webhooks/3/ping' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"data": {
"queued": true
}
}Delivery log
Recent deliveries, newest first: status, attempts, your server’s response code and error.
Path parameters
idintegerrequiredWebhook ID
Responses
Deliveries
not_found
curl 'https://sunlend.io/api/v1/energy/webhooks/3/deliveries' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"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
Sends a failed delivery again.
Path parameters
idintegerrequiredWebhook ID
deliveryIdintegerrequiredDelivery ID
Responses
Queued
not_found
curl -X POST 'https://sunlend.io/api/v1/energy/webhooks/3/deliveries/88' \ -H "Authorization: Bearer $SUNLEND_KEY"
{
"data": {
"queued": true
}
}Errors
Errors always have the same shape. Rely on code — message is for humans and may change.
| HTTP | code | When |
|---|---|---|
| 400 | invalid_json | The body is not valid JSON or not an object |
| 400 | invalid_query | Invalid limit / offset |
| 400 | invalid_idempotency_key | Idempotency-Key has the wrong format |
| 401 | unauthorized | No key, wrong or revoked key, or a blocked account |
| 402 | insufficient_funds | Not enough funds on the balance |
| 403 | insufficient_scope | The key scope is too low for this method |
| 403 | daily_limit | The purchase would exceed the key’s daily limit |
| 404 | not_found | Not found or belongs to another account |
| 409 | idempotency_conflict | Idempotency-Key was already used for an order with other parameters |
| 409 | already_exists | The address already has a rule of this kind |
| 409 | already_active | The address is already active — nothing charged |
| 409 | already_requested | Activation of this address is already in progress |
| 409 | webhook_limit | Webhook limit reached (5) |
| 409 | inactive | The webhook is disabled — enable it (isActive: true) to send a test event |
| 415 | unsupported_media_type | Missing Content-Type: application/json |
| 422 | validation_failed | Unknown body fields (listed in details) |
| 422 | invalid_address | A TRON address starts with T and has 34 characters |
| 422 | invalid_volume | Volume is out of range or not an integer |
| 422 | invalid_resource | resource is neither energy nor bandwidth |
| 422 | invalid_kind | kind is not refill, renewal or smart |
| 422 | invalid_term | Term is not 15m, 1h or 1d |
| 422 | invalid_minimal | Auto top-up threshold is out of range |
| 422 | invalid_url | Webhook URL is not https or points to a private network |
| 422 | invalid_events | Unknown webhook event |
| 429 | rate_limited | Rate limit per key exceeded |
| 502 | provider_failed | The supplier rejected the order and the money is back on the balance. Retry with a new Idempotency-Key |
| 503 | provider_off | Purchasing is temporarily unavailable |
| 503 | provider_unavailable | The supplier is temporarily unavailable (automations), retry in a minute |
| 503 | price_unavailable | Price is temporarily unavailable |
| 503 | check_failed | Could not check the address on-chain, try again later |
| 504 | provider_unknown | The supplier did not answer in time: the order is pending. Do not create a new one — poll this one |
{
"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.
purchase key with a daily limit — it cannot spend more than the limit or create rules and webhooks.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.