Skip to content

Learn

Exchange APIs

OKX API error 50102 "Timestamp request expired" and how to fix it

Figures on this page are as of 2026-09. Fees, limits and margin tiers change — check the venue's own docs.

OKX error 50102 comes back with HTTP 401 and the message "Timestamp request expired." It means the OK-ACCESS-TIMESTAMP header on your request is more than 30 seconds away from OKX’s server time, so OKX treats the request as stale and refuses it.

The fix is almost never in your signing code. OKX’s documentation names the most common culprit — a local timezone offset — and you can confirm or rule it out in a single request. This page is for anyone hand-rolling OKX V5 authentication, or debugging a library that does it for them.

What this error actually means

Every private REST request to OKX carries four headers (the full set is in the OKX passphrase and four-header model). One of them is OK-ACCESS-TIMESTAMP, and the same value is the first component of the string you sign:

timestamp + method + requestPath + body

The timestamp is there to stop replay: a captured request re-sent later falls outside the window, and because the timestamp is signed it cannot be edited in transit. (The general mechanism is in what is HMAC request signing.)

OKX states the window in its authentication section: the server rejects requests where the timestamp “differs from server time by more than 30 seconds (error 50102)”. Note the word differs. A timestamp too far in the future fails just as a stale one does.

The format matters as much as the value. OKX wants ISO 8601, in UTC, with milliseconds:

2020-12-08T09:08:57.715Z

That is unusual. Many HMAC-signed exchange APIs — Binance and Bybit among them — take an integer count of milliseconds since the Unix epoch; see clock sync and timestamps for the cross-venue picture. Code ported from one of those, or written from memory of them, is where OKX timestamps usually go wrong. A formatted string has more ways to be wrong than an integer does, and one of them is quiet.

The usual cause: local time labelled as UTC

OKX’s docs say local timezone offset is “the most common cause” of 50102. Here is how that happens.

The code takes the machine’s local wall-clock time, formats it as ISO 8601 and appends Z — or hands it to a formatter that assumes the value is already UTC. The result is a perfectly well-formed string that claims to be UTC but is actually local time. On a machine set to UTC+8, every timestamp is eight hours in the future. OKX compares that to its own clock, sees a gap measured in hours, and returns 50102.

The fingerprints are distinctive:

  • It fails on every request, not intermittently. A timezone offset is far larger than a 30-second window, so nothing ever squeaks through.
  • It works on one machine and fails on another — typically fine on a server configured for UTC, broken on a laptop that is not, or the reverse after a deploy.
  • The gap, when you measure it, is close to your UTC offset.

In Python, the bug and the fix look like this:

from datetime import datetime, timezone

# Wrong: local wall-clock time, labelled as UTC
ts = datetime.now().isoformat(timespec='milliseconds') + 'Z'

# Right: an aware UTC time, with milliseconds, in the docs' 'Z' form
ts = datetime.now(timezone.utc).isoformat(timespec='milliseconds')
ts = ts.replace('+00:00', 'Z')

In JavaScript, new Date().toISOString() already returns UTC with milliseconds and a trailing Z. Whatever the language, match the shape of OKX’s example exactly rather than relying on an equivalent form being accepted.

Other causes, in order of likelihood

1. The machine’s clock has drifted

Virtual machines after suspend or migration, containers inheriting a wrong host clock, and minimal images without NTP are the usual sources. Drift has to be substantial to cross 30 seconds, so it shows up as something that worked yesterday failing today — intermittently at first. The general remedies are in clock sync and timestamps.

2. The request was signed, then waited

The timestamp is set when you sign, not when the bytes leave. Anything that separates the two can push a correct timestamp out of the window:

  • a client-side rate limiter or queue that holds signed requests;
  • a job that signs a batch of requests up front and sends them over a minute;
  • a retry that re-sends the same signed request after a backoff.

