Spot crypto
Spot crypto on Gemini
Discover crypto markets, place spot orders, and manage balances through Gemini's REST, WebSocket, and FIX interfaces.
Spot crypto REST API
Place spot orders, query market data, and manage crypto balances.
Get your API credentials
Sign up at exchange.gemini.com, navigate to Settings → API, and create a key with the Trading scope enabled.
export GEMINI_API_KEY="account-xxxxxxxxxxxxxxxx"
export GEMINI_API_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Clone the samples repo and install dependencies
Clone the Gemini API samples repository, navigate to the Python directory, and install the required packages (requests and python-dotenv).
git clone https://github.com/gemini/developer-platform.git
cd developer-platform/samples/python
pip install -r requirements.txtConfigure your environment
Create a .env file with your API credentials and the sandbox base URL. The sample uses python-dotenv to load these automatically.
cat > .env << 'EOF'
GEMINI_API_KEY=account-xxxxxxxxxxxxxxxx
GEMINI_API_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
GEMINI_BASE_URL=https://api.sandbox.gemini.com/v1
EOFRun the place order example
Run the example to place a sandbox limit order. The script handles HMAC-SHA384 signing automatically.
python place_order.pyYou're done!
Your order is live in the sandbox. Check the response for order_id and use it to query status or cancel.
# Order confirmed ✓
Order placed: {'order_id': '7651834', 'symbol': 'btcusd', 'side': 'buy', ...}WebSocket
Stream real-time prices, place orders, and receive fill notifications over a persistent WebSocket connection to wss://ws.gemini.com.
Set up credentials
Export your API key and secret as environment variables. WebSocket authentication requires account-scoped keys with time-based nonces enabled — keys without these settings will be rejected at connection time.
export GEMINI_API_KEY="account-xxxxxxxxxxxxxxxx"
export GEMINI_API_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Connect and stream prices
Connect to wss://ws.gemini.com, sign the handshake headers with your secret, then subscribe to {symbol}@bookTicker for real-time best-bid/ask updates.
# pip install websockets
import asyncio, websockets, hmac, hashlib, base64, json, time
API_KEY = "your-api-key"
API_SECRET = "your-api-secret"
SYMBOL = "BTCUSD"
def auth_headers():
nonce = str(int(time.time()))
payload = base64.b64encode(nonce.encode())
signature = hmac.new(API_SECRET.encode(), payload, hashlib.sha384).hexdigest()
return {
"X-GEMINI-APIKEY": API_KEY,
"X-GEMINI-NONCE": nonce,
"X-GEMINI-PAYLOAD": payload.decode(),
"X-GEMINI-SIGNATURE": signature,
}
async def stream_prices():
async with websockets.connect("wss://ws.gemini.com", additional_headers=auth_headers()) as ws:
await ws.send(json.dumps({
"id": "1",
"method": "subscribe",
"params": [f"{SYMBOL}@bookTicker"],
}))
async for msg in ws:
data = json.loads(msg)
if "b" in data and "a" in data:
print(f"{data['s']} bid: ${data['b']} ({data['B']} BTC) ask: ${data['a']} ({data['A']} BTC)")
asyncio.run(stream_prices())Place an order
Subscribe to orders@account alongside the price stream, then send order.place once you see a price. Use cancelOnDisconnect=true as a query parameter to automatically cancel open orders if your connection drops.
async def trade():
url = "wss://ws.gemini.com?cancelOnDisconnect=true"
async with websockets.connect(url, additional_headers=auth_headers()) as ws:
await ws.send(json.dumps({
"id": "1",
"method": "subscribe",
"params": [
f"{SYMBOL}@bookTicker", # live prices
"orders@account", # your order updates
],
}))
# Wait for a price, then place an order
async for msg in ws:
data = json.loads(msg)
if "b" in data and "a" in data and data.get("s") == SYMBOL:
break
await ws.send(json.dumps({
"id": "2",
"method": "order.place",
"params": {
"symbol": SYMBOL,
"side": "BUY",
"type": "LIMIT",
"timeInForce": "GTC",
"price": "95000.00",
"quantity": "0.01",
"clientOrderId": "my-first-ws-order",
},
}))Listen for fills & cancel
Order lifecycle events arrive on the orders@account stream. The X field carries the status (NEW, FILLED, etc.). To cancel, send order.cancel with the order ID from the i field.
# Watch order updates
# X = status, S = side, p = price, q = quantity
async for msg in ws:
data = json.loads(msg)
if data.get("X") in ("NEW", "OPEN", "FILLED", "PARTIALLY_FILLED"):
print(f"Order {data['X']}: side={data.get('S')} price=${data.get('p')} qty={data.get('q')}")
# Cancel once filled
if data.get("X") == "FILLED":
await ws.send(json.dumps({
"id": "3",
"method": "order.cancel",
"params": { "orderId": data.get("i") },
}))
breakBTCUSD bid: $95000.00 (0.50 BTC) ask: $95001.00 (0.25 BTC)
Order NEW: side=BUY price=$95000.00 qty=0.01
Order OPEN: side=BUY price=$95000.00 qty=0.01
Order FILLED: side=BUY price=$95000.00 qty=0.01