Binance 私有 API 报 -1021 / -1022:SIGNED 端点签名与时间戳排查
文档与源码:
- Binance Spot API 文档:
- 开发者站点:
请求 /api/v3/order 或 /api/v3/account 返回 400,body 里常见这几种:
{"code":-1021,"msg":"Timestamp for this request is outside of the recvWindow."}
{"code":-1022,"msg":"Signature for this request is not valid."}
{"code":-1100,"msg":"Illegal characters found in parameter 'signature'."}
SIGNED 端点需要什么
TRADE 与 USER_DATA 端点是 SIGNED 端点。除了业务参数,还必须带:
signature:放在 query string 或 request body 里。timestamp:请求创建/发送时的毫秒时间戳,也支持微秒。recvWindow:可选,指定timestamp之后请求有效的毫秒数。
signature 应放在参数列表最后。HMAC 产生的签名不区分大小写,RSA 与 Ed25519 签名区分大小写。签名是 hex 字符串,合法范围 ^[A-Fa-f0-9]{64}$,也就是 64 位 hex。长度或字符不对会报 -1100 Illegal characters found in parameter 'signature'。
签名算法:HMAC-SHA256 对 totalParams 签名
用 secretKey 作为 key,totalParams 作为待签值做 keyed HMAC。totalParams 是 query string 与 request body 的拼接:query string concatenated with request body。
以下单 LIMIT 为例,参数:
symbol=LTCBTC, side=BUY, type=LIMIT, timeInForce=GTC, quantity=1, price=0.1, recvWindow=5000, timestamp=1499827319559
拼成:
symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000×tamp=1499827319559
用 openssl 复现:
echo -n "symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000×tamp=1499827319559" | openssl dgst -sha256 -hmac "SECRET_KEY"
输出:
c8db56825ae71d6d79447849e617115f4a920fa2acdcab2b053c4b2838bd6b71
请求:
curl -H "X-MBX-APIKEY: $apiKey" -X POST 'https://api.binance.com/api/v3/order?symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000×tamp=1499827319559&signature=c8db56825ae71d6d79447849e617115f4a920fa2acdcab2b053c4b2838bd6b71'
Python 等价写法:
import hashlib
import hmac
secret = "SECRET_KEY"
total_params = (
"symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC"
"&quantity=1&price=0.1&recvWindow=5000×tamp=1499827319559"
)
signature = hmac.new(
secret.encode(), total_params.encode(), hashlib.sha256
).hexdigest()
print(signature)
# c8db56825ae71d6d79447849e617115f4a920fa2acdcab2b053c4b2838bd6b71
hexdigest() 默认输出小写 hex。
recvWindow 与服务端判定
recvWindow 不传时默认 5000 毫秒,最大 60000 毫秒,支持最多三位小数,例如 6000.346,用于精确到微秒。官方建议用小值,5000 或更小,上限不能超过 60000。
服务端判定逻辑:
serverTime = getCurrentTime()
if (timestamp "
如果服务器与本地时钟不同步,例如本地是 GMT+2,时间戳不一致会返回 400。工程上先取服务器时间:
curl "https://api.binance.com/api/v3/time"
用返回的 serverTime 校准本地偏移,再生成 timestamp。本地时钟同步可以交给 NTP,但不要假设它一定准。
常见错误对照
| 错误码 | 含义 | 常见原因 |
|---|---|---|
-1021 | Timestamp for this request is outside of the recvWindow | 发送的 timestamp 在 serverTime - recvWindow 之外;或 timestamp 比服务器时间超前超过 1000ms |
-1022 | Signature for this request is not valid | 某个参数格式不对,例如 recvWindow 必须是整数而不是字符串;参数拼接/顺序变化;代理改参数;secret 不对。python-binance FAQ 也提到可能需要重新生成 API Key 与 Secret |
-1100 | Illegal characters found in parameter 'signature' | signature 不是 64 位 hex,长度或字符不满足 ^[A-Fa-f0-9]{64}$ |
-2015 | Invalid API-key, IP, or permissions for action | key、IP 或权限不匹配 |
-1003 | Too many requests | 被限流 |
几个会让签名直接失效的边界
- 签名前统一按字典序或固定顺序拼接参数。顺序一变,
totalParams变,-1022。 - 不要把
secretKey放前端。签名必须在后端完成。 - 中间层代理或网关如果改动 query 参数、重编码 body,签名会失效,报
-1022。 - 本地时钟只用 NTP 校准还不够,SIGNED 请求应基于
/api/v3/time的serverTime计算偏移。 signature放在参数最后,但计算totalParams时不要把它拼进去。