◈   ⌘ api · Intermediate

OKX API Timestamp Format: A Trader's Complete Guide

Learn how OKX API timestamp format works, how to generate and sign requests correctly, and fix common authentication errors with real Python code examples.

Uncle Solieditor · voc · 05.05.2026 ·views 17
◈   Contents
  1. → What Is the OKX API Timestamp Format?
  2. → Generating the Correct OKX Timestamp in Python
  3. → Full OKX API Authentication Setup
  4. → Fixing Timestamp Errors and Clock Drift
  5. → Parsing OKX API Response Timestamps
  6. → Frequently Asked Questions
  7. → Conclusion

If you've ever connected to the OKX API and got smacked with a 50113 error — timestamp out of range — you know the frustration. OKX, like Binance and Bybit, enforces strict timestamp validation on every authenticated request. Get it wrong by even a few seconds and the entire request gets dropped. This guide breaks down exactly how OKX timestamps work, how to generate them correctly in Python, and how to build authentication that holds up in production without babysitting.

What Is the OKX API Timestamp Format?

OKX requires timestamps in Unix milliseconds — the number of milliseconds elapsed since January 1, 1970 (UTC). This is a 13-digit integer passed as a string in the OK-ACCESS-TIMESTAMP request header. A valid timestamp looks like '1714900000000'. The constraint: OKX rejects any request where the timestamp differs from server time by more than 30 seconds in either direction. No exceptions, no grace period.

This differs from other major exchanges. Coinbase Advanced Trade uses ISO 8601 format (e.g., '2024-05-05T12:00:00Z'). Binance uses Unix milliseconds but accepts them as integers. Bybit matches OKX — Unix milliseconds as a string. Gate.io uses seconds in some endpoints and milliseconds in others, which is its own special kind of chaos. OKX is at least consistent: always milliseconds, always a string, every single time.

Timestamp format comparison across major exchanges
ExchangeFormatExampleWindow
OKXUnix ms (string)"1714900000000"±30 seconds
BinanceUnix ms (integer)1714900000000±1000ms (recvWindow)
BybitUnix ms (string)"1714900000000"±5 seconds
Coinbase AdvancedISO 8601"2024-05-05T12:00:00Z"±30 seconds

Generating the Correct OKX Timestamp in Python

Generating a valid OKX timestamp takes one line. The recipe: multiply time.time() by 1000 to go from seconds to milliseconds, cast to int to drop the decimal, wrap in str() because OKX wants a string header value. Do this fresh on every single request — never cache or reuse.

import time

# OKX requires Unix timestamp in milliseconds as a string
def get_okx_timestamp():
    return str(int(time.time() * 1000))

# Example output: '1714900000000'
ts = get_okx_timestamp()
print(f"OKX timestamp: {ts}")
print(f"Length: {len(ts)} digits")  # Must always be 13 digits

# Quick sanity checks
assert len(ts) == 13, "Timestamp must be 13 digits (milliseconds, not seconds)"
assert ts.isdigit(), "Timestamp must be numeric digits only"

# Wrong approaches to avoid:
# str(time.time())           -> '1714900000.123' (float, wrong)
# str(int(time.time()))      -> '1714900000'     (seconds, 10 digits, wrong)
# datetime.utcnow().timestamp() -> same issue without int() cast
Never cache your timestamp. Generate a fresh one for every request. A timestamp from 31 seconds ago will be rejected with error 50113, even if your signature is perfectly valid.

Full OKX API Authentication Setup

Authenticated OKX requests require four headers: your API key, an HMAC-SHA256 signature, the timestamp, and your API passphrase (set when you created the key). The signature is built by concatenating timestamp + HTTP method + request path + request body, signing the result with your secret key using HMAC-SHA256, then base64-encoding it. Here's a complete working implementation:

import time
import hmac
import hashlib
import base64
import requests

API_KEY = "your_api_key"
SECRET_KEY = "your_secret_key"
PASSPHRASE = "your_passphrase"  # Set when you created the API key
BASE_URL = "https://www.okx.com"

def get_timestamp():
    return str(int(time.time() * 1000))

