POST /apiv2/order1h
Создать заказ на аренду энергии на 1 час через нескольких поставщиков энергии с автоматическим переключением при сбоях.
URL эндпоинта
POST https://netts.io/apiv2/order1hЗаголовки запроса
| Заголовок | Обязательный | Описание |
|---|---|---|
| Content-Type | Да | application/json |
| X-API-KEY | Да | Ваш API-ключ из панели управления Netts |
| X-Real-IP | Да | IP-адрес из вашего белого списка |
Тело запроса
{
"amount": 131000,
"receiveAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE"
}Параметры запроса
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| amount | integer | Да | Количество арендуемой энергии (минимум: 61000, максимум: 3000000) |
| receiveAddress | string | Да | Адрес TRON, который получит энергию (формат TRC-20) |
Выбор поставщика
API автоматически выбирает оптимального поставщика энергии на основе:
- Экономической эффективности — всегда находит самую низкую доступную цену
- Доступности — гарантирует достаточные резервы энергии
- Надежности — использует поставщиков с высоким показателем успешности
- Скорости — отдает приоритет наиболее быстрому времени доставки
Примеры
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
}
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
| detail.code | integer | Всегда 10000 для успешных заказов |
| detail.msg | string | Сообщение об успехе с указанием списанной суммы |
| detail.data.orderId | string | Унифицированный идентификатор заказа (формат: 1H{request_id}) |
| detail.data.paidTRX | number | Общая стоимость в TRX (включает комиссию за активацию, если адрес не был активирован) |
| detail.data.hash | string | null | Хеш транзакции. Поле присутствует всегда, но может быть пустым — некоторые поставщики не возвращают хеш мгновенно. Используйте /apiv2/order_check через 1 минуту, чтобы получить хеш |
| detail.data.delegateAddress | string | Адрес пула, который делегировал энергию |
| detail.data.energy | integer | Количество энергии + буфер (обычно +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"
}Справочник кодов ошибок
| Код | Описание | HTTP-статус |
|---|---|---|
10000 | Успешно | 200 |
10000 | Успешно (кэшированный ответ) | 208 |
- | Повторный запрос все еще обрабатывается | 409 |
1004 | Недостаточный баланс | 403 |
5000 | Внутренняя ошибка сервера | 500 |
5001 | Поставщик энергии недоступен | 503 |
5002 | Поставщик энергии недоступен | 503 |
5003 | Сервис энергии недоступен | 503 |
5004 | Не соблюден минимум поставщика энергии | 503 |
Ограничения частоты запросов
К данному эндпоинту применяются следующие ограничения частоты запросов (на один IP-адрес):
| Период | Лимит | Описание |
|---|---|---|
| 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 секунду)
- Количество энергии
- Адрес получателя
- API-ключ
Каждому запросу предоставляется 1-секундное окно уникальности. Для защиты системы от злоупотреблений и обеспечения корректной обработки запросы с идентичными параметрами не могут отправляться чаще одного раза в секунду.
Текущее поведение: Система автоматически защищает клиентов от ошибочных повторов для уже заказанной энергии. Если вы случайно отправите один и тот же запрос дважды, средства не будут списаны повторно.
Передача собственного ключа
Вы можете взять управление идемпотентностью в свои руки, передавая заголовок X-Idempotency-Key. При его наличии только это значение определяет, является ли запрос повторным, а автоматическая комбинация выше не используется. Когда он отсутствует, ничего не меняется — сервер формирует ключ за вас.
| Заголовок | X-Idempotency-Key |
| Формат | Ровно 64 шестнадцатеричных символа в нижнем регистре — дайджест SHA-256 |
| Время жизни | 24 часа с момента первого запроса, содержащего этот ключ |
| Область действия | Ваш аккаунт. Одинаковое значение, отправленное другим аккаунтом, никогда не вернет ваш результат |
Ключ любого другого формата — UUID с дефисами, base64, шестнадцатеричные символы в верхнем регистре — отклоняется с кодом 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, пока не истечет срок действия их ключа, что совсем не тот ответ, который вы хотите получить посреди повторной попытки.
Размещение двух идентичных заказов
Иногда вам действительно необходимо создать два одинаковых заказа — то же количество энергии на тот же адрес, один за другим. Автоматический ключ не может отличить такую ситуацию от повтора: два запроса идентичны байт в байт, и единственное, что их разделяет, — это момент их поступления.
Без собственного ключа результат зависит от промежутка времени между ними:
| Промежуток между двумя запросами | Что происходит |
|---|---|
| В пределах одного 1-секундного окна | Второй запрос принимается за повтор. Он не выполняется: вы получаете 208 и ответ первого заказа, включая orderId. Средства за него не списываются |
| С разницей более секунды | Два разных ключа — оба заказа размещаются и оба оплачиваются |
Поэтому, если вы полагаетесь на автоматический ключ, выдерживайте паузу более одной секунды между двумя одинаковыми заказами и проверяйте статус-код: 208 означает, что заказ, который вы только что отправили, не был размещен.
Пауза — это временное решение, а не исправление. Она разделяет каждый запрос, включая те, которые вы вовсе не планировали повторять: повтор после таймаута, двойной клик, повторная доставка сообщения из вашей очереди. Они также приходят позже указанного окна, поэтому размещаются как отдельные заказы и списываются отдельно. Таймаут ответа этого эндпоинта составляет 10 секунд, что уже далеко за пределами окна: автоматический ключ не защищает повтор, следующий после таймаута.
Собственный ключ избавляет от неопределенности, поскольку принятие решения переносится на единственную сторону, знающую правильный ответ:
| Что вы делаете | Что вы отправляете | Результат |
|---|---|---|
| Второй, действительно новый заказ | Новый nonce | Новый ключ — заказ размещен |
| Повтор заказа, результат которого вам неизвестен | Значение nonce первой попытки | Тот же ключ — 208, исходный ответ, никакого второго списания |
Вторая строка — это именно та причина, по которой существует заголовок, и именно здесь в реализациях чаще всего возникают ошибки: см. примечание в разделе Как формировать ключ.
HTTP-статусы для повторяющихся запросов
| Код состояния | Название | Описание |
|---|---|---|
| 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.
Дополнительные поля запроса
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| test | boolean | Нет | true — только рассчитать стоимость. Ничего не заказывается, не доставляется и не списывается |
| freezePrice | boolean | Нет | true — зафиксировать цену по вашему договору на 60 секунд. Ничего не заказывается и не списывается |
| quoteId | string | Нет | Разместить заказ по стоимости котировки, выданной через 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"
}
}
}| Поле | Описание |
|---|---|
| unitPriceSun | Цена за единицу энергии в SUN с учетом вашей персональной скидки, если она предусмотрена |
| discount | Ваша персональная скидка (type, value) или null |
| energyTRX / activationTRX / wouldCostTRX | Энергия, активация (если адресу она требуется) и общая сумма в TRX |
| activationCheck | inactive — адрес будет активирован и включен в счет; active — активация не требуется; unknown — не удалось проверить в данный момент |
alt.<currency> | Те же суммы в валюте вашего договора |
| fxAt | Время фиксации обменного курса, использованного для сумм в валюте договора |
Фиксированная котировка (freezePrice: true)
Используйте котировку, чтобы зафиксировать стоимость по вашему договору до создания заказа.
- Отправьте заказ с параметром
"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"
}
}
}| Поле | Описание |
|---|---|
| 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"
}
}Заказ не выполняется, и средства не начисляются к оплате. Прием новых заказов возобновляется после поступления вашей оплаты или увеличения кредитного лимита.
Коды ошибок постоплаты
| Код | HTTP | Описание |
|---|---|---|
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 заказ не создается и начисления не производятся.
Примечания
- Энергия доставляется мгновенно после успешного оформления заказа (обычно в течение 0.5–10 секунд)
- Таймаут ответа API: максимум 10 секунд, стандартное время ответа составляет до 2 секунд
- Активация адреса: если адрес получателя не активирован, Netts активирует его по себестоимости
- Задержка активации: для неактивированных адресов время ответа API может составлять до 6 секунд из-за процесса активации
- Заказы обрабатываются круглосуточно (24/7) с автоматическим переключением между поставщиками
- Минимальное количество энергии: 61 000 единиц
- Максимальное количество энергии: 3 000 000 единиц на один заказ
- Буфер энергии: +50 единиц добавляются автоматически для компенсации со стороны поставщика (бесплатно)
- Хеш транзакции: поле присутствует всегда, но может быть пустым, если поставщик не вернул его сразу. Чтобы получить хеш, вызовите /apiv2/order_check не ранее чем через 1 минуту после создания заказа
- Выбор поставщика: происходит автоматически на основе цены и доступности
- Формат ID заказа:
1H{request_id}для унифицированного отслеживания - Ценообразование: динамическое, зависит от времени суток и объема энергии
- Длительность: фиксированно 1 час (3600 секунд)
- Ограничение частоты запросов: 50 запросов в секунду с одного IP-адреса