POST /apiv2/order1h
通过多个能量供应商创建 1 小时能量租赁订单,支持自动故障转移。
端点 URL
POST https://netts.io/apiv2/order1h请求标头
| Header | Required | Description |
|---|---|---|
| Content-Type | 是 | application/json |
| X-API-KEY | 是 | 来自 Netts 控制台的 API 密钥 |
| X-Real-IP | 是 | 来自您白名单的 IP 地址 |
请求体
{
"amount": 131000,
"receiveAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE"
}请求参数
| Parameter | Type | Required | Description |
|---|---|---|---|
| amount | integer | 是 | 租赁的 Energy 数量(最小值: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": "a1b2c3d4e5f6789...",
"delegateAddress": "TDelegatePoolAddress...",
"energy": 131050
}
}
}响应字段
| 字段 | 类型 | 描述 |
|---|---|---|
| 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 | 交易哈希。该字段始终存在但可能为空 - 部分供应商不会立即返回哈希。1 分钟后请使用 /apiv2/order_check 获取哈希 |
| detail.data.delegateAddress | string | 代理能量的池地址 |
| 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"
}错误代码参考
| 代码 | 描述 | 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 秒窗口)
- Energy 数量
- 接收地址
- API 密钥
每个请求都有一个 1 秒的唯一性窗口。为保护系统免受滥用并确保正常处理,具有相同参数的请求发送频率不得高于每秒一次。
当前行为: 系统会自动保护客户端免受已订购能量的错误重试影响。如果您不小心发送了两次相同的请求,您将不会被重复扣费。
提供您自己的密钥
您可以通过发送 X-Idempotency-Key 请求头来自行控制幂等性。当该请求头存在时,仅凭该值决定请求是否为重复请求,不再使用上述自动组合。当它不存在时,一切照旧——由服务器为您推导密钥。
| Header | 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和编排器接受 16–64 个字符的 base64 密钥。此端点仅接受 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())),将使每次尝试都具有不同的密钥,因此超时后的重试会被当作第二笔订单接受并再次扣费。最简单的正确选择是您已有的订单 ID:它在首次尝试之前就已存在,并且在您的进程重启后依然保留。
# 一次性生成,当订单在您的系统中出现时
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": "b9e67b2412d33c92...",
"retry_after_seconds": 3
}建议: 在检查订单状态之前,请等待指定的 retry_after_seconds 秒数。
最佳实践
- 请勿使用相同参数发送并行请求 - 等待每次响应返回
- 为每笔新订单使用新的
nonce,并在其每次重试中使用首次尝试的nonce - 绝不要在发送时重新生成
nonce— 重试必须复现首次尝试的密钥,而不是生成新密钥 - 处理 409 响应的方式是等待,而不是立即重试
- 检查
idempotency.cached字段以识别缓存响应 —208意味着您刚刚发送的订单未被下达
注意事项
- 订单成功后立即交付 Energy(通常在 0.5-10 秒内)
- API 响应超时:最长 10 秒,通常在 2 秒内响应
- 地址激活:如果接收地址未激活,Netts 将按成本价自动激活
- 激活延迟:对于未激活的地址,由于激活流程,API 响应可能需要长达 6 秒
- 订单全天候 24/7 处理,支持供应商自动故障转移
- 最低 Energy 数量:61,000 单位
- 最高 Energy 数量:每笔订单 3,000,000 单位
- Energy 缓冲量:自动额外增加 +50 单位作为供应商补偿(免费提供)
- 交易哈希:该字段始终存在,但如果供应商未立即返回,可能为空。要获取哈希,请在下单后不早于 1 分钟 调用 /apiv2/order_check
- 供应商选择:根据成本和可用性自动选择
- 订单 ID 格式:
1H{request_id},用于统一跟踪 - 定价:根据一天中的时间和能量数量动态调整
- 时长:固定 1 小时(3600 秒)
- 速率限制:每个 IP 地址每秒 50 次请求