POST /apiv2/order1h
Cree una orden de alquiler de energía de 1 hora a través de múltiples proveedores de energía con conmutación por error automática.
URL del endpoint
POST https://netts.io/apiv2/order1hEncabezados de solicitud
| Encabezado | Requerido | Descripción |
|---|---|---|
| Content-Type | Sí | application/json |
| X-API-KEY | Sí | Su clave API del panel de control de Netts |
| X-Real-IP | Sí | Dirección IP de su lista blanca |
Cuerpo de solicitud
{
"amount": 131000,
"receiveAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE"
}Parámetros
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| amount | integer | Sí | Cantidad de Energy a alquilar (mínimo: 61000, máximo: 3000000) |
| receiveAddress | string | Sí | Dirección TRON que recibirá la energía (formato TRC-20) |
Selección de proveedores
La API selecciona automáticamente el proveedor de energía óptimo basándose en:
- Rentabilidad - Siempre encuentra el precio disponible más bajo
- Disponibilidad - Garantiza reservas de energía suficientes
- Fiabilidad - Utiliza proveedores con altas tasas de éxito
- Velocidad - Prioriza los tiempos de entrega más rápidos
Ejemplos de peticiones
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)}")Respuesta
Respuesta exitosa (200 OK)
{
"detail": {
"code": 10000,
"msg": "Successful, 2.23 TRX deducted",
"data": {
"orderId": "1H123456",
"paidTRX": 2.23,
"hash": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
"delegateAddress": "TNp5gsJhBmZFXgCdgjMgr8pEZ8fHgXUHDq",
"energy": 131050
}
}
}Campos de respuesta
| Campo | Tipo | Descripción |
|---|---|---|
| detail.code | integer | Siempre 10000 para órdenes exitosas |
| detail.msg | string | Mensaje de éxito con el monto deducido |
| detail.data.orderId | string | ID de orden unificado (formato: 1H{request_id}) |
| detail.data.paidTRX | number | Costo total en TRX (incluye la tarifa de activación si la dirección no estaba activada) |
| detail.data.hash | string | null | Hash de la transacción. El campo siempre está presente pero puede estar vacío - algunos proveedores no devuelven el hash de inmediato. Use /apiv2/order_check después de 1 minuto para obtener el hash |
| detail.data.delegateAddress | string | Dirección del grupo (pool) que delegó la energía |
| detail.data.energy | integer | Cantidad de Energy + margen de búfer (típicamente +50) |
Respuestas de error
Error de autenticación (401)
{
"detail": "Invalid API key or IP not in whitelist"
}Saldo insuficiente (403)
{
"code": 1004,
"msg": "Insufficient funds. Required: 2.23 TRX, Available: 1.50 TRX"
}Servicio no disponible (503)
{
"code": 5003,
"msg": "Service temporarily unavailable. All energy providers are currently unavailable."
}Errores del proveedor (503)
{
"code": 5001,
"msg": "Energy provider temporarily unavailable"
}{
"code": 5002,
"msg": "Energy provider temporarily unavailable"
}{
"code": 5004,
"msg": "Energy provider requires higher minimum amount"
}Error interno del servidor (500)
{
"code": 5000,
"msg": "Internal server error occurred"
}Referencia de códigos de error
| Código | Descripción | Estado HTTP |
|---|---|---|
10000 | Éxito | 200 |
10000 | Éxito (respuesta en caché) | 208 |
- | Petición duplicada aún en procesamiento | 409 |
1004 | Saldo insuficiente | 403 |
5000 | Error interno del servidor | 500 |
5001 | Proveedor de energía no disponible | 503 |
5002 | Proveedor de energía no disponible | 503 |
5003 | Servicio de energía no disponible | 503 |
5004 | Mínimo del proveedor de energía no alcanzado | 503 |
Límites de tasa
Se aplican los siguientes límites de tasa a este endpoint (por dirección IP):
| Periodo | Límite | Descripción |
|---|---|---|
| 1 segundo | 50 peticiones | Máximo de 50 peticiones por segundo |
Encabezados de límite de tasa
RateLimit-Limit: 50
RateLimit-Remaining: 49
RateLimit-Reset: 1
X-RateLimit-Limit-Second: 50
X-RateLimit-Remaining-Second: 49Límite de tasa excedido (429)
{
"message": "API rate limit exceeded"
}Idempotencia
La API admite idempotencia para evitar el procesamiento de órdenes duplicadas. Cuando envía varias peticiones idénticas, el sistema garantiza que la orden se procese solo una vez.
Cómo funciona la idempotencia
La singularidad de la petición se determina mediante una combinación de:
- Marca de tiempo de la petición (ventana de 1 segundo)
- Cantidad de energía
- Dirección del receptor
- Clave API
A cada petición se le asigna una ventana de unicidad de 1 segundo. Para proteger el sistema contra abusos y garantizar un procesamiento adecuado, las peticiones con parámetros idénticos no se pueden enviar con una frecuencia superior a una vez por segundo.
Comportamiento actual: El sistema protege automáticamente a los clientes contra reintentos erróneos sobre energía ya ordenada. Si accidentalmente envía la misma petición dos veces, no se le cobrará dos veces.
Proporcionar su propia clave
Puede tomar el control de la idempotencia enviando el encabezado X-Idempotency-Key. Cuando está presente, ese valor por sí solo decide si una petición es repetida y no se utiliza la combinación automática anterior. Cuando no está presente, nada cambia: el servidor deduce la clave por usted.
| Encabezado | X-Idempotency-Key |
| Formato | Exactamente 64 caracteres hexadecimales en minúsculas — un hash SHA-256 |
| Duración | 24 horas a partir de la primera petición que lleve esa clave |
| Alcance | Su cuenta. El mismo valor enviado por una cuenta diferente nunca devuelve su resultado |
Una clave con cualquier otro formato (un UUID con guiones, base64, hexadecimal en mayúsculas) se rechaza con 400 antes de que se cree la orden y antes de que se cobre nada:
{
"detail": "Invalid idempotency key format. Must be 64-character hexadecimal string."
}El formato difiere de otros endpoints.
/apiv2/withdraw,/apiv2/bandwidthy el orquestador aceptan una clave en base64 de 16 a 64 caracteres. Este endpoint solo acepta un resumen hexadecimal de 64 caracteres, por lo que el código de generación de claves copiado de esos endpoints devolverá 400 aquí.
Cómo formar la clave
Derívela a partir de su clave API. Eso hace que el valor sea único para su cuenta, reproducible en un reintento e imposible de obtener para cualquier otra persona:
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()El
noncepertenece a la orden, no a la petición. Elíjalo una vez, cuando la orden se cree en su lado, y pase ese mismo valor en cada envío de esa orden, tanto en el primer intento como en cada reintento. Generar un valor nuevo dentro de la función de envío (str(uuid.uuid4())en cada llamada) le da a cada intento una clave diferente, por lo que un reintento tras un tiempo de espera se acepta como una segunda orden y se cobra nuevamente. La opción correcta más simple es el ID de orden que ya tiene: existe antes del primer intento y sobrevive al reinicio de su proceso.
# una vez, cuando la orden aparece en su sistema
order = create_order(address="TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE", amount=131000)
# en el primer intento y en cada reintento — las mismas tres entradas, la misma clave
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,
}Una clave dura 24 horas. Después de eso, el mismo nonce queda libre nuevamente e inicia una nueva orden.
No use un valor al que cualquiera pudiera llegar: 64 ceros, el hash de una palabra fija. Las claves comparten un único espacio entre cuentas. Dicha colisión nunca expone la orden de otra cuenta, pero su petición se rechazará con 409 hasta que expire la clave de ellos, lo cual no es la respuesta que desea a mitad de un reintento.
Realizar dos órdenes idénticas
A veces realmente desea la misma orden dos veces: la misma cantidad de energía para la misma dirección, una tras otra. La clave automática no puede distinguir eso de un reintento: las dos peticiones son idénticas byte por byte, y lo único que las separa es el momento en que llegan.
Sin una clave propia, el resultado depende del intervalo entre ellas:
| Intervalo entre las dos peticiones | Qué sucede |
|---|---|
| Dentro de la misma ventana de 1 segundo | La segunda petición se toma como una repetición. No se ejecuta: obtiene 208 y la respuesta de la primera orden, incluido orderId. No se cobra nada por ella |
| Con más de un segundo de diferencia | Dos claves diferentes — ambas órdenes se crean y ambas se cobran |
Por lo tanto, si confía en la clave automática, deje más de un segundo entre dos órdenes idénticas y lea el código de estado: 208 significa que la orden que acaba de enviar no se ejecutó.
Una pausa es una solución provisional, no definitiva. Separa cada petición, incluidas aquellas que nunca tuvo la intención de repetir: un reintento tras un tiempo de espera agotado, un doble clic, un mensaje reenviado por su cola. Estas también llegan después de la ventana, por lo que se procesan como órdenes independientes y se cobran por separado. El tiempo de espera de respuesta de este endpoint es de 10 segundos, lo que ya está muy fuera de la ventana: la clave automática no protege un reintento que sigue a un tiempo de espera agotado.
Su propia clave elimina la incertidumbre, porque la decisión se traslada a la única parte que conoce la respuesta:
| Lo que está haciendo | Lo que envía | Resultado |
|---|---|---|
| Una segunda orden genuinamente nueva | Un nuevo nonce | Una nueva clave — la orden se procesa |
| Un reintento de una orden cuyo resultado desconoce | El nonce del primer intento | La misma clave — 208, la respuesta original, sin segundo cobro |
La segunda fila es la razón por la que existe el encabezado y es donde las implementaciones suelen fallar: consulte la nota debajo de Cómo formar la clave.
Códigos de estado HTTP para peticiones duplicadas
| Código de estado | Nombre | Descripción |
|---|---|---|
| 200 | OK | Orden procesada exitosamente (primera petición) |
| 208 | Already Reported | La orden ya fue procesada, devolviendo respuesta en caché |
| 409 | Conflict | La petición se está procesando actualmente, no reintente |
Petición duplicada - Ya procesada (208)
Cuando se recibe una petición duplicada para una orden ya completada:
{
"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"
}
}El cuerpo de la respuesta es idéntico a la respuesta exitosa original, con un objeto idempotency adicional que indica que se trata de una respuesta en caché.
Petición duplicada - Aún en procesamiento (409)
Cuando llega una petición duplicada mientras la original todavía se está procesando:
{
"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
}Recomendación: Espere los retry_after_seconds especificados antes de verificar el estado de la orden.
Mejores prácticas
- No envíe peticiones paralelas con los mismos parámetros; espere cada respuesta
- Use un nuevo
noncepara cada nueva orden, y elnoncedel primer intento para cada reintento de la misma - Nunca regenere el
nonceal momento de enviar — un reintento debe reproducir la clave del primer intento, no una nueva - Maneje las respuestas 409 esperando, no reintentando de inmediato
- Verifique el campo
idempotency.cachedpara identificar respuestas en caché — un208significa que la orden que acaba de enviar no se ejecutó
Cuentas de pospago
Las cuentas con facturación de pospago utilizan el mismo endpoint, URL, encabezados y cuerpo de petición que todos los demás. La diferencia radica en cómo se paga la orden:
- No se deduce nada de su depósito. Cada orden exitosa se agrega al saldo pendiente de su cuenta en la moneda de liquidación acordada en su contrato. Usted la paga mediante factura, según su acuerdo.
- Su cuenta tiene un límite de crédito. Mientras el importe adeudado esté dentro del límite, las órdenes se aceptan incluso con un depósito de cero.
- Solo se facturan los servicios entregados. Una orden que sea rechazada o no ejecutada no cuesta nada.
- Si la dirección receptora debe activarse, la activación forma parte de la misma orden y se factura como un cargo independiente.
La facturación de pospago la habilita Netts para una cuenta previa solicitud. Cada respuesta le indica cómo se paga la orden: paymentMode es postpaid o prepaid.
Campos adicionales de la petición
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| test | boolean | No | true — calcula solo el precio. No se ordena, entrega ni factura nada |
| freezePrice | boolean | No | true — fija el precio bajo su contrato durante 60 segundos. No se ordena ni factura nada |
| quoteId | string | No | Crea la orden al precio de una cotización emitida mediante freezePrice |
| priceCurrency | string | No | TRX (predeterminado) o la moneda de su contrato, como en settlement.asset. Para cuentas de pospago siempre se incluyen los montos en la moneda de su contrato |
quoteId no se puede combinar con test o freezePrice. Cualquier otro campo en el cuerpo de la petición será rechazado.
Comprobación de precio (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"
}
}
}| Campo | Descripción |
|---|---|
| unitPriceSun | Precio por unidad de energía en SUN, incluido su descuento personal si tiene uno |
| discount | Su descuento personal (type, value) o null |
| energyTRX / activationTRX / wouldCostTRX | Energía, activación (si la dirección la necesita) y total, en TRX |
| activationCheck | inactive — la dirección será activada y facturada; active — sin activación; unknown — no se pudo verificar en este momento |
alt.<currency> | Los mismos montos en la moneda de su contrato |
| fxAt | Momento del tipo de cambio utilizado para los montos en la moneda de su contrato |
Cotización de precio fijo (freezePrice: true)
Utilice una cotización para fijar el precio según su contrato antes de ordenar.
- Envíe la orden con
"freezePrice": true. La respuesta tiene el código10011(Quote issued), los mismos campos que una verificación de precio, másquoteIdyexpiresAt. - Dentro de 60 segundos envíe la orden con
"quoteId": "<quoteId>"y la mismaamountyreceiveAddress. - La orden se factura al precio cotizado (el precio según su contrato en el momento de la cotización), incluso si el precio cambia en el ínterin.
Una cotización se puede utilizar una sola vez y solo para el mismo producto: una cotización de 1 hora no puede pagar una orden de 5 minutos. Una cotización vencida o ya utilizada se rechaza y no se ordena nada.
{
"amount": 65000,
"receiveAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"quoteId": "q_4f540f16a2c94b7f8e3d1a6b5c0e9d72"
}Orden exitosa
{
"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"
}
}
}| Campo | Descripción |
|---|---|
| paidTRX | Costo del servicio en TRX. Para cuentas de pospago no se deduce del depósito |
| paymentMode | postpaid |
| energyTRX / activationTRX / totalTRX | Montos exactos, 6 decimales |
| quoteId, alt | Presentes cuando la orden se realizó con una cotización: el ID de cotización y los montos cotizados |
| activationFinal | Presente y false únicamente cuando el monto de activación es preliminar; el monto final aparece en su factura |
Todos los demás campos son los mismos que para las órdenes de prepago.
Límite de crédito alcanzado (402)
{
"detail": {
"code": 1004,
"msg": "Postpaid credit limit reached.",
"paymentMode": "postpaid",
"reason": "limit_reached"
}
}La orden no se ejecuta y no se factura nada. Se aceptan nuevas órdenes nuevamente una vez que se registre su pago o se incremente su límite.
Códigos de error de pospago
| Código | HTTP | Descripción |
|---|---|---|
10010 | 200 | Resultado de la comprobación de precio (test) |
10011 | 200 | Cotización emitida (freezePrice) |
1004 | 402 | Límite de crédito de pospago alcanzado |
4100 | 400 | Petición no válida |
4101 | 400 | priceCurrency no compatible |
4102 | 400 | quoteId combinado con test o freezePrice |
4103 | 409 | Cotización ya utilizada |
4104 | 409 | Cotización vencida |
4105 | 409 | Clave de idempotencia ya utilizada para una petición diferente |
4106 | 404 | Cotización no encontrada |
4107 | 409 | Los parámetros de la orden difieren de la cotización |
4108 | 400 | Formato de quoteId no válido |
4109 | 422 | No disponible para subcuentas |
5032 | 503 | Precio no disponible temporalmente |
5033 | 503 | Tipo de cambio no disponible temporalmente |
5034 | 503 | Facturación no disponible temporalmente — la orden no fue aceptada |
5036 | 503 | Verificación de crédito no disponible temporalmente — reintente más tarde |
Para todas las respuestas 4xx y 503 anteriores no se ordena nada ni se factura nada.
Notas
- La energía se entrega instantáneamente tras una orden exitosa (típicamente entre 0.5 y 10 segundos)
- Tiempo de espera de respuesta de la API: Máximo 10 segundos, normalmente responde en hasta 2 segundos
- Activación de dirección: Si la dirección receptora no está activada, Netts la activa a precio de costo
- Demora de activación: Para direcciones no activadas, la respuesta de la API puede demorar hasta 6 segundos debido al proceso de activación
- Las órdenes se procesan 24/7 con conmutación por error automática de proveedores
- Cantidad mínima de energía: 61,000 unidades
- Cantidad máxima de energía: 3,000,000 de unidades por orden
- Búfer de energía: +50 unidades agregadas automáticamente para compensación del proveedor (sin costo)
- Hash de transacción: El campo siempre está presente pero puede estar vacío si el proveedor no lo devuelve de inmediato. Para recuperar el hash, llame a /apiv2/order_check no antes de 1 minuto después de realizar la orden
- Selección de proveedores: Automática según el costo y la disponibilidad
- Formato del ID de orden:
1H{request_id}para seguimiento unificado - Precios: Dinámicos según la hora del día y la cantidad de energía
- Duración: Fija de 1 hora (3600 segundos)
- Límite de tasa: 50 peticiones por segundo por dirección IP