You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用Pionex API下单时出现INVALID_SIGNATURE错误求助

问题:Pionex API POST下单请求返回INVALID_SIGNATURE错误

调用Pionex API进行GET查询可正常获取结果,但提交POST请求下单时收到以下错误:

{'result': False, 'timestamp': 1702144786335, 'code': 'INVALID_SIGNATURE', 'message': 'invalid signature'}


正常运行的GET查询代码

import requests
import time
import json
import hashlib
import hmac
from urllib.parse import urlencode

api_key = "my api key"
api_secret = "my api secret"

# Function to generate the PIONEX-SIGNATURE
def generate_signature(api_secret, method, path, timestamp, params=None, data=None):
    # Set query parameters as key-value pairs: key=value
    if params is None:
        params = {}
    params['timestamp'] = timestamp
    query_string = urlencode(sorted(params.items()))

    # Concatenate query parameters after PATH with ?
    path_url = f"{path}?{query_string}"

    # Concatenate METHOD and PATH_URL
    message = f"{method.upper()}{path_url}"

    # Concatenate related entity body of POST and DELETE after step 5
    if data is not None:
        message += json.dumps(data, separators=(',', ':'))

    print(message)
    # Use API Secret and the above result to generate HMAC SHA256 code, then convert it to hexadecimal
    signature = hmac.new(api_secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()

    return signature

def make_private_request(method, endpoint, params=None, data=None):
    url = f"https://api.pionex.com{endpoint}"

    # Get current millisecond timestamp
    timestamp = str(int(time.time() * 1000))

    # Set timestamp in params
    if params is None:
        params = {}
    params['timestamp'] = timestamp

    # Set headers
    headers = {
        'PIONEX-KEY': api_key,
        'PIONEX-SIGNATURE': generate_signature(api_secret, method, endpoint, timestamp, params=params, data=data),
        'Content-Type': 'application/json',
    }

    # Make the request
    if method == 'GET':
        response = requests.get(url, headers=headers, params=params)
    elif method == 'POST':
        headers['Content-Type'] = 'application/json'
        response = requests.post(url, headers=headers, json=data, params=params )
    elif method == 'DELETE':
        response = requests.delete(url, headers=headers, json=data, params=params)
    else:
        raise ValueError(f"Unsupported HTTP method: {method}")

    print(f"Generated Signature: {headers['PIONEX-SIGNATURE']}")
    print(f"Generated Message: {method.upper()}{url}?{urlencode(sorted(params.items()))}")
    print(f"Server Timestamp: {response.json().get('timestamp')}")

    return response.json()


endpoint = "/api/v1/trade/order"
order_data = {
    "clientOrderId": "9e3d93d6-e9a4-465a-a39c-2e48568fe194",
    "symbol": "BTC_USDT",
    "side": "BUY",
    "type": "LIMIT",
    "size": "0.1",
    "price": "30000",
    "IOC": True
}

response = make_private_request('GET', '/api/v1/trade/allOrders', params={'symbol': 'BTC_USDT', 'limit': 1})

print(response)

GET查询代码返回结果

GET/api/v1/trade/allOrders?limit=1&symbol=BTC_USDT&timestamp=1702145234971
Generated Signature: f34cb0064bf618d64181d7b2afb64f24f63872f38eed8f7ee8683c34c6e8a86d
Generated Message: GEThttps://api.pionex.com/api/v1/trade/allOrders?limit=1&symbol=BTC_USDT&timestamp=1702145234971
Server Timestamp: 1702145234500
{'result': True, 'data': {'orders': []}, 'timestamp': 1702145234500}

出现签名错误的POST下单代码

import requests
import time
import json
import hashlib
import hmac
from urllib.parse import urlencode


api_key = "my api key"
api_secret = "my api secret"


def get_trade_price(api_key, secret_key, symbol):
    # Endpoint voor het verkrijgen van handelsinformatie
    endpoint = 'https://api.pionex.com/api/v1/market/tickers'

    # Parameters voor het verzoek
    params = {
        'symbol': symbol
    }

    # Voeg timestamp en API-sleutel toe aan de parameters
    params['timestamp'] = int(time.time() * 1000)
    params['apiKey'] = api_key

    # Creëer de handtekening (signature) voor het verzoek
    query_string = '&'.join([f"{key}={params[key]}" for key in sorted(params.keys())])
    signature = hmac.new(secret_key.encode(), query_string.encode(), hashlib.sha256).hexdigest()
    params['signature'] = signature

    # Maak het verzoek naar de Pionex API
    response = requests.get(endpoint, params=params)

    # Controleer of het verzoek succesvol was (statuscode 200)
    if response.status_code == 200:
        # Decodeer de JSON-response
        data = response.json()

        # Controleer de structuur van de ontvangen gegevens
        tickers = data['data']['tickers']
        for ticker in tickers:
            if ticker['symbol'] == symbol:
                # Haal de handelsprijs op
                trade_price = ticker.get('close')

                # Druk de handelsprijs af
                return trade_price
    else:
        print(f"Fout bij het ophalen van de handelsprijs. Statuscode: {response.status_code}")
        print(response.text)
        return None
# Function to generate the PIONEX-SIGNATURE
def generate_signature(api_secret, method, path, timestamp, params=None, data=None):
    # Set query parameters as key-value pairs: key=value
    if params is None:
        params = {}
    params['timestamp'] = timestamp
    query_string = urlencode(sorted(params.items()))

    # Concatenate query parameters after PATH with ?
    path_url = f"{path}?{query_string}"

    # Concatenate METHOD and PATH_URL
    message = f"{method.upper()}{path_url}"

    # Concatenate related entity body of POST and DELETE after step 5
    if data is not None:
        message += json.dumps(data, separators=(',', ':'))

    print(message)
    # Use API Secret and the above result to generate HMAC SHA256 code, then convert it to hexadecimal
    signature = hmac.new(api_secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()

    return signature

def make_private_request(method, endpoint, params=None, data=None, payload=None):
    url = f"https://api.pionex.com{endpoint}"

    # Get current millisecond timestamp
    timestamp = str(int(time.time() * 1000))

    # Set timestamp in params
    if params is None:
        params = {}
    params['timestamp'] = timestamp

    # Set headers
    headers = {
        'PIONEX-KEY': api_key,
        'PIONEX-SIGNATURE': generate_signature(api_secret, method, endpoint, timestamp, params=params, data=data),
        'Content-Type': 'application/json',
    }

    # Make the request
    if method == 'GET':
        response = requests.get(url, headers=headers, params=params)
    elif method == 'POST':
        headers['Content-Type'] = 'application/json'
        response = requests.post(url + f"?timestamp={timestamp}", headers=headers, json=data)
    elif method == 'DELETE':
        response = requests.delete(url, headers=headers, json=data, params=params)
    else:
        raise ValueError(f"Unsupported HTTP method: {method}")

    print(f"Generated Signature: {headers['PIONEX-SIGNATURE']}")
    print(f"Generated Message: {method.upper()}{url}?{urlencode(sorted(params.items()))}")
    print(f"Server Timestamp: {response.json().get('timestamp')}")

    return response.json()

trading_price = get_trade_price(api_key, api_secret, "BTC_USDT")
endpoint = "/api/v1/trade/order"
order_data = {
    "symbol": "BTC_USDT",
}


response = make_private_request('POST', endpoint, data=order_data)


print(response)

POST下单代码返回结果

POST/api/v1/trade/order?timestamp=1702145331250{"symbol":"BTC_USDT"}
Generated Signature: d404d623898e46ba61d37c86245594f2885a2160cbf401bd1c3ed081a04494af
Generated Message: POSThttps://api.pionex.com/api/v1/trade/order?timestamp=1702145331250
Server Timestamp: 1702145330729
{'result': False, 'timestamp': 1702145330729, 'code': 'INVALID_SIGNATURE', 'message': 'invalid signature'}

问题原因与解决方法

核心问题

签名生成时使用了无空格的JSON字符串(json.dumps(data, separators=(',', ':'))),但发送POST请求时使用requests.post(..., json=data)参数,会自动生成带空格的JSON字符串(默认分隔符为, 和: )。两者内容不一致,导致服务器验证签名失败。

同时,POST请求手动拼接URL的方式存在潜在的参数传递风险,应统一使用params参数传递查询参数。

修复后的代码修改点

修改make_private_request函数中的POST请求逻辑:

elif method == 'POST':
    headers['Content-Type'] = 'application/json'
    # 手动生成与签名时完全一致的无空格JSON字符串
    json_payload = json.dumps(data, separators=(',', ':'))
    # 使用params参数传递查询参数,避免手动拼接URL
    response = requests.post(url, headers=headers, data=json_payload, params=params)

额外注意事项

下单请求的order_data需要包含API要求的完整参数(如side、type、size等),否则签名验证通过后仍会返回参数错误。示例完整订单数据:

order_data = {
    "clientOrderId": "9e3d93d6-e9a4-465a-a39c-2e48568fe194",
    "symbol": "BTC_USDT",
    "side": "BUY",
    "type": "LIMIT",
    "size": "0.1",
    "price": "30000",
    "IOC": True
}

内容的提问来源于stack exchange,提问作者Devi Riet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 04:47:32