def sign_request(timestamp, method, path, body=""):
    """HMAC-SHA256 signature for OKX API."""
    prehash = timestamp + method.upper() + path + body
    mac = hmac.new(
        bytes(SECRET_KEY, encoding="utf-8"),
        bytes(prehash, encoding="utf-8"),
        digestmod=hashlib.sha256
    )
    return base64.b64encode(mac.digest()).decode()

def build_headers(method, path, body=""):
    ts = get_timestamp()  # Fresh timestamp every call
    return {
        "OK-ACCESS-KEY": API_KEY,
        "OK-ACCESS-SIGN": sign_request(ts, method, path, body),
        "OK-ACCESS-TIMESTAMP": ts,
        "OK-ACCESS-PASSPHRASE": PASSPHRASE,
        "Content-Type": "application/json"
    }

# Fetch account balance
path = "/api/v5/account/balance"
response = requests.get(
    BASE_URL + path,
    headers=build_headers("GET", path)
)
data = response.json()

if data["code"] == "0":
    print("Auth successful")
    total_eq = data["data"][0]["totalEq"]
    print(f"Total equity: ${float(total_eq):,.2f} USD")
else:
    print(f"Error {data['code']}: {data['msg']}")

# Example with query parameters — include them in the path for signing
path_with_params = "/api/v5/trade/orders-pending?instType=SPOT"
response = requests.get(
    BASE_URL + path_with_params,
    headers=build_headers("GET", path_with_params)
)

The critical detail that trips up developers migrating from Bybit or Binance: query parameters must be included in the path string when computing the signature. If your endpoint is /api/v5/trade/orders-pending?instType=SPOT, that full string goes into the prehash — not just the base path. Miss this and you'll get error 50113 or 50102 even with a perfectly formatted timestamp.

Fixing Timestamp Errors and Clock Drift

Error 50113 'OK-ACCESS-TIMESTAMP Invalid' is the most common OKX auth error for new bot developers. It breaks down to three root causes: your system clock has drifted more than 30 seconds, you're passing seconds instead of milliseconds, or you're reusing a stale timestamp. Clock drift is especially common on cloud VPS instances — AWS, DigitalOcean, and Vultr servers can all silently drift if NTP isn't configured properly.

import time
import requests

BASE_URL = "https://www.okx.com"

def get_server_clock_offset():
    """Measure difference between local clock and OKX server time in ms."""
    r = requests.get(f"{BASE_URL}/api/v5/public/time")
    server_ts = int(r.json()["data"][0]["ts"])
    local_ts = int(time.time() * 1000)
    return server_ts - local_ts

def get_corrected_timestamp(offset_ms=0):
    """Timestamp adjusted for measured clock drift."""
    return str(int(time.time() * 1000) + offset_ms)

# Run at bot startup
clock_offset = get_server_clock_offset()
print(f"Clock offset vs OKX server: {clock_offset:+d}ms")

if abs(clock_offset) > 5000:
    print("CRITICAL: Clock drift exceeds 5 seconds. Fix NTP sync before trading.")
elif abs(clock_offset) > 1000:
    print(f"Minor drift ({clock_offset}ms). Applying software correction.")
else:
    print("Clock in sync with OKX server.")

# Apply correction in your build_headers function:
# Replace: ts = get_timestamp()
# With:    ts = get_corrected_timestamp(clock_offset)
#
# Re-sync every 30 minutes for long-running bots:
# clock_offset = get_server_clock_offset()
On Binance you can configure a recvWindow parameter to extend the allowed timestamp window up to 60 seconds. OKX has no equivalent — 30 seconds is fixed. If you share authentication code between exchange connectors, don't assume the same tolerance applies.

Parsing OKX API Response Timestamps

OKX API responses return timestamps in the 'ts' field as a string of Unix milliseconds — the same format you send. When building a signal aggregator, trade logger, or backtesting pipeline, you'll need to convert these into human-readable datetimes. VoiceOfChain, for example, processes real-time OKX market data to generate trading signals, and precise timestamp parsing is essential for correlating signal triggers with actual market conditions at that moment.

import requests
from datetime import datetime, timezone

BASE_URL = "https://www.okx.com"

def parse_okx_ts(ts_str):
    """Convert OKX timestamp string to UTC datetime."""
    return datetime.fromtimestamp(int(ts_str) / 1000, tz=timezone.utc)

