# Kraken API invalid nonce: causes and multi-process fixes

URL: https://fincodelabs.com/en/blog/kraken-api-invalid-nonce · Publisher: Fincode (Fincode Yazılım ve Teknoloji A.Ş.), Istanbul, Turkey · Category: Exchange APIs · Published: 2026-11-06 · Updated: 2026-11-06

Kraken API invalid nonce explained from Kraken's own pages: why shared keys, retries and clock steps break the nonce, and fixes tested across processes.

**Short answer:** **Kraken API invalid nonce** (`EAPI:Invalid nonce`) means the nonce you sent was not higher than the last one Kraken accepted for that API key, per Kraken's error documentation read on 5 November 2026. It usually comes from bots sharing one key, requests arriving out of order, or a clock that stepped back. Give each process its own key, or let one process number and send every request for the key.

This guide tests six ways of numbering requests across four processes on a local mock and compares how other exchanges check request time. No live key was used and nothing was sent to Kraken. Never paste a live API secret into a chat, an online tool, a forum or an AI assistant.

This is a technical guide, not a recommendation of any platform, and automated trading can lose money. Availability depends on your jurisdiction and each platform's terms. For readers in Turkey: Article 99/A of Capital Markets Law No. 6362 treats activity by foreign crypto-asset platforms directed at Turkish residents as unauthorized.

## What does EAPI:Invalid nonce mean on Kraken?

Kraken rejects a private Spot REST request whose nonce is not above the last one accepted for that API key; the state belongs to the key, not your program.

**Kraken's pages on the nonce, read 5 November 2026**

| Page | What it says |
| --- | --- |
| REST authentication guide | An always-increasing unsigned 64-bit integer, usually Unix time in milliseconds, that cannot be reset lower. Shared keys and clock drift cause trouble; many invalid nonces can bring temporary bans. |
| Error messages guide | The nonce was at or below the last accepted one. Threads sharing a key should use a counter with a mutex. Never retry the same nonce. |
| Support: invalid nonce errors (updated 31 March 2025) | Causes: simultaneous or rapid requests, one key on several bots or devices. Fixes: more keys, a finer nonce, a wider window for network issues only. |
| Support: what is a nonce (updated 29 October 2026) | An unusable nonce, such as a far-future timestamp, needs a new key, which starts at 0. Futures REST needs no nonce. |
| Support: API error messages (updated 26 June 2026) | Too many invalid nonces, failed calls or invalid signatures can cause `EGeneral:Temporary lockout` for about 15 minutes. The error messages guide instead says about 20 minutes, for every key on an IP that sent unknown keys. |

