All requests require HMAC-SHA256 signature:
import { createHmac } from 'crypto';
function sign(timestamp: string, method: string, path: string, body: string, secretKey: string): string {
const prehash = timestamp + method.toUpperCase() + path + (body || '');
return createHmac('sha256', secretKey).update(prehash).digest('base64');
}
// Headers for every request:
const headers = {
'OK-ACCESS-KEY': apiKey,
'OK-ACCESS-SIGN': sign(timestamp, method, path, body, secretKey),
'OK-ACCESS-TIMESTAMP': new Date().toISOString(),
'OK-ACCESS-PASSPHRASE': passphrase,
'Content-Type': 'application/json',
};GET /api/v5/dex/token/detail?chainIndex={chainId}&tokenContractAddress={address}
Response shape:
{
"data": [{
"tokenContractAddress": "0x...",
"tokenSymbol": "TOKEN",
"tokenName": "Token Name",
"decimals": "18",
"totalSupply": "1000000000000000000000000",
"holderCount": "1234",
"marketCap": "5000000"
}]
}GET /api/v5/dex/security/token?chainIndex={chainId}&tokenContractAddress={address}
Response shape:
{
"data": [{
"isHoneypot": false,
"ownerIsRenounced": true,
"buyTax": "0",
"sellTax": "0",
"isProxy": false,
"hasMintFunction": false,
"riskLevel": "low"
}]
}GET /api/v5/dex/market/price?chainIndex={chainId}&tokenContractAddress={address}
Response shape:
{
"data": [{
"price": "1.234",
"priceChange24h": "5.67",
"volume24h": "1000000",
"timestamp": "1712700000"
}]
}GET /api/v5/dex/market/candles?chainIndex={chainId}&tokenContractAddress={address}&bar=1H&limit=24
Response shape:
{
"data": [
["1712700000", "1.23", "1.25", "1.20", "1.24", "50000"]
]
}Fields: [timestamp, open, high, low, close, volume]
GET /api/v5/dex/signal/smart-money?chainIndex={chainId}&tokenContractAddress={address}
Response shape:
{
"data": [{
"walletAddress": "0x...",
"action": "buy",
"amount": "50000",
"timestamp": "1712700000",
"isKOL": true,
"winRate": "0.72"
}]
}GET /api/v5/dex/market/token/ranking?chainIndex={chainId}&sortBy=marketCap&limit=50
Used to find comparable tokens in similar market cap range.
GET /api/v5/dex/aggregator/swap?chainIndex={chainId}&fromTokenAddress={from}&toTokenAddress={to}&amount={amount}&slippage=0.03
Response shape:
{
"data": [{
"routerResult": {
"toTokenAmount": "999000",
"estimatedGas": "200000"
},
"tx": {
"to": "0x...",
"data": "0x...",
"value": "0",
"gasLimit": "250000"
}
}]
}GET /api/v5/dex/balance/token-balances?address={walletAddress}&chainIndex={chainId}
Response shape:
{
"data": [{
"tokenContractAddress": "0x...",
"tokenSymbol": "USDC",
"balance": "1000000000",
"balanceUsd": "1000"
}]
}| Chain | chainIndex |
|---|---|
| Ethereum | 1 |
| Arbitrum | 42161 |
| X Layer | 196 |
| Base | 8453 |
- 3 requests per second per API key
- Implement exponential backoff on 429 responses
- Cache responses with 30-second TTL for market data, 300-second for security checks
{
"code": "50011",
"msg": "Rate limit exceeded",
"data": []
}Common error codes:
- 50011: Rate limit
- 50001: Invalid API key
- 82000: Token not found
- 82100: Chain not supported
Map these to BidKitError with descriptive messages.
- All prices are strings in OKX responses — parse to number with parseFloat
- Amounts are in smallest unit — use BigInt for token amounts
- chainIndex vs chainId: OKX uses chainIndex which maps to standard chain IDs
- Security endpoint may not have data for brand new tokens — handle gracefully with default high-risk score
- Signal endpoint is newer — may return empty for low-activity tokens, treat as neutral (not negative)