文档目录

身份鉴权

每个需要鉴权的 API 请求都必须使用您的 API 密钥私钥(Secret)进行签名。私钥本身绝不在网络上传输:服务端使用相同的参数重新计算签名并比对验证。

开始之前

凭证类型格式示例适用场景有效期
API Key + Secretak_live_… / sk_live_…(Nile 测试网为 ak_test_… / sk_test_…)后端发起的每次 API 请求永久有效,直到主动撤销或到达 expires_at
Bootstrap tokenAuthorization: Bearer abt_…首次注册创建账户、查询充值地址、GET /v1/account 以及创建第一个 API Key —— 仅限这些操作15 分钟
无凭证(公开)—GET /v1/prices、GET /v1/estimate、GET /v1/orderbook、GET /v1/resources/{address} 以及注册挑战挑战字串按源 IP 频控限制

API 密钥是机器凭证:切勿在前端网页中暴露使用。在首次充值前即可创建密钥,以便您的程序自动获取充值地址(GET /v1/deposit-addresses)并自主充值;在账户余额充足以支付订单前,下单请求将返回 4001 insufficient_funds。密钥的 Secret 仅在创建时展示一次,请妥善保存。

三个请求头

请求头取值要求
X-API-KEYAPI 密钥 ID,与控制台展示的一致。非机密。
X-API-TIMESTAMP当前 UTC 时间,采用精确到毫秒的 ISO 8601 格式,例如 2026-09-11T18:04:05.123Z。
X-API-SIGNbase64(HMAC_SHA256(api_secret, canonical_string))

规范字符串 (Canonical String)

text
canonical_string = timestamp + METHOD + path + query + body
组成部分严格要求
timestampX-API-TIMESTAMP 的完整字符串值,逐字节完全一致。
METHOD全大写 HTTP 方法:GET、POST、PATCH、DELETE。
path包含 /v1 前缀的请求路径,必须与实际发送的 URL 编码一致,例如 /v1/orders。
query无查询参数时为 ""(空字符串);有参数时必须包含开头的 ? 以及与网络请求完全相同的原始查询字符串 —— 切勿重新排序或重复编码。
bodyUTF-8 编码的原始请求体字节串;无请求体(如 GET 请求)时为 ""(空字符串)。

各部分之间没有任何分隔符。请确保请求体序列化一次后,用完全相同的字节串同时用于签名和网络传输:使用格式化缩进(pretty-print)的 JSON 进行签名,却发送紧凑压缩(compact)的 JSON,是导致 1002 invalid_signature 最常见的原因。

步骤 1 — 构造并签名字符串

以时间 2026-09-11T18:04:05.123Z、密钥私钥 sk_test_example 对 GET /v1/balance 进行签名:

text
2026-09-11T18:04:05.123ZGET/v1/balance
TS=2026-09-11T18:04:05.123Z
printf '%s' "${TS}GET/v1/balance" | openssl dgst -sha256 -hmac "sk_test_example" -binary | base64
import { createHmac } from "node:crypto";

const ts = "2026-09-11T18:04:05.123Z";
const sign = createHmac("sha256", "sk_test_example").update(`${ts}GET/v1/balance`).digest("base64");
console.log(sign);
import base64, hashlib, hmac

ts = "2026-09-11T18:04:05.123Z"
digest = hmac.new(b"sk_test_example", f"{ts}GET/v1/balance".encode(), hashlib.sha256).digest()
print(base64.b64encode(digest).decode())

上述三种方式均输出 zqttXJ133TRJ7aj14dJ3jamfeCG2mDj+TbNPrrBJl2g=。在排查任何其他错误之前,请先比对您的程序是否生成相同结果。

步骤 2 — 发送请求

TS=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)
SIGN=$(printf '%s' "${TS}GET/v1/balance" | openssl dgst -sha256 -hmac "$TENERGY_SECRET" -binary | base64)
curl -s https://api-nile.tenergy.me/v1/balance \
  -H "X-API-KEY: $TENERGY_KEY" -H "X-API-TIMESTAMP: $TS" -H "X-API-SIGN: $SIGN"
import { createHmac } from "node:crypto";

const ts = new Date().toISOString();
const sign = createHmac("sha256", process.env.TENERGY_SECRET!)
  .update(`${ts}GET/v1/balance`)
  .digest("base64");
const res = await fetch("https://api-nile.tenergy.me/v1/balance", {
  headers: { "X-API-KEY": process.env.TENERGY_KEY!, "X-API-TIMESTAMP": ts, "X-API-SIGN": sign },
});
console.log(res.status, await res.json());
import base64, hashlib, hmac, json, os, urllib.error, urllib.request
from datetime import datetime, timezone

ts = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
sign = base64.b64encode(hmac.new(os.environ["TENERGY_SECRET"].encode(),
                                 f"{ts}GET/v1/balance".encode(), hashlib.sha256).digest()).decode()
req = urllib.request.Request("https://api-nile.tenergy.me/v1/balance", headers={
    "X-API-KEY": os.environ["TENERGY_KEY"], "X-API-TIMESTAMP": ts, "X-API-SIGN": sign})
try:
    with urllib.request.urlopen(req) as res:
        print(res.status, json.load(res))
except urllib.error.HTTPError as err:
    print(err.code, json.load(err))

快速上手指南 将这一签名逻辑封装成了通用的 tenergy(method, path, body) 辅助函数,方便直接调用。

时钟偏移、防重放与 IP 规则

规则标准要求违反时的错误响应
时钟偏移 (Clock skew)与服务器时钟相差不超过 ±5 秒1003 signature_timestamp_skew —— details.server_time 将返回服务器标准时间
防重放 (Replay)在该时间窗口内,每个签名仅允许使用一次1009 replayed_signature —— 每次请求(包括重试)都必须生成全新的时间戳和签名
IP 白名单单个密钥可选配置;留空表示允许任意 IP1004 ip_not_allowed —— details.source_ip 将返回我们识别到的来源 IP
环境隔离主网域名仅接受 ak_live_ 密钥,Nile 测试网仅接受 ak_test_ 密钥在查询密钥之前即直接拒绝,返回 1012 key_environment_mismatch

请在发起签名的服务器上开启 NTP 自动时间同步。时钟漂移会导致所有请求直接返回 1003;单纯扩大重试次数无法解决问题。

权限范围 (Scopes)

API 密钥只具备创建时显式勾选的权限范围 —— scopes 是必填字段,系统不存在默认权限集,也不会在任何地方进行预选。权限名称遵循标准的 area.action 命名规范(详见 openapi.yaml 中的 ApiKeyScope);API 密钥参考 详细列出了每个权限的具体作用。超出密钥权限的调用将返回 403 及 1005 insufficient_scope,并在 details.required_scope 中指出所缺失的权限名称。请仅申请业务实际需要的最小权限。

幂等性保证

请求接口幂等控制字段重复调用时的行为
POST /v1/orders请求体中的 client_order_id返回原始订单结果,HTTP 200,绝不重复扣费
其他写操作接口请求头 Idempotency-Key,长度 8–128 字符返回原始执行结果

幂等记录保留 24 小时。使用相同的幂等键但提交不同的请求体,将被系统拒绝并返回 3010 idempotency_conflict。

下一步

  • 快速上手指南— 在实战中使用签名函数,完成从价格估算到订单确认的全流程。
  • 错误码与频控— 1xxx 错误码的详细说明及应对策略。
  • 网络与环境— 主网与 Nile 测试网的域名与密钥对应关系。

    ↑ ↓ 切换 · Enter 打开 · Esc 关闭