A rejected order was not placed; retrying in a loop moves toward a lockout. The error messages guide lists nonce or POST body order under `EAPI:Invalid signature`, not this error. Signing is covered in [Kraken API in Python](https://fincodelabs.com/en/blog/kraken-api-python).

## Why Kraken nonce errors happen with several bots

A nonce is fixed when a request is built but checked when it arrives, so anything that reorders requests in between can trigger it.

- **Shared keys.** Two bots, or a bot and a dashboard, draw from one nonce sequence over separate connections.
- **Retries.** A request that timed out may have been accepted. Resending it unchanged is rejected; resending with a fresh nonce can place an order twice, so attach your own reference first, as in [client order IDs](https://fincodelabs.com/en/blog/client-order-id).
- **Async sends.** `asyncio.gather` and thread pools number requests in one order and finish them in another.
- **Clock steps.** A time-sync correction can move the clock back, leaving time-based nonces too low until it catches up.

> **Hypothetical example.** Bot A takes nonce 1793836800120 for an order; bot B takes 1793836800121 on the same key. B arrives first and is accepted; A arrives 2 ms later and is rejected: its order is never placed.

## Kraken invalid nonce fixes tested across processes

In a test on 5 November 2026, only one key per process and a single writer avoided rejections in every run.

The script runs a local HTTP mock that keeps state per key and, per design, four processes sending 50 requests each, pausing up to 3 ms between numbering and sending to mimic network delay. The mock also rejects anything but an unsigned 64-bit integer, and a repeated nonce inside the window, since Kraken calls a nonce unique; Kraken does not document its reply in either case. Python 3.11.3 on Windows 11, standard library only.

```python
# LOCAL MOCK of Kraken's documented nonce rule. No real key; nothing is sent to Kraken.
import json, multiprocessing as mp, os, random, tempfile, threading, time, urllib.request
from functools import partial
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

WINDOW, LAST, SEEN, LOCK = {"kw": 5000}, {}, {}, threading.Lock()

def accept(key, nonce):
    with LOCK:
        last, seen = LAST.get(key, 0), SEEN.setdefault(key, set())
        if not (type(nonce) is int and max(0, last - WINDOW.get(key, 0)) < nonce < 2**64) or nonce in seen:
            return ["EAPI:Invalid nonce"]
        LAST[key] = max(last, nonce); seen.add(nonce)
        return []

class Mock(BaseHTTPRequestHandler):
    def do_POST(self):
        body = json.loads(self.rfile.read(int(self.headers["Content-Length"])))
        reply = json.dumps({"error": accept(body["key"], body["nonce"]), "result": {}})
        self.send_response(200); self.end_headers(); self.wfile.write(reply.encode())
    def log_message(self, *args): pass

def send(url, key, nonce):
    with urllib.request.urlopen(url, json.dumps({"key": key, "nonce": nonce}).encode(), timeout=5) as r:
        return json.loads(r.read() or b"{}").get("error") or []

BASE = time.time_ns() - time.perf_counter_ns()
def millis(): return time.time_ns() // 10**6
def micros(): return (BASE + time.perf_counter_ns()) // 10**3

class SharedCounter:
    def __init__(self): self.v = mp.Value("q", millis())
    def __call__(self):
        with self.v.get_lock():
            self.v.value += 1
            return self.v.value

def worker(url, key, make_nonce, out):   # a separate OS process
    bad = 0
    for _ in range(50):
        nonce = make_nonce()
        time.sleep(random.uniform(0, 0.003))   # network and scheduling delay
        bad += "EAPI:Invalid nonce" in send(url, key, nonce)
    out.put(bad)

def next_nonce(path):                    # single writer: persisted, never goes back
    last = 0
    if os.path.exists(path):
        with open(path) as f: last = int(f.read())
    nonce = max(last + 1, micros())
    with open(path + ".tmp", "w") as f: f.write(str(nonce))
    os.replace(path + ".tmp", path)      # atomic: no half-written number after a crash
    return nonce

def producer(jobs, out):
    for _ in range(50): jobs.put("AddOrder")
    out.put(0)

def writer(url, key, jobs, out, path):   # the only process that uses this key
    bad = 0
    for _ in range(200):
        jobs.get()
        bad += "EAPI:Invalid nonce" in send(url, key, next_nonce(path))
    out.put(bad)

if __name__ == "__main__":
    server = ThreadingHTTPServer(("127.0.0.1", 0), Mock)
    threading.Thread(target=server.serve_forever, daemon=True).start()
    url, tmp, jobs, out = f"http://127.0.0.1:{server.server_port}", tempfile.mkdtemp(), mp.Queue(), mp.Queue()
    four = lambda make: [mp.Process(target=worker, args=(url, *make(i), out)) for i in range(4)]
    counter = SharedCounter()
    cases = {"shared key, ms clock": four(lambda i: ("k1", millis)),
             "shared key, microsecond clock": four(lambda i: ("k2", micros)),
             "shared key, locked counter": four(lambda i: ("k3", counter)),
             "shared key, ms clock, window 5000": four(lambda i: ("kw", millis)),
             "one key per process": four(lambda i: (f"k4{i}", partial(next_nonce, os.path.join(tmp, f"k4{i}")))),
             "single writer, 4 producers": [mp.Process(target=producer, args=(jobs, out)) for _ in range(4)]
                 + [mp.Process(target=writer, args=(url, "k5", jobs, out, os.path.join(tmp, "k5")))]}
    for label, procs in cases.items():
        for p in procs: p.start()
        for p in procs: p.join()
        print(f"{label:<35}{sum(out.get(timeout=10) for _ in procs):>3} of 200 rejected")
    before, path = micros() + 2_000_000, os.path.join(tmp, "k6")   # sent before a 2 s step back
    accept("k6", before)
    with open(path, "w") as f: f.write(str(before))
    print("after the step:", accept("k6", micros()), "| single writer:", accept("k6", next_nonce(path)))
```

First of three runs:

```
shared key, ms clock                75 of 200 rejected
shared key, microsecond clock       65 of 200 rejected
shared key, locked counter          57 of 200 rejected
shared key, ms clock, window 5000   20 of 200 rejected
one key per process                  0 of 200 rejected
single writer, 4 producers           0 of 200 rejected
after the step: ['EAPI:Invalid nonce'] | single writer: []
```

Across three runs the shared-key designs were rejected 20 to 77 times in 200, varying with machine load.

- **A lock around the nonce is not enough.** The locked counter was rejected 48 to 57 times because sending happened outside the lock; Kraken's counter-and-mutex advice works only if the lock covers the send.
- **A finer clock does not fix late arrivals** (45 to 65 rejections).
- **The window absorbs delay, not repeats.** At millisecond resolution 5000 meant 5 seconds, longer than any delay here, so its 20 to 38 rejections were all repeated millisecond values.
- **The single writer survives clock steps and restarts:** it stores the last nonce and sends max(last + 1, clock).

Use one key per process for independent bots, and a monitored single writer when strategies must share a key. For integration or code review work in general, see [exchange API integration](https://fincodelabs.com/en/services/exchange-api-integration) and [trading bot code review](https://fincodelabs.com/en/services/trading-bot-code-review); naming Kraken or any other venue here does not mean Fincode integrates it or has built on it.

## Raising nonce resolution and the Kraken nonce window

A key can move to a finer nonce, since larger numbers are accepted, but not back: microseconds to milliseconds makes numbers 1,000 times smaller, and Kraken's fix for an unusable nonce is a new key.

- **How far up.** Kraken's example is milliseconds to tenths of milliseconds. Even nanoseconds fit the unsigned 64-bit limit until July 2554.
- **Check the clock.** On the Windows test machine, Python 3.11 reported `GetSystemTimeAsFileTime()` with 15.625 ms nominal resolution, and `time.time_ns()` moved in steps of about 0.5 ms, so dividing it by 1,000 added digits, not resolution (1 microsecond from Python 3.13, per its What's New). The script anchors a performance counter to wall time instead.

The nonce window lets a lower nonce through for a short span. Kraken's article sets the default at 0, reserves it for network-related errors, and counts it in nonce units: 5000 is 5 seconds for millisecond nonces, but would be 5 ms for microsecond ones. It states no maximum; no other official page read on 5 November 2026 gives one.

## How Binance, OKX and other APIs check request time

Most other venues reject a signed request whose timestamp is too far from server time, so clock drift matters more there than concurrency.

**Request-time rules from official documentation, read 5 November 2026**

| Venue | Field and unit | Allowed gap | Code and exact message |
| --- | --- | --- | --- |
| Binance Spot | `timestamp`, milliseconds or microseconds | `recvWindow` default 5,000 ms, maximum 60,000 ms; rejected if 1 second or more ahead | `-1021`: Timestamp for this request is outside of the recvWindow. / Timestamp for this request was 1000ms ahead of the server's time. |
| BingX (standard contract document) | `timestamp`, milliseconds | 5,000 ms; `recvWindow` changes it, no maximum stated | None named |
| Bitget | `ACCESS-TIMESTAMP`, milliseconds | 30 seconds | None named on the REST API page |
| Bitvavo | `Bitvavo-Access-Timestamp`, milliseconds | `Bitvavo-Access-Window` default 10,000 ms, maximum 60,000 ms | `304`: Bitvavo did not receive your request within the access window. |
| Gate | `Timestamp` header, seconds | 60 seconds | `REQUEST_EXPIRED`: Request `Timestamp` is far from the server time. |
| MEXC Spot | `timestamp`, milliseconds | `recvWindow` default 5,000, maximum 60,000 | `700003`: Timestamp for this request is outside of the recvWindow |
| OKX | `OK-ACCESS-TIMESTAMP`, ISO 8601 UTC with milliseconds | 30 seconds | `50102`: Timestamp request expired. |

BingX's main documentation could not be read as text, so its row comes from the standard contract repository in BingX's GitHub organization, last updated December 2022. Handling the Binance API timestamp error is covered in [Binance API error codes](https://fincodelabs.com/en/blog/binance-api-error-codes), and signing in API signature generation.

## Unix timestamp converter: seconds, milliseconds, nanoseconds

The digit count gives the unit of a current-era Unix timestamp: 10 digits for seconds, 13 for milliseconds, 16 for microseconds, 19 for nanoseconds.

```python
import time
from datetime import datetime, timezone

def to_utc(ts):
    text = str(ts)
    per_second = {10: 1, 13: 10**3, 16: 10**6, 19: 10**9}.get(len(text)) if text.isascii() and text.isdigit() else None
    if per_second is None:
        raise ValueError(f"not a 10, 13, 16 or 19 digit Unix timestamp: {text}")
    seconds, fraction = divmod(int(text), per_second)
    return datetime.fromtimestamp(seconds, timezone.utc).isoformat(), fraction

ns = time.time_ns()
print(ns // 10**9, ns // 10**6, ns // 10**3, ns)
for value in [1793836800123, "1793836800123456789", "1e400"]:
    try:
        print(value, to_utc(value))
    except ValueError as err:
        print(err)
```

Output on 5 November 2026:

```
1793908622 1793908622296 1793908622296940 1793908622296940200
1793836800123 ('2026-11-05T00:00:00+00:00', 123)
1793836800123456789 ('2026-11-05T00:00:00+00:00', 123456789)
not a 10, 13, 16 or 19 digit Unix timestamp: 1e400
```

It divides integers, because a float keeps about 16 significant digits and would cut a nanosecond value, and it rejects `1e400`, which `float()` reads as infinity, and non-ASCII digits. The digit rule holds from September 2001 to November 2286.

## Frequently asked questions

### Can I reset the nonce of a Kraken API key?

No. Kraken says a key's nonce cannot be lowered; if it became unusable, delete the key and create a new one, which starts at 0.

### Should I increase the Kraken nonce window?

Only for network reordering, as Kraken advises; the default is 0. It counts in nonce units, so recalculate it when you change resolution.

### Can two bots share one Kraken API key?

Only if one process numbers and sends every request for that key. In the mock test, every other shared design produced rejections.

### Is a Binance timestamp error the same as a Kraken invalid nonce?

No. Binance error -1021 concerns time against recvWindow; Kraken Spot tracks a nonce per key with no clock window.

## Sources

- [Kraken: Spot REST Authentication (nonce rules)](https://docs.kraken.com/exchange/guides/rest/authentication)
- [Kraken: Error messages (EAPI:Invalid nonce)](https://docs.kraken.com/exchange/guides/general/errors)
- [Kraken Support: Why am I getting invalid nonce errors? (updated 31 March 2025)](https://support.kraken.com/articles/360001148063)
- [Kraken Support: What is a nonce? (updated 29 October 2026)](https://support.kraken.com/articles/360000906023-What-is-a-nonce-)
- [Kraken Support: What is a nonce window? (updated 31 March 2025)](https://support.kraken.com/articles/360001148023-what-is-a-nonce-window-)
- [Kraken Support: API error messages (temporary lockout, updated 26 June 2026)](https://support.kraken.com/articles/360001491786-API-error-messages)
- [Binance Spot API: Request security, timing (last modified 30 October 2026)](https://developers.binance.com/docs/binance-spot-api-docs/rest-api/request-security)
- [Binance Spot API: Error codes (last modified 30 October 2026)](https://developers.binance.com/docs/binance-spot-api-docs/errors)
- [BingX-API on GitHub: Standard Contract REST API document](https://github.com/BingX-API/BingX-Standard-Contract-doc/blob/main/REST%20API.md)
- [Bitget API: REST API, timestamp rule](https://www.bitget.com/docs/uta/rest-api)
- [Bitvavo: REST API introduction (access window)](https://docs.bitvavo.com/docs/rest-api/introduction/)
- [Bitvavo: Errors](https://docs.bitvavo.com/docs/errors/)
- [Gate: API v4 documentation (authentication, error labels)](https://www.gate.com/docs/developers/apiv4/en/)
- [MEXC: Spot V3 API documentation (timing security, error codes)](https://mexcdevelop.github.io/apidocs/spot_v3_en/)
- [OKX: API v5 documentation (REST authentication, error code 50102)](https://www.okx.com/docs-v5/en/)

> This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the [editorial policy](https://fincodelabs.com/en/editorial-policy).

---

Published by Fincode (Fincode Yazılım ve Teknoloji A.Ş.), a software company in Istanbul, Turkey: https://fincodelabs.com/en/blog/kraken-api-invalid-nonce

Cite as: Fincode, "Kraken API invalid nonce: causes and multi-process fixes", updated November 6, 2026, https://fincodelabs.com/en/blog/kraken-api-invalid-nonce
