Gate.io V4 API Documentation: Complete Guide for Traders
Master Gate.io V4 API setup, authentication, and trading endpoints. Learn to automate orders, stream market data, and build bots with real code examples.
Master Gate.io V4 API setup, authentication, and trading endpoints. Learn to automate orders, stream market data, and build bots with real code examples.
Gate.io's V4 REST API is one of the most capable trading interfaces in crypto — covering spot, futures, margin, and options under a single unified authentication model. If you've worked with Binance or Bybit APIs before, Gate.io V4 will feel familiar but with some important structural differences worth understanding before you write a single line of code.
The V4 API replaced the older V2/V3 endpoints and is now the only officially supported interface for automated trading on Gate.io. It follows RESTful conventions with JSON payloads, uses HMAC-SHA512 signing for authenticated requests, and supports WebSocket for real-time data feeds.
Base URL for all REST calls: https://api.gateio.ws/api/v4. Every endpoint falls into one of these namespaces: /spot, /futures, /margin, /options, /wallet, /withdrawals, and /earn. Public endpoints (market data, tickers, order books) require no authentication. Private endpoints (placing orders, checking balances, withdraw) require a signed request.
| Namespace | Scope | Auth Required |
|---|---|---|
| GET /spot/tickers | All spot market tickers | No |
| GET /spot/order_book | Order book depth | No |
| POST /spot/orders | Place spot order | Yes |
| GET /futures/{settle}/positions | Open futures positions | Yes |
| GET /wallet/balances | Account balances | Yes |
| POST /withdrawals | Initiate withdrawal | Yes |
Gate.io V4 uses HMAC-SHA512, not the HMAC-SHA256 that Binance and OKX use. If you're porting code from another exchange, update your signing function — this is the #1 source of authentication errors for developers switching from other platforms.
Before writing code, generate your API credentials in Gate.io's console under Account > API Management. You'll get an API Key and Secret. Set permissions granularly — if your bot only trades spot, don't enable futures or withdrawal permissions. Least privilege matters.
Gate.io V4 authentication requires four headers on every private request: KEY (your API key), Timestamp (Unix epoch in seconds), SIGN (the HMAC-SHA512 signature), and optionally a custom request ID. The signature covers the HTTP method, request path, query string, hashed body, and timestamp — all joined with newlines.
import hashlib
import hmac
import time
import requests
import json
API_KEY = 'your_api_key_here'
API_SECRET = 'your_api_secret_here'
BASE_URL = 'https://api.gateio.ws/api/v4'
def gen_sign(method, url, query_string=None, payload_string=None):
t = time.time()
m = hashlib.sha512()
m.update((payload_string or '').encode('utf-8'))
hashed_payload = m.hexdigest()
s = '\n'.join([
method.upper(),
url,
query_string or '',
hashed_payload,
str(t)
])
sign = hmac.new(
API_SECRET.encode('utf-8'),
s.encode('utf-8'),
hashlib.sha512
).hexdigest()
return {'KEY': API_KEY, 'Timestamp': str(t), 'SIGN': sign}
# Fetch account spot balances
def get_spot_balances():
url = '/spot/accounts'
headers = gen_sign('GET', url)
headers['Accept'] = 'application/json'
response = requests.get(BASE_URL + url, headers=headers)
response.raise_for_status()
return response.json()
balances = get_spot_balances()
for b in balances:
if float(b['available']) > 0:
print(f"{b['currency']}: {b['available']} available, {b['locked']} locked")
Gate.io's spot order endpoint supports limit, market, and stop orders with a clean parameter structure. Unlike Coinbase's Advanced Trade API which uses a more verbose order config object, Gate.io keeps things compact — currency_pair, type, side, amount, and price cover 90% of use cases.
import hashlib
import hmac
import time
import requests
import json
API_KEY = 'your_api_key_here'
API_SECRET = 'your_api_secret_here'
BASE_URL = 'https://api.gateio.ws/api/v4'
def gen_sign(method, url, query_string='', payload_string=''):
t = time.time()
m = hashlib.sha512()
m.update(payload_string.encode('utf-8'))
hashed_payload = m.hexdigest()
s = '\n'.join([method.upper(), url, query_string, hashed_payload, str(t)])
sign = hmac.new(API_SECRET.encode('utf-8'), s.encode('utf-8'), hashlib.sha512).hexdigest()
return {'KEY': API_KEY, 'Timestamp': str(t), 'SIGN': sign}
def place_limit_order(pair, side, amount, price):
url = '/spot/orders'
order = {
'currency_pair': pair, # e.g. 'BTC_USDT'
'type': 'limit',
'side': side, # 'buy' or 'sell'
'amount': str(amount),
'price': str(price),
'time_in_force': 'gtc' # good till cancelled
}
payload = json.dumps(order)
headers = gen_sign('POST', url, '', payload)
headers['Content-Type'] = 'application/json'
response = requests.post(BASE_URL + url, headers=headers, data=payload)
if response.status_code == 201:
result = response.json()
print(f"Order placed: ID {result['id']}, status: {result['status']}")
return result
else:
print(f"Error {response.status_code}: {response.json()}")
return None
def cancel_order(order_id, pair):
url = f'/spot/orders/{order_id}'
query = f'currency_pair={pair}'
headers = gen_sign('DELETE', url, query)
response = requests.delete(f'{BASE_URL}{url}?{query}', headers=headers)
response.raise_for_status()
return response.json()
# Example: buy 0.001 BTC at $60,000
place_limit_order('BTC_USDT', 'buy', 0.001, 60000)
Always store order IDs after placement. Gate.io rate limits order status queries to 100 req/s on spot — if you're running multiple strategies, use WebSocket order update streams instead of polling the REST endpoint.
Gate.io's perpetual futures API lives under /futures/{settle}/ where settle is either usdt or btc depending on the contract's margin currency. USDT-margined contracts (the most common) use /futures/usdt/. This is different from how Bybit structures its unified account — Gate.io keeps spot and futures namespaces cleanly separated.
Opening a futures position requires posting to /futures/{settle}/orders with a size parameter representing the number of contracts (positive = long, negative = short). Leverage is set separately at the contract level via /futures/{settle}/positions/{contract}/leverage before placing your order.
def set_leverage(settle, contract, leverage):
url = f'/futures/{settle}/positions/{contract}/leverage'
payload = json.dumps({'leverage': str(leverage), 'cross_leverage_limit': '0'})
headers = gen_sign('POST', url, '', payload)
headers['Content-Type'] = 'application/json'
response = requests.post(BASE_URL + url, headers=headers, data=payload)
response.raise_for_status()
return response.json()
def open_futures_position(settle, contract, size, price=None):
"""
size > 0 = long, size < 0 = short
price = None for market order
"""
url = f'/futures/{settle}/orders'
order = {
'contract': contract, # e.g. 'BTC_USDT'
'size': size,
'price': str(price) if price else '0',
'tif': 'gtc' if price else 'ioc',
'reduce_only': False
}
payload = json.dumps(order)
headers = gen_sign('POST', url, '', payload)
headers['Content-Type'] = 'application/json'
response = requests.post(BASE_URL + url, headers=headers, data=payload)
if response.status_code == 201:
result = response.json()
print(f"Futures order: {result['id']}, fill price: {result['fill_price']}")
return result
else:
error = response.json()
print(f"Futures error: {error.get('label', 'UNKNOWN')} — {error.get('message', '')}")
return None
# Set 10x leverage on BTC_USDT perpetual, then open long 1 contract
set_leverage('usdt', 'BTC_USDT', 10)
open_futures_position('usdt', 'BTC_USDT', size=1) # 1 contract long
For any serious trading bot, polling REST endpoints for price data is a dead end — you'll hit rate limits and your fills will lag the market. Gate.io's WebSocket V4 API gives you sub-100ms order book updates, trade streams, and private account updates (order fills, position changes) over a persistent connection.
The public WebSocket endpoint is wss://api.gateio.ws/ws/v4/. Authentication for private channels uses the same HMAC-SHA512 scheme as REST, sent in the auth message after connection. Platforms like VoiceOfChain use similar real-time WebSocket feeds across Gate.io, Binance, and KuCoin to deliver trading signals with minimal latency — the principle is the same whether you're consuming data or acting on it.
import asyncio
import json
import hashlib
import hmac
import time
import websockets
API_KEY = 'your_api_key_here'
API_SECRET = 'your_api_secret_here'
WS_URL = 'wss://api.gateio.ws/ws/v4/'
def ws_sign(channel, event, timestamp):
s = f'channel={channel}&event={event}&time={timestamp}'
return hmac.new(
API_SECRET.encode('utf-8'),
s.encode('utf-8'),
hashlib.sha512
).hexdigest()
async def stream_order_book(pair='BTC_USDT'):
async with websockets.connect(WS_URL) as ws:
# Subscribe to order book updates (top 20 levels, 100ms updates)
sub_msg = {
'time': int(time.time()),
'channel': 'spot.order_book',
'event': 'subscribe',
'payload': [pair, '20', '100ms']
}
await ws.send(json.dumps(sub_msg))
print(f'Subscribed to {pair} order book')
async for message in ws:
data = json.loads(message)
if data.get('event') == 'update' and 'result' in data:
book = data['result']
if book.get('bids') and book.get('asks'):
best_bid = book['bids'][0][0]
best_ask = book['asks'][0][0]
spread = float(best_ask) - float(best_bid)
print(f"Bid: {best_bid} | Ask: {best_ask} | Spread: {spread:.2f}")
async def stream_private_orders():
async with websockets.connect(WS_URL) as ws:
ts = int(time.time())
sign = ws_sign('spot.orders', 'subscribe', ts)
auth_msg = {
'time': ts,
'channel': 'spot.orders',
'event': 'subscribe',
'payload': ['!all'], # all spot pairs
'auth': {'method': 'api_key', 'KEY': API_KEY, 'SIGN': sign}
}
await ws.send(json.dumps(auth_msg))
async for message in ws:
data = json.loads(message)
if data.get('event') == 'update':
for order in data.get('result', []):
print(f"Order update: {order['id']} status={order['status']} filled={order['filled_total']}")
# Run the order book stream
asyncio.run(stream_order_book('BTC_USDT'))
Gate.io V4 rate limits are applied per API key, not per IP. Spot REST endpoints allow 900 requests per minute for most read operations and 300 per minute for order writes. Futures endpoints are more generous — 1500 req/min for reads. Exceeding limits returns HTTP 429 with a Retry-After header.
Common error labels you'll encounter: INVALID_CURRENCY_PAIR (check your pair format — Gate.io uses underscores: BTC_USDT not BTCUSDT like Binance), BALANCE_NOT_ENOUGH (obvious), ORDER_NOT_FOUND (order was filled or cancelled before your cancel request landed), and INVALID_PARAM (usually a type issue — Gate.io expects string amounts, not floats).
If you're running bots across multiple exchanges — say Bitget for altcoin perpetuals and Gate.io for spot arbitrage — normalize your error handling layer. Gate.io uses string error labels ('INVALID_PARAM') while Binance uses numeric codes (-1121). Building exchange-agnostic error handling saves you hours of debugging.
Gate.io's V4 API gives you solid coverage across spot, futures, and margin with a consistent auth model once you internalize the HMAC-SHA512 signing pattern. The WebSocket feeds are reliable for real-time data, and the REST endpoints cover every trading operation you'd need for an automated strategy.
Start with the public endpoints to validate your setup, implement signing for balance checks, then move to order placement. If you're using trading signals from platforms like VoiceOfChain, the Gate.io WebSocket private channel is your fastest path to automated execution — signals arrive, your bot reads them, places the order, and monitors the fill, all without a single manual click.