Signing OKEx API requests—especially POST requests—can be challenging, even if your GET requests work perfectly. Many developers encounter the frustrating {'msg': 'Invalid Sign', 'code': '50113'} error when trying to place orders via the /api/v5/trade/order endpoint. This guide walks you through the correct way to sign POST API requests for OKEx (now known as OKX), explains common pitfalls, and provides a working Python implementation that ensures your signature is accepted.
Whether you're building a trading bot, automating portfolio rebalancing, or integrating market data into your application, understanding how to properly authenticate with the OKX API is essential. We’ll cover everything from timestamp formatting to payload handling, ensuring your requests are securely and accurately signed.
Understanding OKX API Authentication
OKX uses HMAC-SHA256 signatures to authenticate private API endpoints. Every authenticated request must include the following headers:
OK-ACCESS-KEY: Your API keyOK-ACCESS-SIGN: The base64-encoded signatureOK-ACCESS-TIMESTAMP: UTC time in ISO format (e.g.,2025-04-05T12:34:56.789Z)OK-ACCESS-PASSPHRASE: The passphrase you set when creating the API key
The signature is generated by combining:
timestamp + method (uppercase) + request path + bodyThis string is then hashed using HMAC-SHA256 with your secret key and encoded in base64.
👉 Discover how to securely manage your API keys and start trading with confidence.
Why GET Works But POST Fails
A common issue developers face is that GET requests succeed, but POST requests return "Invalid Sign". The root cause often lies in how the request body is handled during signature generation.
For GET requests, there's typically no body or it’s empty, so the message string only includes timestamp, method, and endpoint. However, for POST requests:
- The body must be included as a JSON string in the signature message.
- It should not be URL-encoded or converted to a dictionary during hashing.
- Even an empty object
{}should be represented as an empty string''in the signature.
A subtle mismatch between how the body is serialized in the signature vs. how it's sent in the actual HTTP request can lead to signature verification failure.
Step-by-Step Guide to Signing POST Requests
Here’s a complete, tested Python function to sign and send both GET and POST requests to OKX:
import hmac
import json
import base64
import requests
import datetime as dt
# Replace these with your actual credentials
APIKEY = "your_api_key"
APISECRET = "your_api_secret"
PASS = "your_passphrase"
BASE_URL = 'https://www.okx.com'
def send_signed_request(http_method: str, url_path: str, payload: dict = None):
def get_timestamp():
return dt.datetime.utcnow().isoformat(fraction=False) + ".000Z"
def generate_signature(timestamp: str, method: str, request_path: str, body: str, secret: str):
if body is None or body == '{}' or body == 'null':
body = ''
message = timestamp + method.upper() + request_path + body
mac = hmac.new(
bytes(secret, encoding='utf-8'),
bytes(message, encoding='utf-8'),
digestmod='sha256'
)
return base64.b64encode(mac.digest()).decode()
timestamp = get_timestamp()
body_str = json.dumps(payload) if payload else ''
headers = {
'OK-ACCESS-KEY': APIKEY,
'OK-ACCESS-SIGN': generate_signature(timestamp, http_method, url_path, body_str, APISECRET),
'OK-ACCESS-TIMESTAMP': timestamp,
'OK-ACCESS-PASSPHRASE': PASS,
'Content-Type': 'application/json'
}
url = BASE_URL + url_path
if http_method.upper() == 'GET':
response = requests.get(url, headers=headers)
elif http_method.upper() == 'POST':
response = requests.post(url, headers=headers, data=body_str)
else:
raise ValueError("Unsupported HTTP method")
return response.json()Example: Placing a Market Buy Order
order_data = {
"instId": "BTC-USDT",
"tdMode": "cash",
"side": "buy",
"ordType": "market",
"sz": "0.001"
}
result = send_signed_request("POST", "/api/v5/trade/order", order_data)
print(result)This approach ensures consistent serialization of the payload both in the signature and in the actual request.
Common Mistakes and Fixes
| Issue | Solution |
|---|---|
Using json.dumps(body) only in request but not in signature | Serialize body once and use same string for both |
| Mismatched timestamps | Generate timestamp right before signing |
| Including query parameters in path incorrectly | Only use path (e.g., /api/v5/trade/order), not full URL |
Forgetting to set Content-Type: application/json | Always include this header |
👉 Learn how to automate trades safely using secure API practices.
Frequently Asked Questions (FAQ)
Q: What does error code 50113 mean?
A: Error code 50113 means "Invalid Sign". This indicates that the signature provided in the OK-ACCESS-SIGN header does not match what the server expects based on your timestamp, method, endpoint, and body.
Q: Should I include query parameters in the request path when signing?
A: Yes—if your request includes query parameters (e.g., /api/v5/account/balance?ccy=BTC), you must include them exactly as they appear in the URL path when generating the signature.
Q: Can I reuse the same timestamp for multiple requests?
A: No. Each request must have a unique timestamp within a few seconds of server time. Reusing timestamps may cause signature invalidation due to replay protection.
Q: Do I need to enable anything on my OKX account for API trading?
A: Yes. You must create an API key with sufficient permissions (e.g., "Trade" permission enabled) and ensure IP whitelisting allows your server's IP address.
Q: Is it safe to hardcode API keys in scripts?
A: No. Always store API keys securely using environment variables or secret management tools, especially in production environments.
Q: Does OKX support simulated or paper trading?
A: Yes. You can enable demo trading by adding the header 'x-simulated-trading': '1' to your requests. This allows testing without risking real funds.
Final Tips for Reliable Integration
- Use UTC time strictly: Always generate timestamps using UTC (
datetime.utcnow()). - Match body format exactly: The body used in signature generation must be identical to what's sent over HTTP.
- Test with sandbox first: OKX offers a demo environment—use it before going live.
- Handle rate limits: OKX enforces rate limits; design your app to respect them.
- Log requests carefully: Avoid logging sensitive data like secrets or full payloads.
With proper implementation, signing OKX POST API requests becomes straightforward and reliable.
👉 Start building powerful trading applications with real-time API access today.