OKX API Passphrase Explained: Setup & Code Examples
Learn how to create and use an OKX API passphrase for secure trading automation. Includes Python and JavaScript code examples with authentication setup.
Learn how to create and use an OKX API passphrase for secure trading automation. Includes Python and JavaScript code examples with authentication setup.
If you've ever tried connecting a trading bot to OKX and hit a 401 error, the culprit is almost always the passphrase. Unlike Binance or Coinbase, which only require an API key and secret, OKX adds a third layer — a passphrase you set yourself when creating the API key. It's not optional, and it's not recoverable if you lose it. This guide walks through exactly what the passphrase is, how to set it up, and how to include it correctly in your requests.
When you create an API key on OKX, the platform asks you to define three things: an API Key, a Secret Key, and a Passphrase. The passphrase is a custom string you choose — think of it as a second password layered on top of your API credentials. OKX uses it as an additional HMAC signing input and requires it as a header in every authenticated request.
This is different from how Binance handles authentication, where you only need the API key in the header and sign requests with the secret. On OKX, the passphrase travels with every request as the OK-ACCESS-PASSPHRASE header. Forget to include it and you'll get an authentication failure immediately.
Critical: OKX does not store your passphrase anywhere retrievable. If you lose it, you must delete the API key and create a new one. Write it down in a password manager the moment you create it.
Log into your OKX account and navigate to the API management section under your profile settings. Click 'Create API Key' and you'll see fields for a label, passphrase, and permission toggles. The passphrase can be any string you choose — there's no enforced complexity requirement, but treat it like a strong password.
Platforms like Bybit and OKX both offer IP whitelisting for API keys, which is a strong security practice if your bot runs on a fixed server IP. Binance and Gate.io offer similar controls. Enable it whenever possible — it limits blast radius if your credentials are ever leaked.
OKX uses HMAC-SHA256 signatures for authenticated endpoints. The signature is built from a concatenation of the timestamp, HTTP method, request path, and request body. Your passphrase does not go into the signature calculation itself — it travels as a plain header. Here's the exact header set required:
| Header | Value | Notes |
|---|---|---|
| OK-ACCESS-KEY | Your API Key | From key creation |
| OK-ACCESS-SIGN | HMAC-SHA256 signature | Computed per request |
| OK-ACCESS-TIMESTAMP | ISO 8601 UTC timestamp | e.g. 2024-01-15T10:30:00.000Z |
| OK-ACCESS-PASSPHRASE | Your passphrase | Set during key creation |
| Content-Type | application/json | Required for POST requests |
The timestamp must be within 30 seconds of OKX server time. If your system clock drifts, you'll get timestamp errors that look like authentication failures. Always sync your server clock with NTP before running production bots.
Here's a complete Python example that builds the authentication headers, makes a signed request to fetch your account balance, and handles common errors:
import hmac
import hashlib
import base64
import time
import requests
from datetime import datetime, timezone
API_KEY = "your-api-key-here"
SECRET_KEY = "your-secret-key-here"
PASSPHRASE = "your-passphrase-here" # Set during key creation
BASE_URL = "https://www.okx.com"
def get_timestamp():
"""Return ISO 8601 UTC timestamp required by OKX."""
return datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%S.') + \
str(datetime.now(timezone.utc).microsecond // 1000).zfill(3) + 'Z'
def sign_request(timestamp, method, path, body=""):
"""Build HMAC-SHA256 signature for OKX request."""
message = timestamp + method.upper() + path + body
signature = hmac.new(
SECRET_KEY.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
).digest()
return base64.b64encode(signature).decode('utf-8')
def get_headers(method, path, body=""):
"""Build complete auth headers including passphrase."""
timestamp = get_timestamp()
return {
"OK-ACCESS-KEY": API_KEY,
"OK-ACCESS-SIGN": sign_request(timestamp, method, path, body),
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-PASSPHRASE": PASSPHRASE,
"Content-Type": "application/json"
}
def get_account_balance():
"""Fetch account balance from OKX."""
path = "/api/v5/account/balance"
headers = get_headers("GET", path)
try:
response = requests.get(BASE_URL + path, headers=headers, timeout=10)
response.raise_for_status()
data = response.json()
if data.get("code") != "0":
print(f"OKX API error: {data.get('msg')} (code: {data.get('code')})")
return None
return data["data"]
except requests.exceptions.Timeout:
print("Request timed out — check your internet connection")
except requests.exceptions.HTTPError as e:
print(f"HTTP error: {e.response.status_code} — likely wrong passphrase or expired key")
except Exception as e:
print(f"Unexpected error: {e}")
return None
if __name__ == "__main__":
balance = get_account_balance()
if balance:
for account in balance:
for detail in account.get("details", []):
if float(detail.get("availBal", 0)) > 0:
print(f"{detail['ccy']}: {detail['availBal']}")
For bot developers working in Node.js, here's the equivalent implementation using the built-in crypto module. This example places a limit order and shows how to handle the response:
const crypto = require('crypto');
const https = require('https');
const config = {
apiKey: 'your-api-key-here',
secretKey: 'your-secret-key-here',
passphrase: 'your-passphrase-here', // Set during key creation
baseUrl: 'www.okx.com'
};
function getTimestamp() {
return new Date().toISOString().replace(/\.\d{3}Z$/, '.') +
String(new Date().getMilliseconds()).padStart(3, '0') + 'Z';
}
function signRequest(timestamp, method, path, body = '') {
const message = `${timestamp}${method.toUpperCase()}${path}${body}`;
return crypto
.createHmac('sha256', config.secretKey)
.update(message)
.digest('base64');
}
function buildHeaders(method, path, body = '') {
const timestamp = getTimestamp();
return {
'OK-ACCESS-KEY': config.apiKey,
'OK-ACCESS-SIGN': signRequest(timestamp, method, path, body),
'OK-ACCESS-TIMESTAMP': timestamp,
'OK-ACCESS-PASSPHRASE': config.passphrase,
'Content-Type': 'application/json'
};
}
async function placeLimitOrder(instId, side, size, price) {
const path = '/api/v5/trade/order';
const body = JSON.stringify({
instId, // e.g. 'BTC-USDT'
tdMode: 'cash', // 'cash' for spot, 'cross' for margin
side, // 'buy' or 'sell'
ordType: 'limit',
sz: String(size),
px: String(price)
});
const headers = buildHeaders('POST', path, body);
return new Promise((resolve, reject) => {
const options = {
hostname: config.baseUrl,
path,
method: 'POST',
headers: { ...headers, 'Content-Length': Buffer.byteLength(body) }
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
const parsed = JSON.parse(data);
if (parsed.code !== '0') {
reject(new Error(`OKX error ${parsed.code}: ${parsed.msg}`));
} else {
resolve(parsed.data);
}
});
});
req.on('error', reject);
req.setTimeout(10000, () => { req.destroy(); reject(new Error('Timeout')); });
req.write(body);
req.end();
});
}
// Example usage
placeLimitOrder('BTC-USDT', 'buy', '0.001', '60000')
.then(data => console.log('Order placed:', data[0].ordId))
.catch(err => console.error('Order failed:', err.message));
Most OKX API authentication failures come down to a handful of root causes. Knowing them saves you hours of debugging:
Tip: Use OKX's demo trading environment (demo.okx.com) during development. It uses the same API structure but with paper money — you can safely test authentication without risking real funds. Bitget and Bybit offer similar sandbox environments.
If you're building a signal-driven bot, consider pairing your OKX connection with VoiceOfChain — a real-time trading signal platform that pushes alerts via WebSocket and Telegram. Instead of polling the market yourself, you receive structured signals with entry, stop-loss, and take-profit levels that your bot can execute directly through the OKX API.
Your API key, secret, and passphrase together grant full trading access to your account. Treat them accordingly:
import os
from dotenv import load_dotenv
# Load from .env file — never hardcode credentials
load_dotenv()
API_KEY = os.getenv('OKX_API_KEY')
SECRET_KEY = os.getenv('OKX_SECRET_KEY')
PASSPHRASE = os.getenv('OKX_PASSPHRASE')
if not all([API_KEY, SECRET_KEY, PASSPHRASE]):
raise EnvironmentError(
"Missing OKX credentials. Set OKX_API_KEY, OKX_SECRET_KEY, "
"and OKX_PASSPHRASE in your .env file."
)
The OKX API passphrase is a straightforward concept once you understand its role: it's a static authentication header that travels with every request, separate from the HMAC signature. The most common mistakes are losing it (unrecoverable), miscasing it, or missing it entirely from request headers. With the Python and JavaScript examples above, you have working authentication code you can adapt for your own trading bots — whether you're building a simple balance checker, an order execution engine, or a full automated strategy that feeds off real-time signals from VoiceOfChain. Store your credentials securely, use IP whitelisting, and keep your keys scoped to the minimum permissions your bot actually needs.