Kraken API Limits: What Every Trader Needs to Know
A practical guide to Kraken API rate limits, order types, and authentication for algo traders building bots or automating crypto strategies.
A practical guide to Kraken API rate limits, order types, and authentication for algo traders building bots or automating crypto strategies.
If you've built a trading bot on Kraken and suddenly started seeing 429 errors or EAPI:Rate limit exceeded messages, you've hit the API rate limit wall. It happens to everyone — usually at the worst possible moment, right when volatility spikes and your bot needs to fire orders fast. Understanding how Kraken's rate limiting system actually works is the difference between a bot that survives a volatile market and one that goes silent exactly when you need it most.
Kraken uses a token bucket system for its REST API rate limits — not a simple requests-per-second counter. Each API key has a counter that starts at a maximum value and decays over time. Every request costs a certain number of tokens, and if your counter hits zero, requests start failing. The counter regenerates at a fixed rate, so sustained traffic is manageable as long as you don't burst too hard.
For the Kraken REST API, the limits differ based on your account tier. The base tier starts with a counter maximum of 15, with a decay rate of 0.33 per second. Intermediate tier bumps that to a max of 20 with 0.5/second decay. Pro and higher tiers get a max of 20 with a 1/second decay. That might not sound like a lot compared to what Binance offers on its spot API, but Kraken's system is designed for reliability over raw throughput.
| Tier | Counter Max | Decay Rate | Effective RPS |
|---|---|---|---|
| Starter | 15 | 0.33/sec | ~0.33 req/sec sustained |
| Intermediate | 20 | 0.5/sec | ~0.5 req/sec sustained |
| Pro | 20 | 1/sec | ~1 req/sec sustained |
Different endpoints cost different token amounts. A simple ticker call costs 1 token, while placing or canceling orders costs 0 tokens on the trading endpoints — but private endpoints like querying open orders cost 1 token each call. Always check the Kraken API docs for the specific cost of each endpoint you use.
A Kraken API limit order lets you specify the exact price at which you want to buy or sell. Unlike market orders, limit orders sit in the order book until they're filled or you cancel them. Through the API, you submit them to the AddOrder endpoint with ordertype set to limit. You can stack additional flags like post-only (to ensure maker fees) or reduce-only for futures positions.
import krakenex
import time
import hmac
import hashlib
import base64
import urllib.parse
# Initialize the Kraken API client
api = krakenex.API()
api.key = 'YOUR_API_KEY'
api.secret = 'YOUR_API_SECRET'
def place_limit_order(pair, side, volume, price):
"""
Place a limit order on Kraken.
pair: e.g. 'XBTUSD'
side: 'buy' or 'sell'
volume: string, e.g. '0.01'
price: string, e.g. '65000'
"""
params = {
'pair': pair,
'type': side,
'ordertype': 'limit',
'price': str(price),
'volume': str(volume),
'oflags': 'post' # post-only to ensure maker fee
}
try:
response = api.query_private('AddOrder', params)
if response.get('error'):
print(f'Order error: {response["error"]}')
return None
txid = response['result']['txid'][0]
print(f'Order placed: {txid}')
return txid
except Exception as e:
print(f'Exception placing order: {e}')
return None
# Example: buy 0.01 BTC at $65,000
txid = place_limit_order('XBTUSD', 'buy', '0.01', '65000')
One thing that trips up developers coming from Binance or Bybit: Kraken uses its own asset pair naming convention. Bitcoin/USD is XBTUSD, not BTCUSDT. ETH/USD is ETHUSD. Always query the AssetPairs endpoint first if you're building something that needs to handle multiple symbols dynamically.
Kraken uses HMAC-SHA512 for API authentication. Every private request needs a nonce (incrementing integer) and a signature generated from the endpoint path plus the request payload. Getting this wrong is one of the most common reasons new bots fail silently — the auth error comes back as a generic EAPI:Invalid key message.
import urllib.parse
import hashlib
import hmac
import base64
import time
import requests
API_KEY = 'YOUR_API_KEY'
API_SECRET = 'YOUR_API_SECRET'
API_URL = 'https://api.kraken.com'
def get_kraken_signature(url_path, data, secret):
postdata = urllib.parse.urlencode(data)
encoded = (str(data['nonce']) + postdata).encode()
message = url_path.encode() + hashlib.sha256(encoded).digest()
mac = hmac.new(base64.b64decode(secret), message, hashlib.sha512)
return base64.b64encode(mac.digest()).decode()
def kraken_request(uri_path, data):
data['nonce'] = str(int(1000 * time.time()))
headers = {
'API-Key': API_KEY,
'API-Sign': get_kraken_signature(uri_path, data, API_SECRET)
}
response = requests.post(
API_URL + uri_path,
headers=headers,
data=data,
timeout=10
)
return response.json()
# Fetch open orders
result = kraken_request('/0/private/OpenOrders', {'trades': True})
if result.get('error'):
print('API Error:', result['error'])
else:
orders = result['result']['open']
print(f'Open orders count: {len(orders)}')
for txid, order in orders.items():
print(f'{txid}: {order["descr"]["order"]}')
Always store your API keys in environment variables, never hardcoded in source files. Rotate keys immediately if a repo with hardcoded credentials gets pushed to GitHub — Kraken's key management panel lets you create and revoke keys instantly.
The most professional approach to Kraken API rate limits isn't trying to stay under the limit — it's building a system that handles the EAPI:Rate limit exceeded error gracefully and recovers without human intervention. On platforms like Bybit and OKX, WebSocket connections reduce the need to poll REST endpoints repeatedly. Kraken offers WebSockets too, and for live price data or order book updates, you should absolutely use them instead of hammering the REST API.
import time
import requests
from functools import wraps
class KrakenRateLimiter:
def __init__(self, tier='starter'):
tiers = {
'starter': {'max': 15, 'decay': 0.33},
'intermediate': {'max': 20, 'decay': 0.5},
'pro': {'max': 20, 'decay': 1.0}
}
self.config = tiers.get(tier, tiers['starter'])
self.counter = self.config['max']
self.last_check = time.time()
def _update_counter(self):
now = time.time()
elapsed = now - self.last_check
self.counter = min(
self.config['max'],
self.counter + elapsed * self.config['decay']
)
self.last_check = now
def can_request(self, cost=1):
self._update_counter()
if self.counter >= cost:
self.counter -= cost
return True
return False
def wait_and_request(self, cost=1):
while not self.can_request(cost):
time.sleep(0.1)
def with_retry(max_retries=3, backoff=2.0):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
result = func(*args, **kwargs)
if isinstance(result, dict) and result.get('error'):
errors = result['error']
if any('Rate limit' in e for e in errors):
wait = backoff ** attempt
print(f'Rate limited. Waiting {wait}s...')
time.sleep(wait)
continue
# Non-rate-limit errors: don't retry
return result
return result
return result
return wrapper
return decorator
# Usage
rate_limiter = KrakenRateLimiter(tier='intermediate')
@with_retry(max_retries=3, backoff=2.0)
def safe_get_ticker(pair):
rate_limiter.wait_and_request(cost=1)
response = requests.get(
f'https://api.kraken.com/0/public/Ticker?pair={pair}',
timeout=5
)
return response.json()
ticker = safe_get_ticker('XBTUSD')
if not ticker.get('error'):
last_price = ticker['result']['XXBTZUSD']['c'][0]
print(f'BTC last price: ${last_price}')
For signal-driven bots, pairing Kraken's execution engine with a real-time signal platform like VoiceOfChain gives you the best of both worlds: high-quality entry signals from on-chain and market data, with Kraken handling execution. The key is making sure your signal consumer doesn't fire multiple simultaneous API calls when a cluster of signals arrives at once — queue them and process sequentially with the rate limiter in place.
Kraken Pro API limits apply to users who have verified their account and reached the higher trading volume tiers. The main benefit isn't just higher rate limits — it's the faster decay rate that lets you sustain more consistent API traffic without hitting the ceiling. For bots running market-making strategies or scalping algorithms, the difference between 0.33/sec and 1/sec decay is substantial.
To upgrade your Kraken API tier, you need to complete the full KYC verification process and, in some cases, demonstrate trading volume over 30-day periods. Compared to institutional API access at Coinbase Advanced Trade or the VIP tiers at Gate.io and KuCoin, Kraken's tiering system is relatively straightforward — it's tied to account verification level and trading volume rather than requiring separate applications.
Kraken's rate limiting system rewards traders who build thoughtfully over those who brute-force their way through API calls. The token bucket model is actually more forgiving than rolling-window systems used by some other exchanges — a short burst of requests is fine as long as you give the counter time to regenerate. The traders who hit walls repeatedly are almost always polling REST endpoints for data they could get through WebSockets, or checking order status in tight loops when they could use the private WebSocket feed instead.
The full picture for a production-grade Kraken bot: use REST for order placement and account queries, WebSockets for all market data and order updates, implement a local rate limiter based on the token bucket model, and add exponential backoff on any EAPI error. Layer in a signal source like VoiceOfChain for trade entry logic, and you have a system that can run continuously without manual intervention — which is the actual goal.
Pro tip: log every API error with a timestamp and the full response. Rate limit errors cluster predictably around market open, major news events, and liquidation cascades. Reviewing those logs tells you exactly when your bot is most vulnerable and where to optimize first.