Clock sync and timestamp errors
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.
Exchange APIs reject signed requests whose timestamp falls outside a narrow window. When your clock drifts, you get authentication errors — and they look exactly like signature bugs, which is where people spend the next two hours.
The tell is that nothing in your code changed.
Why the timestamp is there
A signed request that could be replayed indefinitely is a signed request an attacker can reuse. The timestamp bounds that: the venue accepts the request only if it arrived within a short window of when it claims to have been created, and the timestamp is inside the signed material so it cannot be edited in transit.
Which means a timestamp problem breaks authentication rather than producing a “your clock is wrong” message.
The windows
Binance — signed requests take a timestamp; recvWindow is optional,
defaults to 5000 ms and has a maximum of 60000 ms. Outside it you get
-1021 INVALID_TIMESTAMP, documented as “Timestamp for this request is outside
of the recvWindow” or “was 1000ms ahead of the server’s time”.
OKX — rejects requests whose timestamp differs from server time by more than
30 seconds, returning 50102. The docs recommend syncing via
GET /api/v5/public/time before placing orders. Format is strict: ISO 8601 UTC
with milliseconds, e.g. 2020-12-08T09:08:57.715Z.
Bybit — X-BAPI-TIMESTAMP in milliseconds, with X-BAPI-RECV-WINDOW
optional and defaulting to 5000 ms.
Note the asymmetry on Binance: being ahead of server time is rejected outright, not just being late. A clock running fast fails even inside the window.
Three different failures wearing one hat
1. Format. OKX wants ISO 8601 UTC with milliseconds. Binance and Bybit want integer milliseconds. Sending seconds instead of milliseconds — a factor of 1000 — produces a timestamp decades off, and an authentication error.
2. Two different values. Generating the timestamp once for the signature and again for the header produces a mismatch, because a few milliseconds elapsed between them. Generate once, use twice. This is a common bug in hand-rolled signing and it fails intermittently, which makes it maddening.
3. Actual drift. The machine’s clock is wrong.
Diagnosing it in one minute
Compare your clock to the venue’s:
GET /api/v5/public/time # OKX
GET /api/v3/time # Binance
If the difference is material, it is drift and no amount of reading your HMAC code will help.
The signature-versus-timestamp fork: a timestamp problem has its own code
(-1021, 50102) distinct from the signature code (-1022, 50113). If you
are getting the timestamp code, stop looking at your signing. If you are getting
the signature code, the timestamp is probably fine — see
OKX API error 50113 for that path.
Where drift comes from
Virtual machines, especially after suspend or migration. Guest clocks drift from the host and can jump.
Containers inherit the host clock, so a wrong host is a wrong container — and people debug the container.
NTP not running. More common than expected on minimal cloud images.
Windows default sync intervals are coarse compared to what a 5-second window wants.
Latency, on a long or unstable route. The timestamp is generated at your end and evaluated at theirs; a slow path consumes the window before arrival.
Fixing it
Run NTP, and verify it is actually running. Installed and enabled are different things.
Do not widen recvWindow as the fix. It is tempting and it treats the
symptom while reducing the replay protection the window exists to provide. If
you genuinely need a larger window because of a slow route, widen it
deliberately and knowingly — not because drift made it fail.
Generate the timestamp once per request and use the same value everywhere it appears.
Log the delta at startup. Query the venue’s server time, compare, and log the difference. If the number is ever surprising you will know immediately, and it costs one request.
Do not compensate in code. Measuring an offset and adding it to your timestamps papers over a broken clock and will silently do the wrong thing when the clock is corrected.
The automation angle
Two consequences worth designing for.
Timestamp failures are ExchangeError, not OperationFailed. The exchange
understood the request and refused it, so retrying unchanged fails again — see
CCXT’s common errors. A retry loop on a clock
problem generates rate-limit pressure without ever succeeding.
A model will retry. A failed tool call comes back as a result to reason about, and the natural response is to try again. Neither the model nor the retry can fix a clock, so this class of error should fail fast and loudly rather than being absorbed into a retry path.
FAQ
What does Binance error -1021 mean?
INVALID_TIMESTAMP — the request’s timestamp fell outside recvWindow, which
defaults to 5000 ms and maxes at 60000 ms, or it was ahead of Binance’s server
time. It is a clock problem rather than a code problem, and the fix is
synchronising your system clock rather than adjusting your signing.
Why do I get authentication errors only sometimes?
Two likely causes. If your clock is drifting, requests fail once the drift exceeds the window and succeed before that. If you generate the timestamp twice — once for the signature and once for the header — the values differ by the milliseconds between them, which fails whenever that gap crosses a boundary. Generate it once.
Should I increase recvWindow to fix timestamp errors?
Only if you have a genuinely slow network path and have decided to trade replay protection for tolerance. Widening the window to accommodate a drifting clock hides a problem that will resurface elsewhere, and the window exists for a reason. Fix the clock first.
How do I check my clock against the exchange?
Query the venue’s server time endpoint — GET /api/v5/public/time on OKX,
GET /api/v3/time on Binance — and compare it to your own. Doing this at
process startup and logging the delta turns an intermittent mystery into a
number you can see.