POST /apiv2/order1h
Створення замовлення на оренду Energy на 1 годину через кількох постачальників Energy з автоматичним перемиканням у разі збоїв (failover).
URL кінцевої точки
POST https://netts.io/apiv2/order1hЗаголовки запиту
| Header | Required | Description |
|---|---|---|
| Content-Type | Так | application/json |
| X-API-KEY | Так | Ваш API-ключ із панелі керування Netts |
| X-Real-IP | Так | IP-адреса з вашого білого списку |
Тіло запиту
{
"amount": 131000,
"receiveAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE"
}Параметри
| Parameter | Type | Required | Description |
|---|---|---|---|
| amount | integer | Так | Кількість Energy для оренди (мінімум: 61000, максимум: 3000000) |
| receiveAddress | string | Так | Адреса TRON, яка отримає Energy (формат TRC-20) |
Вибір постачальника
API автоматично обирає оптимального постачальника Energy на основі:
- Цінової ефективності — завжди знаходить найнижчу доступну ціну
- Доступності — гарантує достатні резерви Energy
- Надійності — використовує постачальників із високим показником успішності
- Швидкості — надає пріоритет найшвидшому часу доставки
Приклади
cURL
curl -X POST https://netts.io/apiv2/order1h \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key" \
-H "X-Real-IP: your_whitelisted_ip" \
-d '{
"amount": 131000,
"receiveAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE"
}'Python
import requests
url = "https://netts.io/apiv2/order1h"
headers = {
"Content-Type": "application/json",
"X-API-KEY": "your_api_key",
"X-Real-IP": "your_whitelisted_ip"
}
payload = {
"amount": 131000,
"receiveAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE"
}
response = requests.post(url, headers=headers, json=payload)
data = response.json()
if response.status_code == 200:
detail = data.get('detail', {})
order_data = detail.get('data', {})
print(f"Order ID: {order_data.get('orderId')}")
print(f"Transaction Hash: {order_data.get('hash')}")
print(f"Energy Delivered: {order_data.get('energy')}")
print(f"Cost: {order_data.get('paidTRX')} TRX")
print(f"Delegate Address: {order_data.get('delegateAddress')}")
else:
error_detail = data.get('detail', data)
print(f"Error Code: {error_detail.get('code', 'N/A')}")
print(f"Error Message: {error_detail.get('msg', error_detail)}")Відповідь
Успішна відповідь (200 OK)
{
"detail": {
"code": 10000,
"msg": "Successful, 2.23 TRX deducted",
"data": {
"orderId": "1H123456",
"paidTRX": 2.23,
"hash": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
"delegateAddress": "TNp5gsJhBmZFXgCdgjMgr8pEZ8fHgXUHDq",
"energy": 131050
}
}
}Поля відповіді
| Field | Type | Description |
|---|---|---|
| detail.code | integer | Завжди 10000 для успішних замовлень |
| detail.msg | string | Повідомлення про успіх зі списаною сумою |
| detail.data.orderId | string | Уніфікований ID замовлення (формат: 1H{request_id}) |
| detail.data.paidTRX | number | Загальна вартість у TRX (включає комісію за активацію, якщо адреса не була активована) |
| detail.data.hash | string | null | Хеш транзакції. Поле завжди присутнє, але може бути порожнім — деякі постачальники не повертають хеш одразу. Використовуйте /apiv2/order_check через 1 хвилину, щоб отримати хеш |
| detail.data.delegateAddress | string | Адреса пулу, яка делегувала Energy |
| detail.data.energy | integer | Кількість Energy + буфер (зазвичай +50) |
Відповіді з помилками
Помилка автентифікації (401)
{
"detail": "Invalid API key or IP not in whitelist"
}Недостатньо коштів (403)
{
"code": 1004,
"msg": "Insufficient funds. Required: 2.23 TRX, Available: 1.50 TRX"
}Сервіс недоступний (503)
{
"code": 5003,
"msg": "Service temporarily unavailable. All energy providers are currently unavailable."
}Помилки постачальника (503)
{
"code": 5001,
"msg": "Energy provider temporarily unavailable"
}{
"code": 5002,
"msg": "Energy provider temporarily unavailable"
}{
"code": 5004,
"msg": "Energy provider requires higher minimum amount"
}Внутрішня помилка сервера (500)
{
"code": 5000,
"msg": "Internal server error occurred"
}Довідник кодів помилок
| Code | Description | HTTP Status |
|---|---|---|
10000 | Успіх | 200 |
10000 | Успіх (кешована відповідь) | 208 |
- | Дублікат запиту ще обробляється | 409 |
1004 | Недостатній баланс | 403 |
5000 | Внутрішня помилка сервера | 500 |
5001 | Постачальник Energy недоступний | 503 |
5002 | Постачальник Energy недоступний | 503 |
5003 | Сервіс Energy недоступний | 503 |
5004 | Не досягнуто мінімуму постачальника Energy | 503 |
Ліміти запитів
До цього ендпоінту застосовуються такі обмеження частоти запитів (на одну IP-адресу):
| Period | Limit | Description |
|---|---|---|
| 1 секунда | 50 запитів | Максимум 50 запитів на секунду |
Заголовки обмеження частоти запитів
RateLimit-Limit: 50
RateLimit-Remaining: 49
RateLimit-Reset: 1
X-RateLimit-Limit-Second: 50
X-RateLimit-Remaining-Second: 49Перевищено ліміт запитів (429)
{
"message": "API rate limit exceeded"
}Ідемпотентність
API підтримує ідемпотентність для запобігання дублюванню обробки замовлень. Коли ви надсилаєте кілька однакових запитів, система гарантує, що замовлення буде оброблено лише один раз.
Як працює ідемпотентність
Унікальність запиту визначається комбінацією таких параметрів:
- Мітка часу запиту (1-секундне вікно)
- Кількість Energy
- Адреса отримувача
- API-ключ
Кожному запиту надається 1-секундне вікно унікальності. Щоб захистити систему від зловживань і забезпечити коректну обробку, запити з ідентичними параметрами не можна надсилати частіше ніж раз на секунду.
Поточна поведінка: Система автоматично захищає клієнтів від помилкових повторних спроб для вже замовленої Energy. Якщо ви випадково надішлете один і той самий запит двічі, з вас не буде списано кошти повторно.
Передача власного ключа
Ви можете взяти контроль над ідемпотентністю у свої руки, передаючи заголовок X-Idempotency-Key. Якщо він присутній, саме це значення одноосібно визначає, чи є запит повторним, а автоматична комбінація, зазначена вище, не використовується. Якщо він відсутній, нічого не змінюється — сервер генерує ключ автоматично.
| Header | X-Idempotency-Key |
| Format | Рівно 64 шістнадцяткових символи в нижньому регістрі — хеш SHA-256 |
| Lifetime | 24 години з моменту першого запиту з цим ключем |
| Scope | Ваш акаунт. Те саме значення, надіслане з іншого акаунта, ніколи не поверне ваш результат |
Ключ будь-якого іншого формату — UUID з дефісами, base64, hex у верхньому регістрі — відхиляється з кодом 400 до розміщення замовлення та до списання коштів:
{
"detail": "Invalid idempotency key format. Must be 64-character hexadecimal string."
}Формат відрізняється від інших ендпоінтів.
/apiv2/withdraw,/apiv2/bandwidthта оркестратор приймають ключ base64 довжиною 16–64 символи. Цей ендпоінт приймає лише 64-символьний шістнадцятковий дайджест, тому код генерації ключів, скопійований із цих ендпоінтів, поверне тут помилку 400.
Як сформувати ключ
Згенеруйте його на основі вашого API-ключа. Це робить значення унікальним для вашого акаунта, відтворюваним під час повторної спроби й недосяжним для будь-кого іншого:
import hashlib
import hmac
def make_idempotency_key(api_key: str, address: str, amount: int, nonce: str) -> str:
message = f"{address}:{amount}:{nonce}"
return hmac.new(api_key.encode(), message.encode(), hashlib.sha256).hexdigest()
nonceналежить замовленню, а не запиту. Згенеруйте його один раз під час створення замовлення на вашому боці та передавайте те саме значення під час кожного надсилання цього замовлення — як для першої спроби, так і для кожного повтору. Генерація нового значення всередині функції надсилання (str(uuid.uuid4())під час кожного виклику) надає кожній спробі новий ключ, через що повтор після таймауту буде прийнято як друге замовлення та оплачено повторно. Найпростіший правильний вибір — це вже наявний у вас ідентифікатор замовлення: він існує до першої спроби та зберігається після перезапуску вашого процесу.
# один раз, коли замовлення з'являється у вашій системі
order = create_order(address="TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE", amount=131000)
# під час першої спроби та кожного повтору — ті самі три вхідні дані, той самий ключ
key = make_idempotency_key(API_KEY, order.address, order.amount, order.id)
headers = {
"Content-Type": "application/json",
"X-API-KEY": API_KEY,
"X-Idempotency-Key": key,
}Ключ дійсний протягом 24 годин. Після цього той самий nonce знову стає вільним і розпочинає нове замовлення.
Не використовуйте значення, до яких може підібратися хтось інший — 64 нулі, хеш фіксованого слова. Ключі ділять єдиний простір між усіма акаунтами. Така колізія ніколи не розкриє замовлення іншого акаунта, але ваш запит відхилятиметься з кодом 409, доки не мине термін дії їхнього ключа, що є небажаною відповіддю під час повторної спроби.
Розміщення двох ідентичних замовлень
Іноді вам дійсно потрібно розмістити однакове замовлення двічі — ту саму кількість Energy на ту саму адресу, одне за одним. Автоматичний ключ не здатний відрізнити це від повторної спроби: обидва запити байт-у-байт ідентичні, і єдине, що їх відрізняє, — це момент їхнього надходження.
Без власного ключа результат залежить від інтервалу між ними:
| Інтервал між двома запитами | Що відбувається |
|---|---|
| У межах того самого 1-секундного вікна | Другий запит вважається повтором. Він не виконується: ви отримуєте 208 і відповідь першого замовлення включно з orderId. Кошти за нього не списуються |
| З різницею понад одну секунду | Два різні ключі — обидва замовлення розміщуються і за обидва списуються кошти |
Тому, якщо ви покладаєтеся на автоматичний ключ, робіть паузу понад одну секунду між двома однаковими замовленнями та аналізуйте код статусу: 208 означає, що щойно надіслане замовлення не було розміщено.
Пауза — це тимчасовий обхідний шлях, а не вирішення. Вона розмежовує кожен запит, включно з тими, які ви ніколи не планували повторювати — повтор після таймауту, подвійне клацання, повідомлення, повторно доставлене вашою чергою. Вони також надходять пізніше зазначеного вікна, тому розміщуються як окремі замовлення й оплачуються окремо. Таймаут відповіді цього ендпоінту становить 10 секунд, що вже далеко за межами вікна: автоматичний ключ не захищає повтор, здійснений після таймауту.
Власний ключ позбавляє від здогадок, оскільки прийняття рішення переноситься на єдину сторону, якій відома відповідь:
| Що ви робите | Що ви надсилаєте | Результат |
|---|---|---|
| Друге, дійсно нове замовлення | Новий nonce | Новий ключ — замовлення розміщується |
| Повтор замовлення, результат якого вам невідомий | nonce першої спроби | Той самий ключ — 208, початкова відповідь, без повторного списання |
Другий рядок — це причина існування заголовка, і саме тут у реалізаціях найчастіше виникають помилки: див. примітку в розділі Як сформувати ключ.
Коди стану HTTP для повторних запитів
| Status Code | Name | Description |
|---|---|---|
| 200 | OK | Замовлення успішно оброблено (перший запит) |
| 208 | Already Reported | Замовлення вже було оброблено, повертається кешована відповідь |
| 409 | Conflict | Запит наразі обробляється, не повторюйте спробу |
Повторний запит — вже оброблено (208)
Коли надходить повторний запит для вже виконаного замовлення:
{
"detail": {
"code": 10000,
"msg": "Successful, 2.54 TRX deducted",
"data": {
"hash": "9e4c20e21e01e4c39b21b670d1ea1fc1e4b0de94d8fbd4c190d5378ba911dfae",
"energy": 65050,
"orderId": "1H70bcc7962a",
"paidTRX": 2.535,
"delegateAddress": "TNp5gsJhBmZFXgCdgjMgr8pEZ8fHgXUHDq"
}
},
"idempotency": {
"status": "completed",
"cached": true,
"original_created_at": "2025-12-03T10:34:49.104896"
}
}Тіло відповіді ідентичне оригінальній успішній відповіді з додатковим об'єктом idempotency, який вказує на те, що ця відповідь кешована.
Повторний запит — ще обробляється (409)
Коли повторний запит надходить тоді, коли оригінальний ще перебуває в процесі обробки:
{
"success": false,
"error": "duplicate_request_processing",
"message": "This request is currently being processed. Please wait and do not retry.",
"idempotency_key": "b9e67b2412d33c92a4f0e1c3d5b7a9f2e4c6d8b0a2f4e6c8d0b2a4f6e8c0d2b4",
"retry_after_seconds": 3
}Рекомендація: Зачекайте вказану кількість retry_after_seconds, перш ніж перевіряти статус замовлення.
Рекомендації щодо використання
- Не надсилайте паралельні запити з однаковими параметрами — дочекайтеся відповіді на кожен із них
- Використовуйте новий
nonceдля кожного нового замовлення, а такожnonceпершої спроби для кожного його повтору - Ніколи не генеруйте
nonceзаново під час надсилання — повторна спроба повинна відтворювати ключ першої спроби, а не новий - Обробляйте відповіді 409 очікуванням, а не негайним повторенням запиту
- Перевіряйте поле
idempotency.cached, щоб визначати кешовані відповіді — статус208свідчить про те, що щойно надіслане замовлення не було розміщено
Постоплатні акаунти
Акаунти з постоплатою використовують той самий ендпоінт, URL, заголовки та тіло запиту, що й усі інші. Різниця полягає у способі оплати замовлення:
- З вашого депозиту нічого не списується. Кожне успішне замовлення додається до суми заборгованості вашого акаунта у валюті розрахунку, зазначеній у вашому договорі. Ви оплачуєте його за рахунком-фактурою згідно з вашою домовленістю.
- Ваш акаунт має кредитний ліміт. Поки сума до сплати залишається в межах ліміту, замовлення приймаються навіть із нульовим депозитом.
- Оплачуються лише фактично надані послуги. Відхилене або невиконане замовлення не коштує нічого.
- Якщо адресу отримувача необхідно активувати, активація є частиною того самого замовлення та виставляється в рахунок як окрема стаття витрат.
Постоплата активується Netts для акаунта за запитом. Кожна відповідь містить інформацію про спосіб оплати замовлення: paymentMode приймає значення postpaid або prepaid.
Додаткові поля запиту
| Parameter | Type | Required | Description |
|---|---|---|---|
| test | boolean | Ні | true — лише розрахунок ціни. Нічого не замовляється, не доставляється та не тарифікується |
| freezePrice | boolean | Ні | true — зафіксувати ціну за вашим договором на 60 секунд. Нічого не замовляється та не тарифікується |
| quoteId | string | Ні | Розмістити замовлення за ціною пропозиції (quote), виданої через freezePrice |
| priceCurrency | string | Ні | TRX (за замовчуванням) або валюта вашого договору, як у settlement.asset. Для постоплатних акаунтів суми у валюті вашого договору включаються завжди |
Поле quoteId не можна комбінувати з test або freezePrice. Будь-яке інше поле в тілі запиту відхиляється.
Перевірка ціни (test: true)
{
"amount": 65000,
"receiveAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"test": true
}{
"detail": {
"code": 10010,
"msg": "Test calculation",
"data": {
"amount": 65000,
"unitPriceSun": "20",
"discount": null,
"energyTRX": "1.300000",
"activationTRX": "1.100000",
"wouldCostTRX": "2.400000",
"paidTRX": 0,
"paymentMode": "postpaid",
"activationCheck": "inactive",
"alt": {"USDC": {"energy": "0.430541", "activation": "0.364304", "total": "0.794845"}},
"fxAt": "2026-10-19T07:15:18.469870+00:00"
}
}
}| Field | Description |
|---|---|
| unitPriceSun | Ціна за одиницю Energy в SUN з урахуванням вашої персональної знижки, якщо вона є |
| discount | Ваша персональна знижка (type, value) або null |
| energyTRX / activationTRX / wouldCostTRX | Вартість Energy, активації (якщо адреса її потребує) та загальна сума в TRX |
| activationCheck | inactive — адресу буде активовано та внесено до рахунку; active — активація не потрібна; unknown — не вдалося перевірити зараз |
alt.<currency> | Ті самі суми у валюті вашого договору |
| fxAt | Час фіксації курсу обміну валют, використаного для сум у валюті вашого договору |
Пропозиція з фіксованою ціною (freezePrice: true)
Використовуйте пропозицію (quote), щоб зафіксувати ціну за вашим договором перед замовленням.
- Надішліть замовлення з
"freezePrice": true. Відповідь міститиме код10011(Quote issued), ті самі поля, що й під час перевірки ціни, а такожquoteIdіexpiresAt. - Протягом 60 секунд надішліть замовлення з
"quoteId": "<quoteId>"та тими самими значеннямиamountіreceiveAddress. - Замовлення тарифікується за зафіксованою ціною — ціною за вашим договором на момент формування пропозиції — навіть якщо ціна зміниться в проміжку між запитами.
Пропозицію можна використати один раз і лише для того самого продукту: пропозиція на 1 годину не може бути використана для оплати 5-хвилинного замовлення. Прострочена або вже використана пропозиція відхиляється, і нічого не замовляється.
{
"amount": 65000,
"receiveAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"quoteId": "q_4f540f16a2c94b7f8e3d1a6b5c0e9d72"
}Успішне замовлення
{
"detail": {
"code": 10000,
"msg": "Successful, 1.30 TRX for energy + 1.10 TRX for address activation, postpaid",
"data": {
"orderId": "1Hf9544a1508",
"paidTRX": 2.4,
"hash": "032bda8fa6911b42c15a6d8d30520dc22bcc4077972a1503b6a3fae491967eb4",
"delegateAddress": "TVnkj2jHKgmea8DVJFo2teD7rRSpH51bne",
"energy": 65000,
"activationHash": "259e1ab276114ae717a6f62d7d25eb1f89a2dccb637cbe41be626df3c2a78519",
"paymentMode": "postpaid",
"energyTRX": "1.300000",
"activationTRX": "1.100000",
"totalTRX": "2.400000"
}
}
}| Field | Description |
|---|---|
| paidTRX | Вартість послуги в TRX. Для постоплатних акаунтів вона не списується з депозиту |
| paymentMode | postpaid |
| energyTRX / activationTRX / totalTRX | Точні суми, 6 знаків після коми |
| quoteId, alt | Присутні, якщо замовлення було розміщено за пропозицією: ідентифікатор пропозиції та зафіксовані суми |
| activationFinal | Присутнє та дорівнює false лише тоді, коли вартість активації є попередньою; остаточна сума з'явиться у вашому рахунку-фактурі |
Усі інші поля такі самі, як і для передоплатних замовлень.
Вичерпано кредитний ліміт (402)
{
"detail": {
"code": 1004,
"msg": "Postpaid credit limit reached.",
"paymentMode": "postpaid",
"reason": "limit_reached"
}
}Замовлення не виконується і кошти не нараховуються. Нові замовлення знову прийматимуться, щойно буде зареєстровано ваш платіж або збільшено ваш ліміт.
Коди помилок для постоплати
| Code | HTTP | Description |
|---|---|---|
10010 | 200 | Результат перевірки ціни (test) |
10011 | 200 | Пропозицію сформовано (freezePrice) |
1004 | 402 | Вичерпано кредитний ліміт постоплати |
4100 | 400 | Некоректний запит |
4101 | 400 | Непідтримувана priceCurrency |
4102 | 400 | quoteId скомбіновано з test або freezePrice |
4103 | 409 | Пропозицію вже використано |
4104 | 409 | Термін дії пропозиції минув |
4105 | 409 | Ключ ідемпотентності вже використано для іншого запиту |
4106 | 404 | Пропозицію не знайдено |
4107 | 409 | Параметри замовлення відрізняються від зазначених у пропозиції |
4108 | 400 | Некоректний формат quoteId |
4109 | 422 | Недоступно для субакаунтів |
5032 | 503 | Ціна тимчасово недоступна |
5033 | 503 | Обмінний курс тимчасово недоступний |
5034 | 503 | Білінг тимчасово недоступний — замовлення не прийнято |
5036 | 503 | Перевірка кредитного ліміту тимчасово недоступна — повторіть спробу пізніше |
Для всіх відповідей 4xx та 503, перелічених вище, нічого не замовляється та рахунки не виставляються.
Примітки
- Energy доставляється миттєво після успішного замовлення (зазвичай протягом 0.5–10 секунд)
- Таймаут відповіді API: Максимум 10 секунд, зазвичай відповідь надходить протягом 2 секунд
- Активація адреси: Якщо адреса отримувача не активована, Netts активує її за собівартістю
- Затримка активації: Для неактивованих адрес відповідь API може тривати до 6 секунд через процес активації
- Замовлення обробляються в режимі 24/7 з автоматичним перемиканням на резервних постачальників
- Мінімальна кількість Energy: 61 000 одиниць
- Максимальна кількість Energy: 3 000 000 одиниць на одне замовлення
- Буфер Energy: +50 одиниць додаються автоматично для компенсації з боку постачальника (безкоштовно)
- Хеш транзакції: Поле присутнє завжди, але може бути порожнім, якщо постачальник не повертає його одразу. Щоб отримати хеш, викличте /apiv2/order_check не раніше ніж через 1 хвилину після розміщення замовлення
- Вибір постачальника: Автоматичний на основі вартості та доступності
- Формат ID замовлення:
1H{request_id}для уніфікованого відстеження - Ціноутворення: Динамічне, залежить від часу доби та обсягу Energy
- Тривалість: Фіксована — 1 година (3600 секунд)
- Обмеження частоти запитів: 50 запитів на секунду для однієї IP-адреси