Four Exchanges, Three Timestamp Units: Seconds, Milliseconds, Nanoseconds — and One Gives You Two at Once
Disclosure: this article was researched and drafted with AI assistance, then checked against primary sources listed at the end. All figures below were pulled live from official public API endpoints on 2026-09-15 between 02:09:16 and 02:09:22 UTC.
If you have ever written code that talks to more than one crypto exchange, you have probably hit this bug: your signed request is rejected with something like "signature does not match" or "timestamp outside recvWindow", even though your key is correct, your signature algorithm is correct, and your clock is perfectly synchronized. Nine times out of ten, the problem is not your signature. It is your unit.
There is no industry standard for what "a timestamp" means in an exchange API. Four of the largest venues each made a different choice — and one of them gives you three precisions in the same response.
The live measurement
On 15 September 2026, within a single six-second window, we called the public time endpoint of four exchanges. No API key required — you can reproduce every number yourself.
Binance — GET /api/v3/time
{"serverTime": 1789438156343}
Thirteen digits. That is milliseconds since the Unix epoch.
Kraken — GET /0/public/Time
{"error": [], "result": {"unixtime": 1789438157, "rfc1123": "Tue, 15 Sep 26 02:09:17 +0000"}}
Ten digits — seconds — plus a human-readable RFC 1123 string as a bonus field. Kraken's own official documentation page for this endpoint uses the same 10-digit format in its example response ("unixtime": 1688669448), so the seconds convention is confirmed at the documentation layer, not just inferred from digit counts.
Bybit — GET /v5/market/time
{"retCode": 0, "retMsg": "OK",
"result": {"timeSecond": "1789438157",
"timeNano": "1789438157667206123"},
"time": 1789438157667}
This one is a small masterpiece of confusion: the same response object carries seconds (timeSecond), nanoseconds (timeNano, 19 digits), and — in the outer envelope field time — milliseconds. Three precisions, one call. Bybit's official docs define each field in plain text: "timeSecond string Bybit server timestamp (sec)" and "timeNano string Bybit server timestamp (nano)".
OKX — GET /api/v5/public/time
{"code": "0", "data": [{"ts": "1789438159520"}], "msg": ""}
Thirteen digits again, so milliseconds — but note it arrives as a string, not a number. OKX is also the most explicit venue of the four: its REST documentation labels timestamp fields individually, thousands of times over, with lines like "Unix timestamp format in milliseconds, e.g. 1597026383085".
Cross-check: converting all four readings to a common unit, the four servers agreed to within about one second — the spacing of our own requests. The exchange clocks are in sync. The only disagreement is about units.
Why this bites you twice
First bite: parsing the server time. If you naively do datetime.fromtimestamp(serverTime) on Binance's 13-digit value, you get a date roughly 57,000 years in the future. Divide by 1,000? Fine for Binance and OKX, wrong for Kraken, wrong for Bybit's timeNano.
Second bite, and the expensive one: signing requests. The timestamp you sign must match what the server expects, in the unit the server expects:
- Binance's official REST documentation states that SIGNED requests require a
timestampparameter "in milliseconds or microseconds" — yes, either one, but not seconds — with a defaultrecvWindowof 5,000 ms. - OKX requires the
OK-ACCESS-TIMESTAMPheader and rejects requests whose timestamp differs from server time by more than 30 seconds. - Bybit validates your request timestamp against its own clock with a receive-window header in milliseconds.
The classic failure mode: you fetch Kraken's time (seconds), reuse the same helper for a Bybit signature (milliseconds), and your timestamp is a factor of 1,000 too small — instantly outside any reasonable receive window. The error message mentions signatures, so you debug signatures for a day. The bug was arithmetic.
The practical rule this creates for any multi-venue codebase: a per-exchange conversion constant — multiply by 10⁰, 10³, or 10⁹ depending on the venue — and a unit test that asserts the digit count of every timestamp before it is ever signed. Digit counting is a cheap, brutal sanity check: 10 digits means seconds, 13 means milliseconds, 16 means microseconds, 19 means nanoseconds.
Two subtleties worth remembering from the live data above: Bybit's triple-precision response means "the Bybit timestamp" is not one thing — pick the field deliberately. And Binance allowing either milliseconds or microseconds for signatures means two valid clients can look completely different in the logs, which matters when you are diffing a broken request against a working one.
What the docs say (so you don't have to guess)
Every claim above has a documentation layer, not just an observed value: Kraken's Get Server Time page shows a 10-digit example; Bybit's market-time page defines timeSecond and timeNano verbatim; OKX's docs label "Unix timestamp format in milliseconds" on individual fields; Binance's spot API spec states the milliseconds-or-microseconds rule for signed requests. Endpoint paths and quoted field names are listed in the sources below, so any of this can be re-checked in about thirty seconds by calling the endpoints yourself.
None of the four choices is wrong. There is simply no standard — so your abstraction layer has to carry the unit as a first-class attribute, not assume it. Before you write the converter, read the docs' unit. It is the cheapest bug you will ever prevent.
Sources (all official, all fetched 2026-09-15 02:09 UTC unless noted):
- Binance public time endpoint:
GET https://api.binance.com/api/v3/time(live value above); Binance official REST spec (binance-spot-api-docs/rest-api.md) on timestamp/recvWindow rules. - Kraken public time endpoint:
GET https://api.kraken.com/0/public/Time(live value above); Kraken official "Get Server Time" documentation page (example response in seconds). - Bybit public time endpoint:
GET https://api.bybit.com/v5/market/time(live value above); Bybit official v5 market-time documentation (timeSecond/timeNano field definitions). - OKX public time endpoint:
GET https://www.okx.com/api/v5/public/time(live value above); OKX official REST v5 documentation (per-field millisecond labeling).
AI disclosure: This article was produced with AI assistance under a two-source verification process; every number was re-fetched from the official endpoints listed above immediately before publication.
Not financial advice. This is a technical article about API specifications. Nothing here is investment, trading, or legal advice. Exchange endpoints, fields, and rules change without notice — verify against each venue's current official documentation before building anything on top of them.
No affiliate or referral links appear in this article.
Verify with official sources: api.binance.com · api.kraken.com · api.bybit.com · www.okx.com/docs-v5
Top comments (0)