The last is the subtle one: the original attempt was fine, the retry fails with 50102, and the log makes it look as though the clock broke between attempts. Re-sign on every attempt. And for order placement specifically, a retry needs more thought than a fresh timestamp — see why retrying a failed order is not safe.

3. REST and WebSocket share a timestamp helper

WebSocket login is signed differently. For the login message, OKX’s docs specify a timestamp in Unix epoch seconds — not ISO 8601 — with method always GET and requestPath always /users/self/verify, and the login expires 30 seconds after its timestamp. A single “make timestamp” helper shared between the two channels will be wrong for one of them. Keep them separate and name them for the channel.

50102 and its neighbours

Three codes in OKX’s error table are easy to confuse, and they point at different fixes:

CodeHTTPOKX’s messageWhat it points at
50102401“Timestamp request expired.”A timestamp that parses but is more than 30 s from server time
50112401“Invalid OK-ACCESS-TIMESTAMP.”The timestamp header itself is not acceptable
50113401“Invalid signature.”The signature does not match what OKX computed

Source: OKX’s V5 error-code table.

The docs do not spell out exactly which malformed strings produce 50112 rather than 50102. Practically it does not matter: if what you send is not ISO 8601 UTC with milliseconds, fix the format first, whichever code you got.

One timestamp bug lands on the signature code instead. If you generate the timestamp twice — once for the signature, once for the header — both values are inside the window, but they differ by a few milliseconds and the signature can never match. That is 50113 territory; see OKX API error 50113.

How to confirm which one you have

  1. Ask OKX what time it is. GET /api/v5/public/time is public and needs no key. OKX’s docs recommend synchronising against it before placing orders precisely to avoid 50102.
  2. Log the exact timestamp string your client sent — the header value, not a value you recompute afterwards.
  3. Convert both to the same representation and subtract.
  4. Read the gap:
    • Hours, close to your UTC offset → local time labelled as UTC.
    • Seconds to minutes, growing over days → clock drift; fix NTP.
    • Near zero at signing time, yet the request failed → the request sat somewhere before sending; look at queues and retries.
    • Near zero, but the string does not look like 2020-12-08T09:08:57.715Z → a format problem, which may be reported as 50112.

Do this check once at process startup and log the delta. It costs one request and turns a mystery into a number.

Correcting the system clock is the real fix. If you also apply an offset measured from /api/v5/public/time, re-measure it periodically and alert on a large one rather than absorbing it silently.

Why this should fail loudly

50102 does not clear on retry. Retrying with the same timestamp fails; retrying with a fresh timestamp from the same broken clock fails too. An integration that feeds it into a generic retry loop just burns rate limit. Treat it as a configuration fault that stops the process — the classification is laid out in error handling patterns for trading APIs. The same applies when a model is calling tools: it will naturally try again, and neither the model nor the retry can fix a clock.

FAQ

What does OKX error 50102 mean?

It means the OK-ACCESS-TIMESTAMP on your request differs from OKX’s server time by more than 30 seconds, so OKX rejects the request as expired. It comes back with HTTP 401 and the message “Timestamp request expired.” OKX’s docs name a local timezone offset as the most common cause.

Why does OKX return 50102 on my laptop but not on my server?

Most likely your code formats local time as if it were UTC. A server set to UTC hides the bug because local time and UTC coincide there; a machine in another timezone produces timestamps off by its UTC offset, far outside OKX’s 30-second window. Generate the timestamp from an explicit UTC clock.

What timestamp format does the OKX API require?

For REST, ISO 8601 in UTC with milliseconds, for example 2020-12-08T09:08:57.715Z, and the same value must appear in the signature and in the OK-ACCESS-TIMESTAMP header. WebSocket login is different: it uses Unix epoch seconds, and the login expires 30 seconds after that timestamp.

Will retrying fix OKX error 50102?

No. The cause is the clock or the timestamp format, and a retry reuses one or the other. Fix the timestamp generation or synchronise the clock — checking the gap against GET /api/v5/public/time shows which — and re-sign each request at the moment it is sent.