# Fetch recent trades for BTC-USDT (public endpoint, no auth needed)
url = f"{BASE_URL}/api/v5/market/trades"
params = {"instId": "BTC-USDT", "limit": "5"}
response = requests.get(url, params=params)
trades = response.json()["data"]

print("Recent BTC-USDT trades on OKX:")
for trade in trades:
    dt = parse_okx_ts(trade["ts"])
    price = float(trade["px"])
    size = float(trade["sz"])
    side = trade["side"].upper()
    # Millisecond-precision timestamp formatting
    ts_str = dt.strftime("%H:%M:%S.") + f"{dt.microsecond // 1000:03d}"
    print(f"  {ts_str} UTC | {side:4s} | {size:.4f} BTC @ ${price:,.2f}")

# Check trade freshness
latest_ts = int(trades[0]["ts"])
current_ts = int(__import__('time').time() * 1000)
lag_ms = current_ts - latest_ts
print(f"\nLatest trade lag: {lag_ms}ms from now")

# Candlestick note: OKX candle 'ts' is the OPEN time of the candle
# A 1m candle with ts=1714900000000 covers:
#   open:  1714900000000
#   close: 1714900059999

One critical gotcha: OKX candlestick data from /api/v5/market/candles uses the candle open time in the ts field, not the close time. This catches a lot of developers building backtesting systems who assume the timestamp marks when the candle closed. When ts=1714900000000 on a 1-minute candle, that bar covers the 60 seconds starting at that timestamp — factor this in when aligning candle data with trade signals.

Frequently Asked Questions

What timestamp format does OKX API use?
OKX uses Unix milliseconds formatted as a string — a 13-digit number like '1714900000000' passed in the OK-ACCESS-TIMESTAMP header. Unlike Coinbase which uses ISO 8601, or Binance which accepts the milliseconds as an integer, OKX specifically wants a numeric string.
What causes OKX API error 50113?
Error 50113 means your timestamp is outside OKX's allowed ±30 second window. The three most common causes are a drifted system clock, passing seconds instead of milliseconds (10 digits instead of 13), or reusing a cached timestamp from a previous request. Generate a fresh timestamp inline for every single request.
Does the OKX API timestamp need to be in UTC?
Unix timestamps are inherently UTC by definition — they count milliseconds since January 1, 1970 00:00:00 UTC regardless of your local system timezone. As long as you use time.time() in Python or Date.now() in JavaScript, you get UTC-based milliseconds automatically without any conversion.
How do I sync my trading bot's clock with OKX server time?
Call GET /api/v5/public/time (no authentication required) to get OKX's current server timestamp, subtract your local time.time() * 1000, and store the offset. Apply this offset when generating timestamps for authenticated requests. Re-sync every 30 minutes for long-running bots.
Is the OKX timestamp format the same for futures and spot?
Yes, the OK-ACCESS-TIMESTAMP format is identical across all OKX products — spot, futures, perpetual swaps, and options all use the same millisecond string format. The entire authentication flow (key, sign, timestamp, passphrase headers) is identical across product types.
Can I use Python's datetime module instead of time.time() for OKX timestamps?
Yes, but it's more verbose. Use datetime.now(timezone.utc).timestamp() * 1000 cast to int and string. The time.time() approach is simpler and equivalent. Avoid datetime.now() without a timezone — it gives local time and will produce wrong results if your server isn't set to UTC.

Conclusion

OKX's timestamp requirement is strict but entirely predictable — once you lock in the pattern (Unix milliseconds as a string, generated fresh per request, within 30 seconds of server time), authentication stops being a source of bugs. The clock offset check at startup, the inline timestamp generation, and the response timestamp parsing patterns covered here transfer directly to any production trading system. Platforms like VoiceOfChain build on this exact foundation to process real-time OKX, Binance, and Bybit market data for live trading signals. The timestamp is the handshake — get it right and the API opens up completely.

◈   more on this topic
◉ basics Mastering the ccxt library documentation for crypto traders ⌂ exchanges Mastering the Binance CCXT Library for Crypto Traders ⌬ bots Best Crypto Trading Bots 2025: Profitable AI-Powered Strategies