OKX API passphrase and the four-header model
Figures on this page are as of 2026-09. Fees, limits and margin tiers change — check the venue's own docs before acting on a number.
Two things about OKX’s API are unusual enough that people coming from other exchanges get caught by both: there is a fourth credential, and demo trading is a header rather than a hostname.
Neither is complicated. They are just not where you expect them.
The four headers
Every private REST request carries:
| Header | Content |
|---|---|
OK-ACCESS-KEY | API key |
OK-ACCESS-SIGN | Base64-encoded HMAC-SHA256 signature |
OK-ACCESS-TIMESTAMP | ISO 8601 UTC with milliseconds, e.g. 2020-12-08T09:08:57.715Z |
OK-ACCESS-PASSPHRASE | The passphrase created at API key setup |
The passphrase is the one with no equivalent elsewhere. Binance uses a key and a signature; Bybit uses a key, timestamp, signature and recv window. Neither has a third secret you chose yourself at creation time.
Consequences worth knowing:
- You choose it, and it is not recoverable. It is not generated and shown like the secret; you type it. If you lose it, the key is unusable and must be replaced.
- Swapping passphrase and secret is a common mistake, because there is no slot for it in a mental model built on two credentials. It produces an authentication failure that looks like a signing bug.
- It is a credential. Store it with the same care as the secret, not in a config comment.
The signature
Pre-hash string: timestamp + method + requestPath + body
Sign with HMAC-SHA256 using the secret key, then Base64-encode.
OKX’s own example:
sign = CryptoJS.enc.Base64.stringify(
CryptoJS.HmacSHA256(
timestamp + 'GET' + '/api/v5/account/balance?ccy=BTC',
SecretKey
)
)
Note what is included: the HTTP method, uppercase, and the request path with its query string. Bybit includes neither; Binance’s construction is different again. Signing code does not port between these venues.
Demo trading is a header
| Purpose | Access |
|---|---|
| Production REST | https://openapi.okx.com |
| Demo trading | Same host, with x-simulated-trading: 1 |
| US & AU users | us.okx.com |
| EU users | eea.okx.com |
This is the arrangement to be most careful with. On Binance, Bybit and Hyperliquid, testnet is a different hostname — point at the wrong one and the credentials do not exist there, so you get an authentication error rather than a live order. On OKX, production is what you get by omitting a header.
Ways the header goes missing without you removing it: a client library that does not forward custom headers; a proxy that strips unknown ones; a retry path that rebuilds the request from parts; a code path that constructs requests differently from the one you tested.
The related diagnostic trap: using demo credentials against production surfaces as an authentication error, not an environment error. So the message points you at your key and signature when the actual problem is a missing header.
If you are debugging an OKX auth failure, check the header before the signature. It is faster to rule out and more often the answer than its reputation suggests.
Clock sync
OKX rejects requests whose timestamp differs from server time by more than 30
seconds, returning 50102. The docs recommend synchronising via
GET /api/v5/public/time before placing orders.
The timestamp format is stricter than most: ISO 8601 UTC with milliseconds. A timestamp without the millisecond component, or in a local timezone, fails. And the same value must go into both the signature and the header — generating it twice produces two different strings and a signature that cannot match.
Signing checklist
When OK-ACCESS-SIGN is rejected, work through these in order:
- Method uppercase in the pre-hash string.
- Request path includes the query string for GET.
- Body is the exact raw JSON sent — empty string for GET, and byte-identical to what goes on the wire for POST.
- Timestamp is the same value in the signature and the header.
- Timestamp format is ISO 8601 UTC with milliseconds.
- Base64 after HMAC, not hex.
- Passphrase and secret are not swapped.
x-simulated-trading: 1present if using demo credentials.- Correct domain — signing for
okx.comwhile calling a legacyokex.comhost fails.
Item 2 is the one that produces the maddening version of this bug: some calls work and others do not, because query-parameter ordering or encoding differs between the string you signed and the URL you actually requested. If your failures are endpoint-specific rather than universal, look there first.
FAQ
What is the OKX API passphrase and where do I find it?
It is a fourth credential you choose when creating the API key, sent as the
OK-ACCESS-PASSPHRASE header alongside key, signature and timestamp. It is not
generated for you and not recoverable — if you did not record it at creation,
the key must be replaced.
Why does my OKX demo key fail against the API?
Most likely a missing x-simulated-trading: 1 header. OKX uses the same
hostname for demo and production, so the environment is carried by the header
rather than the URL. Using demo credentials without it reaches production, where
they do not exist, and the failure presents as an authentication error.
What does OKX error 50102 mean?
Your request timestamp differs from OKX’s server time by more than 30 seconds.
Sync your clock, and consider calling GET /api/v5/public/time before placing
orders. Check the format too — ISO 8601 UTC with milliseconds is required.
Why do some of my OKX requests authenticate and others do not?
Usually query-parameter ordering or encoding differing between the path you
signed and the path you requested. The pre-hash string uses
timestamp + method + requestPath + body, and requestPath must include the
query string exactly as sent. Logging the signed string and comparing it to the
outgoing URL finds this quickly.