Introduction
Gemini offers two WebSocket APIs for streaming data:
- a private Order Events API
- a public Market Data API
Advantages include:
- receive notifications in real time
- reduce the amount of data you have to transfer over the network
- reduce latency introduced by polling interval
For example, to keep track of your orders, you might be requesting the Get Active Orders endpoint every five seconds:
- request
/v1/orders
- bring back HTTP header data and a response body
- parse the JSON in the response body
- compare the latest response against the previous response to see what has changed
Using the private Order Events API, you would subscribe once and receive real time notifications of all order activity.
For general information about how the WebSocket protocol works, refer to:
Survey
Please complete our API Use Survey to help us improve your experience using the Gemini APIs.
Sandbox
Gemini's sandbox site is an instance of the Gemini Exchange that offers full exchange functionality using test funds.
- Gemini has an automated system that makes trades on the exchange to simulate normal exchange activity
- all funds are for testing purposes. Only Testnet BTC deposits and withdrawals are supported.
Sandbox URLs
Website
https://exchange.sandbox.gemini.com
REST API
https://api.sandbox.gemini.com
WebSocket Feed
wss://api.sandbox.gemini.com
Documentation
https://docs.sandbox.gemini.com
Create your account
Go to the sandbox site to register for a test account to begin trading.
- use the website to get comfortable trading on Gemini
- use the API to validate your trading systems before deploying them against the real Gemini exchange
Your account will automatically be credited with USD, BTC, ETH, BCH, LTC, OXT, LINK, BAT and DAI. You may use these funds to trade, both through the web site and through the API.
Gemini's sandbox site does not support either depositing or withdrawing your test funds, which can only be used to trade on the sandbox exchange.
Sandbox does not support email notifications. If you need this as part of your testing plan, please contact trading@gemini.com.
If you have any issues, or if you need to adjust your balances (to test insufficient funds handling, for example), contact trading@gemini.com.
Two Factor Authentication
Two factor authentication ("2FA") is enabled by default for all sandbox accounts. To disable 2FA for automated testing, please do the following:
- At the Authy 2FA entry screen, set a cookie or an HTTP header with the name GEMINI-SANDBOX-2FA. The value doesn’t matter.
- Enter 9999999 as your 2FA code
Rate Limits
To prevent abuse, Gemini imposes rate limits on incoming requests as described in the Gemini API Agreement.
For public WebSocket APIs, we recommend that you do not exceed 1 request per symbol per minute.
Requests
Both public and private WebSocket API requests begin with a GET request that includes headers asking for an upgrade to the WebSocket protocol.
However, the private API WebSocket request also includes the standard private API headers.
The Sec-WebSocket-Key and Sec-WebSocket-Version headers shown in the examples wll be added by your WebSocket client. See the WebSocket RFC for more detail.
Public API request headers
GET wss://api.gemini.com/v1/marketdata/BTCUSD
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: uRovscZjNol/umbTt5uKmw==
Sec-WebSocket-Version: 13
Private request headers
GET wss://api.gemini.com/v1/order/events
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: uRovscZjNol/umbTt5uKmw==
Sec-WebSocket-Version: 13
X-GEMINI-APIKEY: qOfnZJDZTTBsxdM3bVRP
X-GEMINI-PAYLOAD: eyJyZXF1ZXN0IjoiL3YxL29yZGVyL2V2ZW50cyIsIm5vbmNlIjoxNDc3OTYzMjQwNzQxMDgzMzA3fQ==
X-GEMINI-SIGNATURE: 88cd6f391d8f920a76a2060d613b519a8e8b4b3fb5bff089ea826d49ac73888bd479c0c2e2062ba60ba7afbe273132e3
Private API invocation
To walk through the process of generating a private API invocation, we start with the request json itself
{
"request": "/v1/order/events",
"nonce": <nonce>
}
Whitespace is ignored by the server, and may be included if desired. The hashes are always taken on the base64 string directly, with no normalization, so whatever is sent in the payload is what should be hashed, and what the server will verify.
~$ base64 << EOF
> {
> "request": "/v1/order/events",
> "nonce": <nonce>
> }
> EOF
ewogICAgInJlcXVlc3QiOiAiL3YxL29yZGVyL3N0YXR1cyIsCiAgICAibm9uY2UiOiAxMjM0NTYs
CgogICAgIm9yZGVyX2lkIjogMTg4MzQKfQo=
import ssl
import websocket
import json
import base64
import hmac
import hashlib
import time
def on_message(ws, message):
print(message)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
gemini_api_key = "mykey"
gemini_api_secret = "1234abcd".encode()
payload = {"request": "/v1/order/events","nonce": time.time()}
encoded_payload = json.dumps(payload).encode()
b64 = base64.b64encode(encoded_payload)
signature = hmac.new(gemini_api_secret, b64, hashlib.sha384).hexdigest()
ws = websocket.WebSocketApp("wss://api.sandbox.gemini.com/v1/order/events",
on_message=on_message,
header={
'X-GEMINI-PAYLOAD': b64.decode(),
'X-GEMINI-APIKEY': gemini_api_key,
'X-GEMINI-SIGNATURE': signature
})
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
In this example, the
api_secret
is1234abcd
echo -n 'ewogICAgInJlcXVlc3QiOiAiL3YxL29yZGVyL3N0YXR1cyIsCiAgICAibm9uY2UiOiAxMjM0NTYsCgogICAgIm9yZGVyX2lkIjogMTg4MzQKfQo=' | openssl sha384 -hmac "1234abcd"
(stdin)= 337cc8b4ea692cfe65b4a85fcc9f042b2e3f702ac956fd098d600ab15705775017beae402be773ceee10719ff70d710f
hmac.new("1234abcd", b64, hashlib.sha384).hexdigest()
'337cc8b4ea692cfe65b4a85fcc9f042b2e3f702ac956fd098d600ab15705775017beae402be773ceee10719ff70d710f'
The final request will look like this. The linebreaks are added for clarity, your http library may or may not put them in.
POST /v1/order/events
Content-Type: text/plain
Content-Length: 0
X-GEMINI-APIKEY: mykey
X-GEMINI-PAYLOAD:ewogICAgInJlcXVlc3QiOiAiL3YxL29yZGVyL3N
0YXR1cyIsCiAgICAibm9uY2UiOiAxMjM0NTYsCgogICAgIm9yZGV
yX2lkIjogMTg4MzQKfQo=
X-GEMINI-SIGNATURE: 337cc8b4ea692cfe65b4a85fcc9f042b2e3f
702ac956fd098d600ab15705775017beae402be773ceee10719f
f70d710f
Authentication
Gemini uses API keys to allow access to private APIs. You can obtain these by logging on and creating a key in Settings/API. This will give you both an "API Key" that will serve as your user name, and an "API Secret" that you will use to sign messages.
All requests must contain a nonce, a number that will never be repeated and must increase between requests. This is to prevent an attacker who has captured a previous request from simply replaying that request. We recommend using a timestamp at millisecond or higher precision. The nonce need only be increasing with respect to the session that the message is on.
Payload
The payload of the requests will be a JSON object, which will be described in the documentation below. Rather than being sent as the body of the POST request, it will be base-64 encoded and stored as a header in the request.
All of them will include the request name and the nonce associated with the request. The nonce must be increasing with each request to prevent replay attacks.
Header | Value |
---|---|
Content-Length | 0 |
Content-Type | text/plain |
X-GEMINI-APIKEY | Your Gemini API key |
X-GEMINI-PAYLOAD | The base64-encoded JSON payload |
X-GEMINI-SIGNATURE | hex(HMAC_SHA384(base64(payload), key=api_secret)) |
Cache-Control | no-cache |
Roles
Example of error response due to API key missing a role
{
"result":"error",
"reason":"MissingRole",
"message":"To access this endpoint, you need to log in to the website and go to the settings page to assign one of these roles [FundManager] to API key wujB3szN54gtJ4QDhqRJ which currently has roles [Trader]"
}
Gemini uses a role-based system for private API endpoints so that you can separate privileges for your API keys.
By assigning different roles to different API keys, you can create
- one API key that can trade, and
- another API key that can withdraw digital assets, or
- an API key to have access to read-only endpoints
You can configure which roles are assigned to your API keys by logging in to the Gemini Exchange website and going to API Settings to configure your API keys.
If you try to access an endpoint that requires a role you did not assign to your API key, you will get back a response with:
403
status- a JSON response body with
reason
set toMissingRole
, andmessage
explaining what role you need to add to your API key to use this endpoint
See Error Codes for more information about API error responses.
Trader
Assigning the Trader role to an API key allows this API key to:
- access the Order Events WebSocket API
Fund Manager
Gemini does not offer any WebSocket APIs for the Fund Manager role right now.
Instead, this role is used for REST API endpoints:
Auditor
Assigning the Auditor role to an API key allows this API key to:
- access the Order Events WebSocket API
Endpoint summary
Here's a summary of which role you need to assign to your API key to use each endpoint in the API:
Endpoint | URI | Trader can access? | Fund Manager can access? | Auditor can access? |
---|---|---|---|---|
Order Events | /v1/order/events |
✓ | ✗ | ✓ |
Responses
If successful, API requests will return an HTTP 101 Switching Protocols
code in the response headers:
HTTP/1.1 101 Switching Protocols
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: wEV5o5orKGO27qATSTLczquY3EH=
Then the HTTP connection will be replaced by a WebSocket connection. All API responses, both public and private, will be sent over the WebSocket connection.
Client Order ID
Order Event subscription Accepted event showing client_order_id
[ {
"type" : "accepted",
"order_id" : "372456298",
"event_id" : "372456299",
"client_order_id": "20170208_example",
"api_session" : "AeRLptFXoYEqLaNiRwv8",
"symbol" : "btcusd",
"side" : "buy",
"order_type" : "exchange limit",
"timestamp" : "1478203017",
"timestampms" : 1478203017455,
"is_live" : true,
"is_cancelled" : false,
"is_hidden" : false,
"avg_execution_price" : "0",
"original_amount" : "14.0296",
"price" : "1059.54"
} ]
Order Status endpoint for the same order, showing client_order_id
{
"avg_execution_price": "0.00",
"client_order_id": "20170208_example",
"exchange": "gemini",
"executed_amount": "0",
"id": "372456298",
"is_cancelled": false,
"is_hidden": false,
"is_live": true,
"order_id": "372456298",
"original_amount": "14.0296",
"price": "1059.54",
"remaining_amount": "14.0296",
"side": "buy",
"symbol": "btcusd",
"timestamp": "1478203017",
"timestampms": 1478203017455,
"type": "exchange limit",
"was_forced": false
}
Client order ID is a client-supplied order identifier that Gemini will echo back to you in all subsequent messages about that order.
Although this identifier is optional, Gemini strongly recommends supplying client_order_id
when placing orders using the New Order endpoint.
This makes it easy to track the Order Events: Accepted and Order Events: Booked responses in your Order Events WebSocket subscription.
Visibility
Your client order ids are only visible to the Gemini exchange and you. They are never visible on any public API endpoints.
Uniqueness
Gemini recommends that your client order IDs should be unique per trading session.
Allowed characters
Your client order ids should match against this PCRE regular expression: [:\-_\.#a-zA-Z0-9]{1,100}
.
Characters | Description | ASCII Codes (Dec) |
---|---|---|
A-Z |
Uppercase A-Z | 65 - 90 |
a-z |
Lowercase a-z | 97 - 122 |
0-9 |
Digits | 48 - 57 |
# |
Hash, octothorpe, number sign | 35 |
- |
Hyphen | 45 |
. |
Period | 46 |
: |
Colon | 58 |
_ |
Underscore | 95 |
Data Types
The protocol description below will contain references to various types, which are collected here for reference
Type | Description |
---|---|
string | A simple quoted string, following standard JSON rules; see the JSON spec for details. |
decimal | A decimal value, encoded in a JSON string. The contents will be a series of digits, followed by an optional decimal point and additional digits. |
timestamp | The number of seconds since 1970-01-01 UTC. This is usually provided for compatibility; implementors should use the more precise timestampms when available. When used as an input, either the millisecond or second precision is usable; this is unambiguous for dates past 1/29/1970 |
timestampms | The number of milliseconds since 1970-01-01 UTC. The begin date is the standard UNIX epoch, so this will be 1000 times the UNIX timestamp in seconds. This will be transmitted as a JSON number, not a string. |
integer | An whole number, transmitted as a JSON number. |
boolean | A JSON boolean, the literal string true or false |
array | a JSON array. Each element contains a payload that will be described. |
Timestamps
The timestamp data type describes a date and time as a whole number in Unix Time format, as the number of seconds or milliseconds since 1970-01-01 UTC.
Requests
When timestamp is supplied as a request parameter, the following two values are supported in order of preference:
- The number of milliseconds since 1970-01-01 UTC
- The number of seconds since 1970-01-01 UTC (unix epoch)
For your convenience, a POST
request may supply the timestamp
parameter in a JSON payload as a string instead of a number.
Timestamp format | example | Supported request type |
---|---|---|
whole number (seconds) | 1495127793 |
GET , POST |
string (seconds) | "1495127793" |
POST only |
whole number (milliseconds) | 1495127793000 |
GET , POST |
string (milliseconds) | "1495127793000" |
POST only |
Behavior
If the timestamp
parameter is not present, the default behavior is to return the most recent items in the list. For example, the public Trade History endpoint will return the most recent trades without a timestamp
parameter.
The first trade on Gemini occurred at 1444311607801
milliseconds. Any request for a timestamp value before this is the same as requesting the very first item in the list.
You may choose to supply a timestamp of 0
to get the first trade in the list when retrieving trades historically.
If unable to parse your timestamp
value, the exchange will return an InvalidTimestampInPayload
error.
Responses
In a JSON response, the key
timestamp
denotes the number of seconds since 1970-01-01 UTCtimestampms
denotes the number of milliseconds since 1970-01-01 UTC
For backwards compatibility, some but not all timestamp values will be supplied in seconds.
Basis Point
We calculate fees as a fraction of the notional value of each trade (i.e., price × amount). We use units of basis points (“bps”), which represent 1/100th of a percent of notional value. For example, a fee of 25 bps means that 0.25% of the denominated value of the trade will be kept by our exchange, either deducted from the gross proceeds of a trade or charged to the account at the time a trade is executed. Any fees will be applied at the time an order is placed. For partially filled orders, only the executed portion is subject to trading fees.
For more information see Fee Calculation.
Symbols and minimums
Symbols are formatted as CCY1CCY2
where prices are in CCY2
and quantities are in CCY1
. CCY1
is in the Currency
column and CCY2
is in the respective CCY2 Price Increment
column:
Symbol | Minimum Order Size | Tick Size | Quote Currency Price Increment |
---|---|---|---|
btcusd | 0.00001 BTC (1e-5) | 0.00000001 BTC (1e-8) | 0.01 USD |
btceur | 0.00001 BTC (1e-5) | 0.00000001 BTC (1e-8) | 0.01 EUR |
btcgbp | 0.00001 BTC (1e-5) | 0.00000001 BTC (1e-8) | 0.01 GBP |
btcsgd | 0.00001 BTC (1e-5) | 0.00000001 BTC (1e-8) | 0.01 SGD |
ethbtc | 0.001 ETH (1e-3) | 0.000001 ETH (1e-6) | 0.00001 BTC (1e-5) |
ethusd | 0.001 ETH (1e-3) | 0.000001 ETH (1e-6) | 0.01 USD |
etheur | 0.001 ETH (1e-3) | 0.000001 ETH (1e-6) | 0.01 EUR |
ethgbp | 0.001 ETH (1e-3) | 0.000001 ETH (1e-6) | 0.01 GBP |
ethsgd | 0.001 ETH (1e-3) | 0.000001 ETH (1e-6) | 0.01 SGD |
bchusd | 0.001 BCH (1e-3) | 0.000001 BCH (1e-6) | 0.01 USD |
bchbtc | 0.001 BCH (1e-3) | 0.000001 BCH (1e-6) | 0.00001 BTC (1e-5) |
bcheth | 0.001 BCH (1e-3) | 0.000001 BCH (1e-6) | 0.0001 ETH (1e-4) |
ltcusd | 0.01 LTC (1e-2) | 0.00001 LTC (1e-5) | 0.01 USD |
ltcbtc | 0.01 LTC (1e-2) | 0.00001 LTC (1e-5) | 0.0000001 BTC (1e-7) |
ltceth | 0.01 LTC (1e-2) | 0.00001 LTC (1e-5) | 0.00001 ETH (1e-5) |
ltcbch | 0.01 LTC (1e-2) | 0.00001 LTC (1e-5) | 0.0001 BCH (1e-4) |
batusd | 1.0 BAT (1e0) | 0.000001 BAT (1e-6) | 0.00001 USD (1e-5) |
daiusd | 0.1 DAI (1e-1) | 0.000001 DAI (1e-6) | 0.00001 USD (1e-5) |
linkusd | 0.1 LINK (1e-1) | 0.000001 LINK (1e-6) | 0.00001 USD (1e-5) |
oxtusd | 1.0 OXT (1e0) | 0.000001 OXT (1e-6) | 0.00001 USD (1e-5) |
linkbtc | 0.1 LINK (1e-1) | 0.000001 LINK (1e-6) | 0.00000001 BTC (1e-8) |
linketh | 0.1 LINK (1e-1) | 0.000001 LINK (1e-6) | 0.0000001 ETH (1e-7) |
ampusd | 10.0 AMP (1e1) | 0.000001 AMP (1e-6) | 0.00001 USD (1e-5) |
compusd | 0.001 COMP (1e-3) | 0.000001 COMP (1e-6) | 0.01 USD |
paxgusd | 0.0001 PAXG (1e-4) | 0.00000001 PAXG (1e-8) | 0.01 USD |
mkrusd | 0.001 MKR (1e-3) | 0.000001 MKR (1e-6) | 0.01 USD |
zrxusd | 0.1 ZRX (1e-1) | 0.000001 ZRX (1e-6) | 0.00001 USD (1e-5) |
manausd | 1.0 MANA (1e0) | 0.000001 MANA (1e-6) | 0.00001 USD (1e-5) |
storjusd | 0.1 STORJ (1e-1) | 0.000001 STORJ (1e-6) | 0.00001 USD (1e-5) |
crvusd | 0.1 CRV (1e-1) | 0.000001 CRV (1e-6) | 0.0001 USD (1e-4) |
uniusd | 0.01 UNI (1e-2) | 0.000001 UNI (1e-6) | 0.0001 USD (1e-4) |
renusd | 0.01 REN (1e-2) | 0.000001 REN (1e-6) | 0.00001 USD (1e-5) |
umausd | 0.01 UMA (1e-2) | 0.000001 UMA (1e-6) | 0.0001 USD (1e-4) |
yfiusd | 0.00001 YFI (1e-5) | 0.000001 YFI (1e-6) | 0.01 USD |
aaveusd | 0.001 AAVE (1e-3) | 0.000001 AAVE (1e-6) | 0.0001 USD (1e-4) |
filusd | 0.1 FIL (1e-1) | 0.000001 FIL (1e-6) | 0.0001 USD (1e-4) |
sklusd | 0.1 SKL (1e-1) | 0.000001 SKL (1e-6) | 0.00001 USD (1e-5) |
grtusd | 0.1 GRT (1e-1) | 0.000001 GRT (1e-6) | 0.0001 USD (1e-4) |
lrcusd | 0.1 LRC (1e-1) | 0.000001 LRC (1e-6) | 0.00001 USD (1e-5) |
sandusd | 0.1 SAND (1e-1) | 0.000001 SAND (1e-6) | 0.00001 USD (1e-5) |
cubeusd | 0.01 CUBE (1e-2) | 0.000001 CUBE (1e-6) | 0.0001 USD (1e-4) |
lptusd | 0.001 LPT (1e-3) | 0.000001 LPT (1e-6) | 0.0001 USD (1e-4) |
maticusd | 0.1 MATIC (1e-1) | 0.000001 MATIC (1e-6) | 0.00001 USD (1e-5) |
injusd | 0.01 INJ (1e-2) | 0.000001 INJ (1e-6) | 0.0001 USD (1e-4) |
sushiusd | 0.01 SUSHI (1e-2) | 0.000001 SUSHI (1e-6) | 0.0001 USD (1e-4) |
dogeusd | 0.1 DOGE (1e-1) | 0.000001 DOGE (1e-6) | 0.00001 USD (1e-5) |
ftmusd | 0.03 FTM (3e-2) | 0.000001 FTM (1e-6) | 0.0001 USD (1e-4) |
ankrusd | 0.1 ANKR (1e-1) | 0.000001 ANKR (1e-6) | 0.00001 USD (1e-5) |
btcgusd | 0.00001 BTC (1e-5) | 0.00000001 BTC (1e-8) | 0.01 GUSD |
ethgusd | 0.001 ETH (1e-3) | 0.000001 ETH (1e-6) | 0.01 GUSD |
ctxusd | 0.002 CTX (2e-3) | 0.000001 CTX (1e-6) | 0.0001 USD (1e-4) |
xtzusd | 0.02 XTZ (2e-2) | 0.000001 XTZ (1e-6) | 0.0001 USD (1e-4) |
axsusd | 0.003 AXS (3e-3) | 0.000001 AXS (1e-6) | 0.01 USD (1e-2) |
dogebtc | 1.0 DOGE (1e0) | 0.00000001 DOGE (1e-8) | 0.000000001 BTC (1e-9) |
dogeeth | 1.0 DOGE (1e0) | 0.00000001 DOGE (1e-8) | 0.00000001 ETH (1e-8) |
rareusd | 0.1 RARE (1e-1) | 0.000001 RARE (1e-6) | 0.001 USD (1e-3) |
qntusd | 0.0004 QNT (4e-4) | 0.000001 QNT (1e-6) | 0.01 USD (1e-2) |
maskusd | 0.01 MASK (1e-2) | 0.000001 MASK (1e-6) | 0.001 USD (1e-3) |
fetusd | 0.1 FET (1e-1) | 0.000001 FET (1e-6) | 0.00001 USD (1e-5) |
api3usd | 0.03 API3 (3e-2) | 0.000001 API3 (1e-6) | 0.001 USD (1e-3) |
usdcusd | 0.1 USDC (1e-1) | 0.000001 USDC (1e-6) | 0.00001 USD (1e-5) |
shibusd | 1000.0 SHIB (1e3) | 0.000001 SHIB (1e-6) | 0.000000001 USD (1e-9) |
rndrusd | 0.02 RNDR (2e-2) | 0.000001 RNDR (1e-6) | 0.001 USD (1e-3) |
galausd | 0.4 GALA (4e-1) | 0.000001 GALA (1e-6) | 0.00001 USD (1e-5) |
ensusd | 0.002 ENS (2e-3) | 0.000001 ENS (1e-6) | 0.001 USD (1e-3) |
tokeusd | 0.002 TOKE (2e-3) | 0.000001 TOKE (1e-6) | 0.001 USD (1e-3) |
ldousd | 0.02 LDO (2e-2) | 0.000001 LDO (1e-6) | 0.001 USD (1e-3) |
rlyusd | 0.2 RLY (2e-1) | 0.000001 RLY (1e-6) | 0.00001 USD (1e-5) |
solusd | 0.001 SOL (1e-3) | 0.000001 SOL (1e-6) | 0.001 USD (1e-3) |
apeusd | 0.02 APE (2e-2) | 0.000001 APE (1e-6) | 0.001 USD (1e-3) |
gusdsgd | 0.1 GUSD | 0.000001 GUSD (1e-6) | 0.001 SGD (1e-3) |
chzusd | 0.5 CHZ (5e-1) | 0.000001 CHZ (1e-6) | 0.00001 USD (1e-5) |
jamusd | 10.0 JAM (1e1) | 0.000001 JAM (1e-6) | 0.0000001 USD (1e-7) |
gmtusd | 0.1 GMT (1e-1) | 0.000001 GMT (1e-6) | 0.00001 USD (1e-5) |
aliusd | 2.0 ALI (2e0) | 0.000001 ALI (1e-6) | 0.000001 USD (1e-6) |
gusdgbp | 0.1 GUSD | 0.0001 GUSD (1e-4) | 0.001 GBP (1e-3) |
dotusd | 0.01 DOT (1e-2) | 0.000001 DOT (1e-6) | 0.0001 USD (1e-4) |
ernusd | 0.05 ERN (5e-2) | 0.000001 ERN (1e-6) | 0.0001 USD (1e-4) |
elonusd | 60000.0 ELON (6e4) | 0.000001 ELON (1e-6) | 0.00000000001 USD (1e-11) |
galusd | 0.04 GAL (4e-2) | 0.000001 GAL (1e-6) | 0.0001 USD (1e-4) |
samousd | 10.0 SAMO (1e+1) | 0.000001 SAMO (1e-6) | 0.0000001 USD (1e-7) |
imxusd | 0.1 IMX (1e-1) | 0.000001 IMX (1e-6) | 0.00001 USD (1e-5) |
iotxusd | 3.0 IOTX (3e+0) | 0.000001 IOTX (1e-6) | 0.000001 USD (1e-6) |
avaxusd | 0.005 AVAX (5e-3) | 0.000001 AVAX (1e-6) | 0.001 USD (1e-3) |
atomusd | 0.01 ATOM (1e-2) | 0.000001 ATOM (1e-6) | 0.001 USD (1e-3) |
usdtusd* | 0.1 USDT (1e-1) | 0.000001 USDT (1e-6) | 0.0001 USD (1e-4) |
btcusdt* | 0.00001 BTC (1e-5) | 0.00000001 BTC (1e-8) | 0.01 USDT (1e-2) |
ethusdt* | 0.001 ETH (1e-3) | 0.000001 ETH (1e-6) | 0.01 USDT (1e-2) |
pepeusd | 1000 PEPE (1e3) | 0.000001 PEPE (1e-6) | 0.000000001 USD (1e-9) |
xrpusd | 0.1 XRP (1e-1) | 0.000001 XRP (1e-6) | 0.00001 USD (1e-5) |
hntusd | 0.04 HNT (4e-1) | 0.000001 HNT (1e-6) | 0.0001 USD (1e-4) |
wifusd | 0.07 WIF (7e-2) | 0.000001 WIF (1e-6) | 0.0001 USD (1e-4) |
bonkusd | 4000 BONK (4e3) | 0.000001 BONK (1e-6) | 0.000000001 USD (1e-9) |
popcatusd | 0.07 POPCAT (7e-2) | 0.000001 POPCAT (1e-6) | 0.0001 USD (1e-4) |
opusd | 0.07 OP (7e-2) | 0.000001 OP (1e-6) | 0.0001 USD (1e-4) |
moodeng | 1 MOODENG (1) | 0.000001 MOODENG (1e-6) | 0.000001 USD (1e-6) |
pnutusd | 0.2 PNUT (2e-1) | 0.0001 PNUT (1e-4) | 0.0001 USD (1e-4) |
goatusd | 0.1 GOAT (1e-1) | 0.000001 GOAT (1e-6) | 0.0001 USD (1e-4) |
mewusd | 10 MEW (1e1) | 0.01 MEW (1e-2) | 0.000001 USD (1e-6) |
bomeusd | 10 BOME (1e1) | 0.01 BOME (1e-2) | 0.000001 USD (1e-6) |
*Note: Not available to trade for customers in New York
All Supported Symbols
btcusd
ethbtc
ethusd
bchusd
bchbtc
bcheth
ltcusd
ltcbtc
ltceth
ltcbch
batusd
daiusd
linkusd
oxtusd
linkbtc
linketh
ampusd
compusd
paxgusd
mkrusd
zrxusd
manausd
storjusd
crvusd
uniusd
renusd
umausd
yfiusd
aaveusd
filusd
btceur
btcgbp
etheur
ethgbp
btcsgd
ethsgd
sklusd
grtusd
lrcusd
sandusd
cubeusd
lptusd
maticusd
injusd
sushiusd
dogeusd
ftmusd
ankrusd
btcgusd
ethgusd
ctxusd
xtzusd
axsusd
dogebtc
dogeeth
rareusd
qntusd
maskusd
fetusd
api3usd
usdcusd
shibusd
rndrusd
galausd
ensusd
elonusd
tokeusd
ldousd
rlyusd
solusd
apeusd
gusdsgd
chzusd
jamusd
gmtusd
aliusd
gusdgbp
dotusd
ernusd
galusd
samousd
imxusd
iotxusd
avaxusd
atomusd
usdtusd
btcusdt
ethusdt
pepeusd
xrpusd
hntusd
wifusd
bonkusd
popcatusd
opusd
moodeng
pnutusd
goatusd
mewusd
bomeusd
Sequence numbers
So you can easily ensure that you are receiving all of your WebSocket messages in the expected order without any gaps, events and heartbeats contain a special sequence number.
- WebSocket connection is established
- Optional: subscription acknowledgement, such as Order Events: Subscription Acknowledgement
- Your subscription begins - you receive your first event with
socket_sequence
set to a value of0
- For all further messages, each message - whether a heartbeat or an event - should increase this sequence number by one.
- If you see a gap in this sequence number, then you should disconnect and reconnect.
Please note:
- Each time you reconnect, the sequence number resets to zero.
- If you have multiple WebSocket connections, each will have a separate sequence number beginning with zero - make sure to keep track of each sequence number separately!
Order Events
Order events is a private API that gives you information about your orders in real time.
When you connect, you get a book of your active orders. Then in real time you'll get information about order events like:
- when your orders are accepted by the exchange
- when your orders first appear on the book
- fills
- cancels
and more.
You can use optional subscription filters to tailor your WebSocket feed to suit your individual needs. You can even create multiple WebSocket feeds for multiple purposes.
Event Types
-
- Entry Type
- Can be filtered?
- Description
-
subscription_ack
- N
- Acknowledges your order events subscription and echoes back your parsed filters.
-
heartbeat
- Y
- Sent at five-second intervals to show that your WebSocket connection to Gemini is alive. This is filtered independently from the
eventTypeFilter
asheartbeat=true
. The default for theheartbeat
parameter istrue
.
-
initial
- Y
- At the time you begin your subscription, you receive a list of your current active orders. Each active order will have the
initial
event type. You only see this event type at the beginning of each subscription.
-
accepted
- Y
- Acknowledges that the exchange has received your order for initial processing. An order which cannot be accepted for initial processing receives a
rejected
event.
-
rejected
- Y
- When an order is rejected.
booked
- Y
Your order is now visible on the Gemini order book. Under certain conditions, when you place an order you will not receive a
booked
event. These include:- When your order is completely filled after being accepted
- When your order is accepted for initial processing but then immediately cancelled because some condition cannot be fulfilled (for instance, if you submit a maker-or-cancel order but your order would cross)
fill
- Y
- When an order is filled.
cancelled
- Y
- When an order is cancelled.
cancel_rejected
- Y
When your request to cancel an order cannot be fulfilled. Reasons this might happen include:
- The order cannot be found
closed
- Y
- The last event in the order lifecycle: whether this order was completely filled or cancelled, the consumer can use the
closed
event as a signal that the order is off the book on the Gemini side.
Workflow
- Client submits order to Gemini exchange
- Is the order accepted?
- Yes, order is accepted
- Gemini sends an
accepted
order event - Gemini sends zero or more initial
fill
events - Does the order have non-zero remaining quantity?
- Yes, the order has non-zero remaining quantity
- Gemini sends a
booked
event - the order rests until
- client sends a cancel request
- Is the order cancelled?
- Yes, the order is cancelled
- Gemini sends a
cancelled
event followed by aclosed
event - No further order events about this order
- Gemini sends a
- No, the cancel request could not be fulfilled
- Gemini sends a
cancel_rejected
event explaining why the order cancel request could not be fulfilled. - The order continues to rest on the books
- Gemini sends a
- Yes, the order is cancelled
- Is the order cancelled?
- a trade executes, partially or completely filling the order
- Gemini sends a
fill
event with details about the trade, including remaining quantity - Is the order completely filled?
- Yes, the order is completely filled
- Gemini sends a
closed
event - No further order events about this order
- Gemini sends a
- No, the order has remaining quantity
- The order continues to rest on the books
- Yes, the order is completely filled
- Gemini sends a
- client sends a cancel request
- Gemini sends a
- No, the order has been completely filled
- Gemini sends a
closed
event - No further order events about this order
- Gemini sends a
- Yes, the order has non-zero remaining quantity
- Gemini sends an
- No, order is rejected
- Gemini responds with a
rejected
order event explaining why the order was rejected - No further order events about this order
- Gemini responds with a
- Yes, order is accepted
Keeping track of your orders
When you place an order using the New Order endpoint, supply your identifier in the client_order_id
field.
The Order Events API will then echo it back to you in every single message about this order.
See Client Order ID for more information.
You can use the account_name
field, if using a Master scoped API key.
How Filtering Works
Filtering is completely optional. If you don't specify any filters when you connect, you'll see all your order events: for every symbol, every API session and the UI, every event type.
If you want to filter, it's simple. Filtering works by whitelisting. You can filter on any combination of the following criteria:
- one or more supported symbols
- one or more of your API session keys
- use
UI
as the session key for orders placed through the website
- use
- one or more order event types
- if you don't specify
initial
, you will not receive your active orders at the beginning of the subscription
- if you don't specify
You may create multiple connections, filtered any way you like.
To provide a list of arguments, repeat the parameter once for each argument you want to provide:
wss://api.gemini.com/v1/order/events?symbolFilter=btcusd&symbolFilter=ethbtc&eventTypeFilter=initial&eventTypeFilter=fill&eventTypeFilter=closed
For example, if you wanted to see all BTCUSD order event types for both API session key t14phVqvAAJlK4YiXmBM
and your web users, you would subscribe to order events using this WebSocket URL:
wss://api.gemini.com/v1/order/events?apiSessionFilter=t14phVqvAAJlK4YiXmBM&&apiSessionFilter=UI&symbolFilter=btcusd
You would neither see orders for a different currency nor orders placed by a different API session key than the ones you specified.
If you wanted to create a dropcopy-like event feed, to see order fills for all orders associated with your account on all symbols, connect using:
wss://api.gemini.com/v1/order/events?eventTypeFilter=fill
To see your active orders, fills, and cancels:
wss://api.gemini.com/v1/order/events?eventTypeFilter=initial&eventTypeFilter=fill&eventTypeFilter=canceled
In general, if you create a custom event type filter, Gemini recommends always including initial
: otherwise, you won't see your active orders when you connect.
WebSocket Request
npm install -g wscat
wscat -H X-GEMINI-PAYLOAD:your-payload \
-H X-GEMINI-APIKEY:your-api-key \
-H X-GEMINI-SIGNATURE:your-signature \
--connect wss://api.gemini.com/v1/order/events
import ssl
import websocket
import json
import base64
import hmac
import hashlib
import time
def on_message(ws, message):
print(message)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
gemini_api_key = "mykey"
gemini_api_secret = "1234abcd".encode()
payload = {"request": "/v1/order/events","nonce": time.time()}
encoded_payload = json.dumps(payload).encode()
b64 = base64.b64encode(encoded_payload)
signature = hmac.new(gemini_api_secret, b64, hashlib.sha384).hexdigest()
ws = websocket.WebSocketApp("wss://api.gemini.com/v1/order/events?symbolFilter=btcusd&eventTypeFilter=fill&eventTypeFilter=closed&apiSessionFilter=UI&heartbeat=true",
on_message=on_message,
header={
'X-GEMINI-PAYLOAD': b64.decode(),
'X-GEMINI-APIKEY': gemini_api_key,
'X-GEMINI-SIGNATURE': signature
})
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
wss://api.gemini.com/v1/order/events
Roles
The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.
NOTE: Using a Master scoped API key receives event data for all accounts in the group.
Headers
Your WebSocket request needs to include these three headers:
Header | Value |
---|---|
X-GEMINI-APIKEY |
Your Gemini API session key |
X-GEMINI-PAYLOAD |
Before base64-encoding, the JSON payload for the X-GEMINI-PAYLOAD header looks like this, where 123456 is a valid nonce value for your account.{ "request": "/v1/order/events", "nonce": 123456 } |
X-GEMINI-SIGNATURE |
See Private API Invocation for an explanation of how to create the signature hash. |
URL Parameters
Parameter | Type | Required? | Description |
---|---|---|---|
symbolFilter |
string | N | Optional symbol filter for order event subscription |
apiSessionFilter |
string | N | Optional API session key filter for order event subscription |
eventTypeFilter |
string | N | Optional order event type filter for order event subscription |
heartbeat |
boolean | N | Optional filter to stream heartbeats. The default for this parameter is false . |
Response
Limit Order accepted
[{
"type": "accepted",
"order_id": "109535951",
"event_id": "109535952",
"account_name": "primary",
"api_session": "UI",
"symbol": "btcusd",
"side": "buy",
"order_type": "exchange limit",
"timestamp": "1547742904",
"timestampms": 1547742904989,
"is_live": true,
"is_cancelled": false,
"is_hidden": false,
"original_amount": "1",
"price": "3592.00",
"socket_sequence": 13
}]
Market Buy accepted
[{
"type": "accepted",
"order_id": "109964529",
"event_id": "109964530",
"account_name": "primary",
"api_session": "UI",
"symbol": "bchusd",
"side": "buy",
"order_type": "market buy",
"timestamp": "1547756076",
"timestampms": 1547756076644,
"is_live": false,
"is_cancelled": false,
"is_hidden": false,
"total_spend": "200.00",
"socket_sequence": 29
}]
Market Sell accepted
[{
"type": "accepted",
"order_id": "109964616",
"event_id": "109964617",
"account_name": "primary",
"api_session": "UI",
"symbol": "ethusd",
"side": "sell",
"order_type": "market sell",
"timestamp": "1547756893",
"timestampms": 1547756893937,
"is_live": true,
"is_cancelled": false,
"is_hidden": false,
"original_amount": "25",
"socket_sequence": 26
}]
Maker-or-cancel limit order accepted
[{
"type": "accepted",
"order_id": "109964647",
"event_id": "109964648",
"account_name": "primary",
"api_session": "UI",
"symbol": "ethusd",
"side": "sell",
"order_type": "exchange limit",
"timestamp": "1547757139",
"timestampms": 1547757139783,
"is_live": true,
"is_cancelled": false,
"is_hidden": false,
"original_amount": "5",
"price": "122.30",
"behavior": "maker-or-cancel",
"socket_sequence": 16
}]
Immediate-or-cancel limit order accepted, then immediately filled and closed
[{
"type" : "accepted",
"order_id" : "652164",
"event_id" : "652165",
"account_name": "primary",
"api_session" : "UI",
"symbol" : "btcusd",
"side" : "buy",
"order_type" : "exchange limit",
"timestamp" : "1478790127",
"timestampms" : 1478790127297,
"is_live" : true,
"is_cancelled" : false,
"is_hidden" : false,
"original_amount" : "2",
"price" : "714.01",
"behavior" : "immediate-or-cancel",
"socket_sequence" : 131419
}]
...
[{
"type" : "fill",
"order_id" : "652164",
"account_name": "primary",
"api_session" : "UI",
"symbol" : "btcusd",
"side" : "buy",
"order_type" : "exchange limit",
"timestamp" : "1478790127",
"timestampms" : 1478790127297,
"is_live" : false,
"is_cancelled" : false,
"is_hidden" : false,
"avg_execution_price" : "714.00",
"executed_amount" : "2",
"remaining_amount" : "0",
"original_amount" : "2",
"price" : "714.01",
"fill" : {
"trade_id" : "652166",
"liquidity" : "Taker",
"price" : "714.00",
"amount" : "2",
"fee" : "3.57",
"fee_currency" : "USD"
},
"socket_sequence" : 131420
}, {
"type" : "closed",
"order_id" : "652164",
"event_id" : "652168",
"account_name": "primary",
"api_session" : "UI",
"symbol" : "btcusd",
"side" : "buy",
"order_type" : "exchange limit",
"timestamp" : "1478790127",
"timestampms" : 1478790127297,
"is_live" : false,
"is_cancelled" : false,
"is_hidden" : false,
"avg_execution_price" : "714.00",
"executed_amount" : "2",
"remaining_amount" : "0",
"original_amount" : "2",
"price" : "714.01",
"socket_sequence" : 131421
}]
Once your WebSocket session is established, you will receive:
- a subscription acknowledgement
- a list of your active orders, if you supply either
- no
eventTypeFilter
, or - an explicit
eventTypeFilter=initial
argument as part of one or moreeventTypeFilter
arguments in your WebSocket request
- no
- ongoing order events interspersed with heartbeats every five seconds
- A response is a JSON array containing one or more order event objects
- Order events will arrive in real time, in the sequence that they happened on the exchange
- Each order event pertains to a single order but a batch of order events may contain order events pertaining to multiple orders.
- Use the
order_id
field in the initial order to keep track of what's happening to each order - Heartbeats are never batched with other order events
Common fields
These fields are common to all order events except subscription_ack
and heartbeat
.
-
- Field
- Type
- Required?
- Description
-
type
- string
- Y
- An order event type, e.g.
accepted
,booked
,fill
,cancelled
-
socket_sequence
- integer
- Y
- zero-indexed monotonic increasing sequence number attached to each message sent - if there is a gap in this sequence, you have missed a message. See Sequence Numbers for more information.
-
order_id
- string
- Y
- The order id that Gemini has assigned to this order, first provided in the
accepted
orrejected
message. All further events (fill
,booked
,cancelled
,closed
) will refer to thisorder_id
-
event_id
- string
- Y*
- The event id associated with this specific order event.
event_id
is supplied for every order event type except theinitial
events that supply the active orders when you initially subscribe.
-
account_name
- string
- N
- Returned when using a Master scoped API key. The name of the account within the subaccount group. Master API keys can get all account names using the Get Accounts endpoint.
-
api_session
- string
- Y*
- The API session key associated with this order.
- Orders placed by website users show
UI
as the API session key.
- Orders placed by website users show
-
client_order_id
- Client Order ID
- N
- The optional client-specified order id
-
symbol
- string
- Y
- The symbol of the order
-
side
- string
- Y
- Either
buy
orsell
-
behavior
- string
- N
- When limit orders are placed with
immediate-or-cancel
,fill-or-kill
, ormaker-or-cancel
behavior, this field will indicate the order behavior.
-
order_type
- string
- Y
- Description of the order
-
timestamp
- string
- Y
- The timestamp the order was submitted. Note that for compatibility reasons, this is returned as a string. We recommend using the
timestampms
field instead.
-
timestampms
- timestampms
- Y
- The timestamp the order was submitted in milliseconds.
-
is_live
- boolean
- Y
true
if the order is active on the book (has remaining quantity and has not been canceled)
-
is_cancelled
- boolean
- Y
true
if the order has been canceled. Note the spelling, "cancelled" instead of "canceled". This is for compatibility reasons.
-
is_hidden
- boolean
- Y
true
if the order is active but not visible
-
avg_execution_price
- decimal
- N
- The average price at which this order as been executed so far. 0 if the order has not been executed at all.
-
executed_amount
- decimal
- N
- The amount of the order that has been filled.
-
remaining_amount
- decimal
- N
- The amount of the order that has not been filled. Present for limit and market sell orders; absent for market buy orders.
-
original_amount
- decimal
- N
- For limit orders and market sells, the quantity the order was placed for. Not present for market buys.
-
price
- decimal
- N
- For limit orders, the price the order was placed for. Not present for market buys and sells.
-
total_spend
- decimal
- N
- For market buys, the total spend (fee-inclusive notional value) the order was placed for. Not present for limit orders and market sells.
Subscription acknowledgement
Subscription acknowledgement response:
{
"type": "subscription_ack",
"accountId": 5365,
"subscriptionId": "ws-order-events-5365-b8bk32clqeb13g9tk8p0",
"symbolFilter": [
"btcusd"
],
"apiSessionFilter": [
"UI"
],
"eventTypeFilter": [
"fill",
"closed"
]
}
The first message you receive acknowledges your subscription.
Compare the filters to the ones you requested to make sure your request was parsed as you expected.
Field | Type | Required? | Description |
---|---|---|---|
type |
order event type | Y | subscription_ack |
accountId |
integer | Y | The account id associated with the API session key you supplied in your X-GEMINI-APIKEY header. When using a master API key, it will be the account group id associated with the API session key. See Private API Invocation for more details. |
subscriptionId |
string | Y | The id associated with this websocket subscription; the component after the last dash is a request trace id that will be echoed back in the heartbeat traceId field. |
symbolFilter |
string array | Y | An array of zero or more supported symbols. An empty array means your subscription is not filtered by symbol. |
apiSessionFilter |
string array | Y | An array of zero or more API session keys associated with your account. The string "UI" means you want to see orders placed by your website users. An empty array means you want to see all orders on your account, regardless of whether they were placed via the API or the website. |
eventTypeFilter |
string array | Y | An array of zero or more order event types. An empty array means your subscription is not filtered by event type. |
Heartbeats
Heartbeat response
{
"type": "heartbeat",
"timestampms": 1547742998508,
"sequence": 31,
"trace_id": "b8biknoqppr32kc7gfgg",
"socket_sequence": 37
}
Gemini will send a heartbeat every five seconds so you'll know your WebSocket connection is active.
Gemini recommends logging and retaining all heartbeat messages. If your WebSocket connection is unreliable, please contact Gemini support with this log.
Field | Type | Required? | Description |
---|---|---|---|
type |
order event type | N | heartbeat . This can be set to true in the [heartbeat URL parameter]((#order-events). Defaults to true |
timestampms |
timestampms | Y | Gemini adds a timestamp so if you get disconnected, you may contact Gemini support with the timestamp of the last heartbeat you received. |
sequence |
integer | Y | Gemini adds a monotonically incrementing sequence to make it easy to tell if you've missed a heartbeat. Not the same as socket_sequence ! |
socket_sequence |
integer | Y | zero-indexed monotonic increasing sequence number attached to each message sent - if there is a gap in this sequence, you have missed a message. See Sequence Numbers for more information. |
trace_id |
string | Y | Gemini adds a trace id to each WebSocket request that our networking team can use to trace your request in our logs. |
Active Orders
Your active orders
[ {
"type": "initial",
"order_id": "109939984",
"account_name": "primary",
"api_session": "myapikey",
"symbol": "btcusd",
"side": "sell",
"order_type": "exchange limit",
"timestamp": "1547754474",
"timestampms": 1547754474438,
"is_live": true,
"is_cancelled": false,
"is_hidden": false,
"avg_execution_price": "0.00",
"executed_amount": "0",
"remaining_amount": "1",
"original_amount": "1",
"price": "3631.23",
"socket_sequence": 0
}, {
"type": "initial",
"order_id": "109940168",
"api_session": "UI",
"symbol": "solusd",
"side": "buy",
"order_type": "exchange limit",
"timestamp": "1547754480",
"timestampms": 1547754480759,
"is_live": true,
"is_cancelled": false,
"is_hidden": false,
"avg_execution_price": "0.00",
"executed_amount": "0",
"remaining_amount": "1",
"original_amount": "1",
"price": "53.83",
"socket_sequence": 1
} ]
The next group of messages you receive shows all your current active orders at the time you subscribed. (Unless you've chosen to filter out the initial
order event type, in which case you will not receive these messages.)
Field | Type | Required? | Description |
---|---|---|---|
type |
order event type | Y | initial |
Accepted
When you place an order on the exchange, Gemini acknowledges that your order has been accepted for initial processing by sending you an accepted
event.
Your order is now live on the exchange. Possible outcomes are:
- if your limit order can be immediately filled and you have not chosen maker-or-cancel behavior, you will get zero or more initial
fill
events, followed by- if your order has remaining quantity, then you will receive a
booked
event to let you know that the remaining quantity is now visible on the exchange - if your order has been completely filled, you will receive a
closed
event
- if your order has remaining quantity, then you will receive a
- if your limit order includes behavior such as maker-or-cancel which cannot be fulfilled,
cancelled
and thenclosed
- market orders will be immediately filled with one or more
fill
events followed by aclosed
event
Field | Type | Required? | Description |
---|---|---|---|
type |
order event type | Y | accepted |
Rejected
This order was rejected because its price does not conform to the market price tick specified in Symbols and minimums.
[ {
"type" : "rejected",
"order_id" : "104246",
"event_id" : "104247",
"reason" : "InvalidPrice",
"account_name": "primary",
"api_session" : "UI",
"symbol" : "btcusd",
"side" : "buy",
"order_type" : "exchange limit",
"timestamp" : "1478205545",
"timestampms" : 1478205545047,
"is_live" : false,
"original_amount" : "5",
"price" : "703.14444444",
"socket_sequence" : 310311
} ]
If your order cannot be accepted by the exchange, you will receive a single rejected
event.
Note that the is_live
field of a rejected
order event is always false
but so is the is_cancelled
field: is_cancelled
is reserved for orders that are actually cancelled, and rejected orders are not live but they are also not cancelled.
Field | Type | Required? | Description |
---|---|---|---|
type |
order event type | Y | rejected |
reason |
string | The reason your order was rejected. Contact trading@gemini.com for clarification. |
Booked
Order booked
[{
"type": "booked",
"order_id": "109535955",
"event_id": "109535957",
"account_name": "primary",
"api_session": "UI",
"symbol": "btcusd",
"side": "sell",
"order_type": "exchange limit",
"timestamp": "1547742952",
"timestampms": 1547742952725,
"is_live": true,
"is_cancelled": false,
"is_hidden": false,
"avg_execution_price": "0.00",
"executed_amount": "0",
"remaining_amount": "1",
"original_amount": "1",
"price": "3592.23",
"socket_sequence": 25
}]
When limit orders are booked
, they have a non-zero quantity visible on the exchange. These orders remain on the exchange until they are completed filled or cancelled.
Market orders are never booked: they are accepted, filled, and then closed.
Field | Type | Required? | Description |
---|---|---|---|
type |
order event type | Y | booked |
Filled
Complete fill on a limit order
[{
"type": "fill",
"order_id": "109535955",
"api_session": "UI",
"symbol": "btcusd",
"side": "sell",
"order_type": "exchange limit",
"timestamp": "1547743216",
"timestampms": 1547743216580,
"is_live": false,
"is_cancelled": false,
"is_hidden": false,
"avg_execution_price": "3592.23",
"executed_amount": "1",
"remaining_amount": "0",
"original_amount": "1",
"price": "3592.23",
"fill": {
"trade_id": "109535970",
"liquidity": "Maker",
"price": "3592.23",
"amount": "1",
"fee": "8.980575",
"fee_currency": "USD"
},
"socket_sequence": 81
}]
Partial fill on a limit order
[{
"type" : "fill",
"order_id" : 556309,
"account_name": "primary",
"api_session" : "UI",
"symbol" : "ethbtc",
"side" : "sell",
"order_type" : "exchange limit",
"timestamp" : "1478729284",
"timestampMs" : 1478729284169,
"is_live" : true,
"is_cancelled" : false,
"is_hidden" : false,
"avg_execution_price" : "0.01514",
"total_executed_amount" : "481.95988631",
"remaining_amount" : "303.06099969",
"original_amount" : "785.020886",
"original_price" : "0.01514",
"fill" : {
"trade_id" : "557315",
"liquidity" : "Maker",
"price" : "0.01514",
"amount" : "481.95988631",
"fee" : "0.0182421816968335",
"fee_currency" : "BTC"
},
"socket_sequence" : 471177
}]
A fill
event indicates a partial or a complete fill. A complete fill is distinguished by a remaining_amount
of 0
.
Note that here executed_amount
is the total amount of the order that has been filled. The quantity filled by this specific trade is fill.amount
.
Similarly, price
is an optional field that denotes the original price of a limit order (absent for market buys and sells), while fill.price
is the execution price of the trade (always present).
Field | Type | Required? | Description |
---|---|---|---|
type |
order event type | Y | fill |
fill.trade_id |
string | Y | the event id the order was filled at |
fill.liquidity |
string | Y | whether this side of the trade represents Maker or Taker liquidity |
fill.price |
decimal | Y | the price the trade filled at |
fill.amount |
decimal | Y | the amount of the trade fill |
fill.fee |
decimal | Y | the fee associated with this side of the trade |
fill.fee_currency |
string | Y | the three-letter code of the currency associated with the fee |
Cancelled
Continuous book order cancelled
[{
"type": "cancelled",
"order_id": "109944118",
"event_id": "109964524",
"cancel_command_id": "109964523",
"reason": "Requested",
"account_name": "primary",
"api_session": "myapikey",
"symbol": "bchusd",
"side": "buy",
"order_type": "exchange limit",
"timestamp": "1547756060",
"timestampms": 1547756060142,
"is_live": false,
"is_cancelled": true,
"is_hidden": false,
"avg_execution_price": "0.00",
"executed_amount": "0",
"remaining_amount": "1",
"original_amount": "1",
"price": "128.72",
"socket_sequence": 22
}]
When one of your active orders is cancelled, you will receive a notification via a cancelled
event.
Your order could be cancelled because:
- you requested it
- when you placed the order, you added a behavior such as
immediate-or-cancel
,fill-or-kill
, ormaker-or-cancel
which the exchange could not fulfil - in this case, your order is cancelled and closed instead of being booked - your FIX connection was disconnected, and all your orders have been cancelled
- your API session key has been provisioned to require a heartbeat and due to a heartbeat timeout, all your orders have been cancelled
Field | Type | Required? | Description |
---|---|---|---|
type |
order event type | Y | fill |
cancel_command_id |
string | N | The event id of the command to cancel your order. |
reason |
string | N | When possible, Gemini will supply the reason your order was cancelled. |
Cancel Rejected
Cancel Rejected
[ {
"type" : "cancel_rejected",
"order_id" : "6425",
"event_id" : "6434",
"cancel_command_id" : "6433",
"reason" : "OrderNotFound",
"account_name": "primary",
"api_session" : "UI",
"symbol" : "btcusd",
"side" : "buy",
"order_type" : "limit",
"timestamp" : "1478204773",
"timestampms" : 1478204773113,
"is_live" : true,
"is_cancelled" : false,
"is_hidden" : true,
"avg_execution_price" : "0.00",
"executed_amount" : "0",
"remaining_amount" : "5",
"original_amount" : "5",
"price" : "721.24",
"socket_sequence" : 312300
} ]
When Gemini cannot fulfil your request to cancel an order, you'll receive a cancel_rejected
notification.
Reasons this might happen include:
- the order you tried to cancel can't be found
Field | Type | Required? | Description |
---|---|---|---|
type |
order event type | Y | cancel_rejected |
cancel_command_id |
string | Y | The event id of the command to cancel your order. |
reason |
string | Y | The reason Gemini could not fulfil your request to cancel your order. |
Closed
Limit Sell closed
[{
"type": "closed",
"order_id": "109535955",
"event_id": "109535971",
"api_session": "UI",
"symbol": "btcusd",
"side": "sell",
"order_type": "exchange limit",
"timestamp": "1547743216",
"timestampms": 1547743216580,
"is_live": false,
"is_cancelled": false,
"is_hidden": false,
"avg_execution_price": "3592.23",
"executed_amount": "1",
"remaining_amount": "0",
"original_amount": "1",
"price": "3592.23",
"socket_sequence": 82
}]
The closed
event is the last event in the workflow of any order that has been accepted
on the exchange. When you receive this event, you will know that your order has been removed from the book.
Field | Type | Required? | Description |
---|---|---|---|
type |
order event type | Y | closed |
Market Data
Market data is a public API that streams all the market data on a given symbol.
BTC Market Data Feed
import ssl
import websocket
def on_message(ws, message):
print(message)
ws = websocket.WebSocketApp(
"wss://api.gemini.com/v1/marketdata/BTCUSD",
on_message=on_message)
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
npm install -g wscat
wscat --connect wss://api.gemini.com/v1/marketdata/btcusd
Example: Offers only, top of book
import ssl
import websocket
def on_message(ws, message):
print(message)
ws = websocket.WebSocketApp(
"wss://api.gemini.com/v1/marketdata/btcusd?top_of_book=true&bids=false",
on_message=on_message)
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
$ wscat --connect=wss://api.gemini.com/v1/marketdata/btcusd?top_of_book=true\&bids=false
connected (press CTRL+C to quit)
< {"type":"update","eventId":35015,"socket_sequence":0,"events":[{"type":"change","reason":"initial","price":"6629.89","delta":"13.928195144","remaining":"13.928195144","side":"ask"}]}
< {"type":"update","eventId":35051,"timestamp":1528247534,"timestampms":1528247534252,"socket_sequence":1,"events":[{"type":"top-of-book","side":"ask","price":"6631.44","remaining":"33.96746891"}]}
< {"type":"update","eventId":35053,"timestamp":1528247534,"timestampms":1528247534252,"socket_sequence":2,"events":[{"type":"top-of-book","side":"ask","price":"6631.44","remaining":"28.267969234"}]}
< {"type":"update","eventId":35117,"timestamp":1528247538,"timestampms":1528247538541,"socket_sequence":3,"events":[{"type":"top-of-book","side":"ask","price":"6633.04","remaining":"23.53504107"}]}
< {"type":"update","eventId":35122,"timestamp":1528247538,"timestampms":1528247538745,"socket_sequence":4,"events":[{"type":"top-of-book","side":"ask","price":"6633.87","remaining":"19.15815988"}]}
Example: Trades only
import ssl
import websocket
def on_message(ws, message):
print(message)
ws = websocket.WebSocketApp(
"wss://api.gemini.com/v1/marketdata/btcusd?trades=true",
on_message=on_message)
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
$ wscat --connect=wss://api.gemini.com/v1/marketdata/btcusd?trades=true
connected (press CTRL+C to quit)
< {"type":"update","eventId":62653,"socket_sequence":0,"events":[]}
< {"type":"update","eventId":62711,"timestamp":1528249346,"timestampms":1528249346783,"socket_sequence":1,"events":[{"type":"trade","tid":62711,"price":"6619.37","amount":"7.8662471812","makerSide":"ask"}]}
< {"type":"update","eventId":62713,"timestamp":1528249346,"timestampms":1528249346783,"socket_sequence":2,"events":[{"type":"trade","tid":62713,"price":"6619.46","amount":"13.9673234988","makerSide":"ask"}]}
< {"type":"update","eventId":62795,"timestamp":1528249351,"timestampms":1528249351276,"socket_sequence":3,"events":[{"type":"trade","tid":62795,"price":"6619.46","amount":"16.7321435012","makerSide":"ask"}]}
< {"type":"update","eventId":62797,"timestamp":1528249351,"timestampms":1528249351276,"socket_sequence":4,"events":[{"type":"trade","tid":62797,"price":"6619.70","amount":"2.3054248088","makerSide":"ask"}]}
< {"type":"update","eventId":62823,"timestamp":1528249352,"timestampms":1528249352909,"socket_sequence":5,"events":[{"type":"trade","tid":62823,"price":"6619.70","amount":"0.0002606894","makerSide":"ask"}]}
< {"type":"update","eventId":62830,"timestamp":1528249353,"timestampms":1528249353316,"socket_sequence":6,"events":[{"type":"trade","tid":62830,"price":"6610.15","amount":"0.00273253","makerSide":"bid"}]}
Example: Full depth, bids and offers only
import ssl
import websocket
def on_message(ws, message):
print(message)
ws = websocket.WebSocketApp(
"wss://api.gemini.com/v1/marketdata/btcusd?bids=true&offers=true",
on_message=on_message)
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
$ wscat --connect=wss://api.gemini.com/v1/marketdata/btcusd?bids=true&offers=true
connected (press CTRL+C to quit)
< {"type":"update","eventId":64575,"socket_sequence":0,"events":[{"type":"change","reason":"initial","price":"6511.13","delta":"26.93362206","remaining":"26.93362206","side":"bid"},{"type":"change","reason":"initial","price":"6823.47","delta":"34.526471","remaining":"34.526471","side":"ask"}]}
< {"type":"update","eventId":64609,"timestamp":1528249465,"timestampms":1528249465320,"socket_sequence":1,"events":[{"type":"change","side":"ask","price":"6622.84","remaining":"16.49742094","delta":"16.49742094","reason":"place"}]}
< {"type":"update","eventId":64634,"timestamp":1528249466,"timestampms":1528249466750,"socket_sequence":2,"events":[{"type":"change","side":"bid","price":"6592.30","remaining":"18.97068216","delta":"18.97068216","reason":"place"}]}
< {"type":"update","eventId":64651,"timestamp":1528249467,"timestampms":1528249467565,"socket_sequence":3,"events":[{"type":"change","side":"ask","price":"6636.75","remaining":"16.10859393","delta":"16.10859393","reason":"place"}]}
< {"type":"update","eventId":64656,"timestamp":1528249467,"timestampms":1528249467975,"socket_sequence":4,"events":[{"type":"change","side":"ask","price":"6642.91","remaining":"23.553287","delta":"23.553287","reason":"place"}]}
< {"type":"update","eventId":64663,"timestamp":1528249468,"timestampms":1528249468587,"socket_sequence":5,"events":[{"type":"change","side":"ask","price":"6635.61","remaining":"17.97336167","delta":"17.97336167","reason":"place"}]}
< {"type":"update","eventId":64678,"timestamp":1528249469,"timestampms":1528249469405,"socket_sequence":6,"events":[{"type":"change","side":"bid","price":"6596.96","remaining":"21.93141551","delta":"21.93141551","reason":"place"}]}
< {"type":"update","eventId":64703,"timestamp":1528249471,"timestampms":1528249471450,"socket_sequence":7,"events":[{"type":"change","side":"bid","price":"6588.67","remaining":"17.66913232","delta":"17.66913232","reason":"place"}]}
< {"type":"update","eventId":64708,"timestamp":1528249471,"timestampms":1528249471858,"socket_sequence":8,"events":[{"type":"change","side":"ask","price":"6623.78","remaining":"16.44716907","delta":"16.44716907","reason":"place"}]}
< {"type":"update","eventId":64736,"timestamp":1528249474,"timestampms":1528249474104,"socket_sequence":9,"events":[{"type":"change","side":"ask","price":"6623.89","remaining":"36.91752526","delta":"36.91752526","reason":"place"}]}
< {"type":"update","eventId":64748,"timestamp":1528249475,"timestampms":1528249475127,"socket_sequence":10,"events":[{"type":"change","side":"ask","price":"6630.94","remaining":"17.8888451","delta":"17.8888451","reason":"place"}]}
The initial response message will show the existing state of the order book for both bids and offers, regardless of the additional parameters applied in the URL. Subsequent messages will show all executed trades, as well as all other changes to the order book from orders placed or canceled.
WebSocket Request
wss://api.gemini.com/v1/marketdata/:symbol
URL Parameters
Parameter | Required | Default | Description |
---|---|---|---|
heartbeat |
No | false | Optionally add this parameter and set to true to receive a heartbeat every 5 seconds |
top_of_book |
No | false | If absent or false , receive full order book depth; if present and true , receive top of book only. Only applies to bids and offers. |
bids |
No | true | Include bids in change events |
offers |
No | true | Include asks in change events |
trades |
No | true | Include trade events |
The semantics of entry type filtering is:
- To be excluded from
change
events in the response, an entry type must be explicitly flaggedfalse
- If no entry types filtering parameters are included in the url, then all entry types will appear in the response
NOTE: top_of_book
has no meaning and initial book events are empty when only trades
is specified
Response
Initial JSON response message for BTC top of book
{
"type": "update",
"eventId": 5375461993,
"socket_sequence": 0,
"events": [
{
"type": "change",
"reason": "initial",
"price": "3641.61",
"delta": "0.83372051",
"remaining": "0.83372051",
"side": "bid"
},
{
"type": "change",
"reason": "initial",
"price": "3641.62",
"delta": "4.072",
"remaining": "4.072",
"side": "ask"
}
]
}
If heartbeat is enabled:
{
"type":"heartbeat",
"socket_sequence":30
}
A WebSocket stream with each frame containing a JSON message of the following format:
Field | Type | Description |
---|---|---|
type |
string | heartbeat or update |
socket_sequence |
integer | zero-indexed monotonic increasing sequence number attached to each message sent - if there is a gap in this sequence, you have missed a message. If you choose to enable heartbeats, then heartbeat and update messages will share a single increasing sequence. See Sequence Numbers for more information. |
Messages with type heartbeat
have no additional fields.
Messages of type update
have the following additional fields:
Field | Type | Description |
---|---|---|
eventId |
integer | A monotonically increasing sequence number indicating when this change occurred. These numbers are persistent and consistent between market data connections. |
events |
array | Either a change to the order book, or the indication that a trade has occurred. |
timestamp |
timestamp | The timestamp in seconds for this group of events (included for compatibility reasons). We recommend using the timestampms field instead. |
timestampms |
timestampms | The timestamp in milliseconds for this group of events. |
Common fields
All elements of the events
share the following fields:
Field | Type | Description |
---|---|---|
type |
string | Either trade or change . |
Change event
When an order is placed:
{
"type": "update",
"eventId": 5375504382,
"timestamp": 1547759967,
"timestampms": 1547759967559,
"socket_sequence": 66,
"events": [
{
"type": "change",
"side": "bid",
"price": "3626.73",
"remaining": "1.6",
"delta": "0.8",
"reason": "place"
}
]
}
When an order is canceled:
{
"type": "update",
"eventId": 5375503736,
"timestamp": 1547759964,
"timestampms": 1547759964051,
"socket_sequence": 2,
"events": [
{
"type": "change",
"side": "bid",
"price": "3628.01",
"remaining": "0",
"delta": "-2",
"reason": "cancel"
}
]
}
Elements of type change
have the following fields:
Field | Type | Description |
---|---|---|
price |
decimal | The price of this order book entry. |
side |
string | Either bid or ask . |
reason |
string | Either place , trade , cancel , or initial , to indicate why the change has occurred. initial is for the initial response message, which will show the entire existing state of the order book. |
remaining |
decimal | The quantity remaining at that price level after this change occurred. May be zero if all orders at this price level have been filled or canceled. |
delta |
decimal | The quantity changed. May be negative, if an order is filled or canceled. For initial messages, delta will equal remaining . |
Note that every trade will trigger a message with entries of both types trade
and change
.
To keep an up-to-date order book, just watch for any events
with {"type": "change"}
, and update the price
level at price
with the amount at remaining
. The initial response message will contain all the change
events necessary
to populate your order book from scratch.
Trade event
When a trade occurs:
{
"type": "update",
"eventId": 5375547515,
"timestamp": 1547760288,
"timestampms": 1547760288001,
"socket_sequence": 15,
"events": [
{
"type": "trade",
"tid": 5375547515,
"price": "3632.54",
"amount": "0.1362819142",
"makerSide": "ask"
}
]
}
Elements of the type trade
have the following fields:
Field | Type | Description |
---|---|---|
price |
decimal | The price this trade executed at. |
amount |
decimal | The amount traded. |
makerSide |
string | The side of the book the maker of the trade placed their order on. Either bid , ask . |
Market Data Version 2
The initial response message will show the existing state of the order books and last 50 trades. Subsequent messages will show all executed trades, as well as all other changes to the order book from order placed or cancelled.
Level 2 Subscribe Message Fields
Send a JSON formatted message with the following fields upon connecting to v2/marketdata
BTC Market Data Feed
import ssl
import websocket
import _thread as thread
def on_message(ws, message):
print(message)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
def run(*args):
ws.send(logon_msg)
thread.start_new_thread(run, ())
if __name__ == "__main__":
logon_msg = '{"type": "subscribe","subscriptions":[{"name":"l2","symbols":["BTCUSD","ETHUSD","ETHBTC"]}]}'
websocket.enableTrace(True)
ws = websocket.WebSocketApp("wss://api.gemini.com/v2/marketdata",
on_message = on_message,
on_error = on_error,
on_close = on_close,
on_open = on_open)
ws.on_open = on_open
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
$ wscat --connect wss://api.gemini.com/v2/marketdata
> connected (press CTRL+C to quit)
> {"type": "subscribe","subscriptions":[{"name":"l2","symbols":["BTCUSD","ETHUSD","ETHBTC"]}]}
Market data v2 is a public API that can stream all market and candle data across books. Market data v2 also supports multiple subscriptions in the same data feed.
WebSocket Request
wss://api.gemini.com/v2/marketdata
Subscription Message
After connecting to v2/marketdata
you can subscribe to any of the following data feeds.
Name | Data Description |
---|---|
l2 |
Level 2 book data |
candles_1m |
1 minute candle data |
candles_5m |
5 minute candle data |
candles_15m |
15 minute candle data |
candles_30m |
30 minute candle data |
candles_1h |
1 hour candle data |
candles_6h |
6 hour candle data |
candles_1d |
1 day candle data |
To subcribe to a data feed, send a subscription message in the following format.
Field Name | Type | Values |
---|---|---|
type |
string | subscribe |
subscriptions |
array | |
-- name | string | l2 , candle_1m , etc. |
-- symbols | array | ["BTCUSD, "ETHBTC", ... ] |
Level 2 Data
Initial JSON response for l2 subscription
{
"type": "l2_updates",
"symbol": "BTCUSD",
"changes": [
[
"buy",
"9122.04",
"0.00121425"
],
...,
[
"sell",
"9122.07",
"0.98942292"
]
...
],
"trades": [
{
"type": "trade",
"symbol": "BTCUSD",
"eventid": 169841458,
"timestamp": 1560976400428,
"price": "9122.04",
"quantity": "0.0073173",
"side": "sell",
"tid": 2840140800042677
},
...
],
}
Field Name | Type | Values |
---|---|---|
type |
string | subscribe |
subscriptions |
array | |
-- name | string | l2 |
-- symbols | array | ["BTCUSD", "ETHBTC", ...] |
Level 2 Update Response
Field Name | Type | Values |
---|---|---|
type |
string | l2_updates |
symbol |
string | BTCUSD , etc |
changes |
array of arrays | Changes to order book |
-- | string | Buy or Sell |
-- | string | Price Level |
-- | string | Quantity |
JSON reponse for l2 update
{
"type": "l2_updates",
"symbol": "BTCUSD",
"changes": [
[
"sell",
"9160.20",
"0.1921229751"
]
]
}
Trade Response
Field Name | Type | Values |
---|---|---|
type |
string | trade |
symbol |
string | BTCUSD , etc |
event_id |
long | Event ID of the trade |
timestamp |
long | Time of the trade in milliseconds |
price |
string | Price of the trade |
quantity |
string | Quantity traded |
side |
string | Side of the taker in the trade. buy or sell |
tid |
long | Trade ID of the trade |
JSON response trade execution
{
"type": "trade",
"symbol": "BTCUSD",
"event_id": 3575573053,
“timestamp”: 151231241,
"price": "9004.21000000",
"quantity": "0.09110000",
"side": "buy",
"tid": 2840140800042677
}
Candles Data Feed
The Candle Data feed provides periodic updates with OHLCV data for the given timeframe.
Candle Data Feed Subcribe Message
Send a JSON formatted message with the following fields upon connecting to v2/marketdata
Example JSON Candle Subscription Message
{
"type": "subscribe",
"subscriptions": [
{
"name": "candles_15m",
"symbols": [
"BTCUSD"
]
}
]
}
Field Name | Type | Values |
---|---|---|
type | string | subscribe |
subscriptions | array | |
-- name | string | candles_1m , candles_5m , etc. |
-- symbols | array | ["BTCUSD", "ETHBTC", ...] |
Example JSON Candle Response
{
"type": "candles_15m_updates",
"symbol": "BTCUSD",
"changes": [
[
1561054500000,
9350.18,
9358.35,
9350.18,
9355.51,
2.07
],
[
1561053600000,
9357.33,
9357.33,
9350.18,
9350.18,
1.5900161
]
...
]
}
Candle Data Response
Field Name | Type | Values |
---|---|---|
type | string | candles_1m_updates , candles_5m_updates , etc. |
symbol | string | BTCUSD , etc. |
candles | Array of Arrays (TOHLCV) | Changes to order book |
-- -- time | long | milliseconds |
-- -- open | decimal | Open price |
-- -- high | decimal | High price |
-- -- low | decimal | Low price |
-- -- close | decimal | Close price |
-- -- volume | decimal | Volume |
Mark Price Feed
The Mark Price feed provides mark price updates for a subscribed perpetual instrument
Mark Price Feed Subscribe Message
Send a JSON formatted message with following fields upon connecting to v2/marketdata
Example JSON Mark Price Subscription Message
{
"type": "subscribe",
"subscriptions": [
{
"name": "mark_price",
"symbols": [
"BTC-GUSD-PERP"
]
}
]
}
Field Name | Type | Values |
---|---|---|
type | string | subscribe |
subscriptions | array | |
-- name | string | mark_price |
-- symbols | array | ["BTC-GUSD-PERP", ...] |
Mark Price Response
Example JSON Mark Price Response
{
"type": "mark_price_updates",
"symbol": "BTCGUSDPERP",
"changes": [
{
"timestamp": 1673932381478308169,
"mark_price": "21154.098",
"spot_index": "21175.27333"
}
]
}
Field Name | Type | Values |
---|---|---|
type | string | mark_price_updates |
symbol | string | BTC-GUSD-PERP |
changes | array | change to mark price |
All elements of changes
share the following fields:
Field Name | Type | Values |
---|---|---|
timestamp | integer | nanoseconds |
mark_price | string | mark price |
spot_index | string | spot index |
Funding Amount Feed
The Funding Amount feed provides funding amount updates for a subscribed perpetual instrument
Funding Amount Feed Subscribe Message
Send a JSON formatted message with following fields upon connecting to v2/marketdata
Example JSON Funding Amount Subscription Message
{
"type": "subscribe",
"subscriptions": [
{
"name": "funding_amount",
"symbols": [
"BTC-GUSD-PERP"
]
}
]
}
Field Name | Type | Values |
---|---|---|
type | string | subscribe |
subscriptions | array | |
-- name | string | funding_amount |
-- symbols | array | ["BTC-GUSD-PERP", ...] |
Funding Amount Response
Example JSON Funding Amount Response
{
"type": "funding_amount_updates",
"symbol": "BTCGUSDPERP",
"changes": [
{
"timestamp": 1673932380007696874,
"funding_amount": "0",
"funding_date_time": 1673932380007696874,
"funding_interval_in_minutes": 60,
"is_realized": false
}
]
}
Field Name | Type | Values |
---|---|---|
type | string | funding_amount_updates |
symbol | string | BTC-GUSD-PERP |
changes | array | change to funding amount |
All elements of changes
share the following fields:
Field Name | Type | Values |
---|---|---|
timestamp | integer | nanoseconds |
funding_amount | string | funding amount |
funding_date_time | integer | funding date time |
funding_interval_in_minutes | integer | funding interval in minutes |
is_realized | boolean | is realized funding |
Unsubscribe
Unsubscribe from data feeds by sending a message in the following format.
Sample unsubscribe message
{
"type": "unsubscribe",
"subscriptions": [{
"name": "l2",
"symbols": [
"BTCUSD",
"ETHBTC"
]
},
{
"name": "candles_1m",
"symbols": [
"BTCUSD",
"ETHBTC"
]
}
]
}
Field Name | Type | Values |
---|---|---|
type | string | unsubscribe |
subscriptions | array | |
-- name | string | l2 , candles_1m , etc. |
-- symbols | array | ["BTCUSD", "ETHBTC", ...] |
Multi Market Data
Multi market data is a public API which allows for multiple symbols to be streamed via a single endpoint.
BTCUSD and ETHUSD Multi Market Data Feed
import ssl
import websocket
def on_message(ws, message):
print(message)
ws = websocket.WebSocketApp(
"wss://api.gemini.com/v1/multimarketdata?symbols=BTCUSD,ETHUSD",
on_message=on_message)
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
npm install -g wscat
wscat --connect wss://api.gemini.com/v1/v1/multimarketdata?symbols=BTCUSD,ETHUSD
< {"type":"update","eventId":773797793,"socket_sequence":0,"events":[{"type":"change","reason":"initial","price":"1.00","delta":"236.5","remaining":"236.5","side":"bid","symbol":"BTCUSD"},{"type":"change","reason":"initial","price":"2.00","delta":"176"},
...
{"type":"change","reason":"initial","price":"59137.50","delta":"0.00031","remaining":"0.00031","side":"ask","symbol":"BTCUSD"}]}
< {"type":"update","eventId":773797763,"socket_sequence":1,"events":[{"type":"change","reason":"initial","price":"6.95","delta":"9","remaining":"9","side":"bid","symbol":"ETHUSD"},
...
{"type":"change","reason":"initial","price":"1827.84","delta":"14.8","remaining":"14.8","side":"ask","symbol":"ETHUSD"}]}
< {"type":"update","eventId":773797906,"timestamp":1617045817,"timestampms":1617045817599,"socket_sequence":2,"events":[{"type":"trade","tid":773797906,"price":"57734.10","amount":"0.002","makerSide":"bid","symbol":"BTCUSD"},{"type":"change","side":"bid","price":"57734.10","remaining":"0.0107803","delta":"-0.002","reason":"trade","symbol":"BTCUSD"}]}
The initial response message will show the existing state of the order books requested. Subsequent messages will show all executed trades, as well as all other changes to the order books from orders placed or canceled.
WebSocket Request
wss://api.gemini.com/v1/multimarketdata?symbols=BTCUSD,ETHUSD
URL Parameters
Parameter | Required | Default | Description |
---|---|---|---|
symbols |
Yes | false | required. Will stream market data for symbols provided. Can provide data for multiple symbols on the same stream. |
heartbeat |
No | false | Optionally add this parameter and set to true to receive a heartbeat every 5 seconds |
top_of_book |
No | false | If absent or false , receive full order book depth; if present and true , receive top of book only. Only applies to bids and offers. |
bids |
No | true | Include bids in change events |
offers |
No | true | Include asks in change events |
trades |
No | true | Include trade events |
The semantics of entry type filtering is:
- If any entry type is specified as
true
orfalse
, all of them must be explicitly flaggedtrue
to show up in the response - If no entry types filtering parameters are included in the url, then all entry types will appear in the response
NOTE: top_of_book
has no meaning and initial book events are empty when only trades
is specified
Response
Initial JSON response messages for BTCUSD and ETHUSD
{
"type":"update",
"eventId":773797793,"socket_sequence":0,
"events":[
{
"type":"change",
"reason":"initial",
"price":"1.00",
"delta":"236.5",
"remaining":"236.5",
"side":"bid",
"symbol":"BTCUSD"
},
...
{
"type":"change",
"reason":"initial",
"price":"59137.50",
"delta":"0.00031",
"remaining":"0.00031",
"side":"ask",
"symbol":"BTCUSD"
}
]
}
{
"type":"update",
"eventId":773797763,"socket_sequence":1,
"events":[
{
"type":"change",
"reason":"initial",
"price":"6.95",
"delta":"9",
"remaining":"9",
"side":"bid",
"symbol":"ETHUSD"
},
...
{
"type":"change",
"reason":"initial",
"price":"1827.84",
"delta":"14.8",
"remaining":"14.8",
"side":"ask",
"symbol":"ETHUSD"
}
]
}
A WebSocket stream that provides multiple symbols with each frame containing a JSON message of the following format:
Field | Type | Description |
---|---|---|
type |
string | heartbeat or update |
socket_sequence |
integer | zero-indexed monotonic increasing sequence number attached to each message sent - if there is a gap in this sequence, you have missed a message. If you choose to enable heartbeats, then heartbeat and update messages will share a single increasing sequence. See Sequence Numbers for more information. |
Messages with type heartbeat
have no additional fields.
Messages of type update
have the following additional fields:
Field | Type | Description |
---|---|---|
eventId |
integer | A monotonically increasing sequence number indicating when this change occurred. These numbers are persistent and consistent between market data connections. |
events |
array | Either a change to the order book, or the indication that a trade has occurred. |
timestamp |
timestamp | The timestamp in seconds for this group of events (included for compatibility reasons). We recommend using the timestampms field instead. |
timestampms |
timestampms | The timestamp in milliseconds for this group of events. |
Common fields
All elements of the events
share the following fields:
Field | Type | Description |
---|---|---|
type |
string | Either trade or change . |
symbol |
string | Will return the order book for which the message type is for. Only symbols provided in the URL parameter will be present in the stream. BTCUSD , ETHUSD or any supported symbol |
Change event
Elements of type change
have the following fields:
Field | Type | Description |
---|---|---|
price |
decimal | The price of this order book entry. |
side |
string | Either bid or ask . |
reason |
string | Either place , trade , cancel , or initial , to indicate why the change has occurred. initial is for the initial response message, which will show the entire existing state of the order book. |
remaining |
decimal | The quantity remaining at that price level after this change occurred. May be zero if all orders at this price level have been filled or canceled. |
delta |
decimal | The quantity changed. May be negative, if an order is filled or canceled. For initial messages, delta will equal remaining . |
Note that every trade will trigger a message with entries of both types trade
and change
.
To keep an up-to-date order book, just watch for any events
with {"type": "change"}
, and update the price
level at price
with the amount at remaining
. The initial response message will contain all the change
events necessary
to populate your order book from scratch.
Trade event
When a trade occurs:
{
"type": "update",
"eventId": 5375547515,
"timestamp": 1547760288,
"timestampms": 1547760288001,
"socket_sequence": 15,
"events": [
{
"type": "trade",
"tid": 5375547515,
"price": "3632.54",
"amount": "0.1362819142",
"makerSide": "ask",
"symbol": "BTCUSD"
}
]
}
Elements of the type trade
have the following fields:
Field | Type | Description |
---|---|---|
price |
decimal | The price this trade executed at. |
amount |
decimal | The amount traded. |
makerSide |
string | The side of the book the maker of the trade placed their order on. Either bid or ask . |
Error Codes
If a response is in error, then the HTTP response code will be set to reflect this, and a JSON body will be returned that will contain information about the failure.
HTTP Error Codes
HTTP Status | Meaning |
---|---|
200 | Request was successful |
30x | API entry point has moved, see Location: header. Most likely an http: to https: redirect. |
400 | Market not open, or the request was malformed; in the case of a private API request, missing or malformed Gemini private API authentication headers |
403 | The API key is missing the role necessary to access this private API endpoint |
404 | Unknown API entry point or Order not found |
406 | Insufficient Funds |
429 | Rate Limiting was applied |
500 | The server encountered an error |
502 | Technical issues are preventing the request from being satisfied |
503 | The exchange is down for maintenance |
Error payload
{
"result": "error",
"reason": "BadNonce",
"message": "Out-of-sequence nonce <1234> precedes previously used nonce <2345>"
}
In the event of an error, a non-200 error code will be returned, and the response body will be a json object with three fields:
result
, which will always be "error"reason
, which will be one of the strings listed in the table belowmessage
, a human-readable English string indicating additional error information.
Reason | Meaning |
---|---|
ClientOrderIdTooLong | The Client Order ID must be under 100 characters |
ClientOrderIdMustBeString | The Client Order ID must be a string |
ConflictingOptions | New orders using a combination of order execution options are not supported |
ConflictingAccountName | The specified name is already in use within the master group |
EndpointMismatch | The request was submitted to an endpoint different than the one in the payload |
EndpointNotFound | No endpoint was specified |
GTSTradeIDMustBeString | The Clearing ID must be a string |
InsufficientFunds | The order was rejected because of insufficient funds |
InvalidJson | The JSON provided is invalid |
InvalidNonce | The nonce was not greater than the previously used nonce or was not within +/- 30 seconds of Unix Epoch timestamp |
InvalidOrderType | An unknown order type was provided |
InvalidPrice | For new orders, the price was invalid |
InvalidStopPrice | For new stop limit orders, the price was invalid |
InvalidStopPriceSell | For new stop limit sell orders, the "stop_price" price was lower than the "sell" price |
InvalidStopPriceBuy | For new stop limit buy orders, the "stop_price" price was greater than the "buy" price |
InvalidStopPriceRatio | For new stop limit orders, the "buy" or "sell" price was not within 50% of the "stop_price" |
InvalidQuantity | A negative or otherwise invalid quantity was specified |
InvalidSide | For new orders, and invalid side was specified |
InvalidSignature | The signature did not match the expected signature |
InvalidSymbol | An invalid symbol was specified |
InvalidTimestampInPayload | The JSON payload contained a timestamp parameter with an unsupported value. |
InvalidAccountName | The specified name did not match any accounts within the master group |
InvalidAccountType | The specified type did not match exchange or custody |
InvalidFundTransfer | The fund transfer was not successful |
Maintenance | The system is down for maintenance |
MarketNotOpen | The order was rejected because the market is not accepting new orders |
MissingAccountName | A required account name was not specified in a field requiring one |
MissingAccounts | A required account field was not specified |
MissingApikeyHeader | The X-GEMINI-APIKEY header was missing |
MissingOrderField | A required order_id field was not specified |
MissingRole | The API key used to access this endpoint does not have the required role assigned to it |
MissingPayloadHeader | The X-GEMINI-PAYLOAD header was missing |
MissingPayloadKey | The payload is missing a required key |
MissingSignatureHeader | The X-GEMINI-SIGNATURE header was missing |
MissingName | A required name field was not specified |
MissingNonce | A nonce was not provided in the payload. See Private API Invocation for more detail. |
MoreThanOneAccount | More than one account was specified on an API that only accepts a single account |
AccountClosed | Account account is closed and cannot be used for this operation. |
AccountsOnGroupOnlyApi | The account field was specified on a non-master API key |
AccountLimitExceeded | The account field specified more than the maximum supported accounts for that API |
NoAccountOfTypeRequired | The account field specified multiple accounts and some were not of the required account type |
AccountNotOfTypeRequired | The account specified in the account field was not of the required account type |
NotGroupApiCompatible | A master API key was used to invoke an account only API |
ExceededMaxAccountsInGroup | An account could not be created as the master group already has the maximum number of allowed accounts in it |
NoSSL | You must use HTTPS to access the API |
OptionsMustBeArray | The options parameter must be an array. |
OrderNotFound | The order specified was not found |
RateLimit | Requests were made too frequently. See Rate Limits below. |
System | We are experiencing technical issues |
UnsupportedOption | This order execution option is not supported. |
HasNotAgreedToCustodyTerms | The Group has not yet agreed to the Custody terms and conditions. Please visit https://exchange.gemini.com/custody to read the terms and conditions of custody accounts. |
BadAccountType | The type parameter must contain a string of either exchange or custody . |
RemoteAddressForbidden | Request received from an IP address that is not whitelisted under the group. |
Revision History
Date | Notes |
---|---|
2016/11/10 | Initial WebSocket API documentation |
2016/12/14 | New feature: API key roles |
2017/02/08 | Better market data examples |
2017/03/06 | Documentation bugfix: correct location of market data JSON example for trade events |
2017/05/15 | Document Order Events Subscription Acknowledgement subscriptionId field |
2017/05/15 | Clarified how Gemini rate limits incoming requests to public WebSocket APIs |
2017/07/13 | API Change timestamp and timestampms added to Market Data API |
2017/07/27 | Documentation bugfix: clarify the purpose of the trace_id in Order Events: Heartbeats |
2017/08/10 | New Feature to make it easy to detect WebSocket messages that were missed or received out-of-order, Gemini has added a socket_sequence field to both the Market Data and Order Events APIs. Further details available in Sequence Numbers. |
2017/12/01 | API Change collar_price added to Market Data API |
2018/04/06 | API Change Document block trades in Market Data and Order Events APIs. |
2018/06/06 | API Change Market depth and entry filtering added to Market Data API |
2019/06/20 | Adding documentation for Market Data v2 and Candles Data Feed |
2019/11/22 | Updated response messages for Candles Data Feed on Market Data v2 |
2020/04/09 | Documentation for new token support: BAT , DAI , LINK , OXT |
2020/08/28 | Removing DAIBTC and DAIETH trading pairs |
2020/10/05 | Documentation for new order book support: BTCDAI and ETHDAI |
2020/10/07 | Documentation for new token support: AAVE |
2020/10/14 | Documentation for new token support: FIL |
2021/01/28 | Documentation for new symbol support: BTCSGD and ETHSGD |
2021/03/11 | Added new optional heartbeat filter |
2021/03/22 | Documentation for new token support: SKL , GRT , BNT , 1INCH , ENJ , LRC , SAND |
2021/03/29 | Documentation for new Multi Market Data Feed |
2021/04/27 | Documentation for new token support: CUBE , LPT , BOND , MATIC , INJ , SUSHI |
2021/05/05 | Documentation for new token support: DOGE |
2021/06/16 | Documentation for new token support: ALCX , MIR , FTM , ANKR |
2021/07/14 | Documentation for new token support: CTX |
2021/07/21 | Documentation for new token support: XTZ |
2021/09/15 | Documentation for new token support: AXS , SLP , LUNA , UST , MCO2 |
2021/11/13 | Documentation for new token support: WCFG , RARE , RAD , QNT , NMR , MASK , FET , ASH , AUDIO , API3 , USDC , SHIB |
2021/12/20 | Documentation for new token support: RNDR , MC , GALA , ENS , KP3R , CVC , ELON , MIM , SPELL |
2022/02/01 | Documentation for new token support: TOKE , LDO , RLY |
2022/02/28 | Documentation for new token support: SOL |
2022/03/01 | Documentation for new token support: RAY , SBR |
2022/03/16 | Documentation for new token support: APE |
2022/03/29 | Documentation for new token support: RBN , FXS , DPI , LQTY , LUSD , FRAX , INDEX , MPL |
2022/04/26 | Documentation for new token support: GUSDSGD |
2022/04/27 | Documentation for new token support: METIS , QRDO , ZBC , CHZ , REVV , JAM , FIDA , GMT |
2022/05/17 | Documentation for new token support: GFI , ORCA |
2022/06/14 | Documentation for new token support: ALI , TRU |
2022/06/22 | Documentation for new token support: GUSDGBP |
2022/06/23 | Deprecating documentation for Auction and Block trading support |
2022/07/06 | Documentation for new token support: DOT , ERN |
2022/08/01 | Documentation for new token support: GAL , EUL , SAMO |
2022/08/23 | Documentation for new token support: BICO , IMX , PLA , IOTX |
2022/09/07 | Documentation for new token support: BUSD |
2022/10/11 | Documentation for new token support: AVAX |
2023/01/10 | Documentation for new token support: ATOM , USDT |
2023/05/09 | Documentation for new token support: PEPE |
2023/08/04 | Documentation for token delist: ENJ |
2023/08/10 | Documentation for new token support: XRP |
2023/09/11 | Documentation for new token support: HNT |
2023/09/18 | Removed Documentation for new token support: MPL , MC , METIS , RBN , GFI , LQTY , and LUSD |
2023/11/15 | Remove Documentation for delisted token: MIR , UST , FXS , FRAX , BUSD |
2024/04/23 | Remove Documentation for delisted token: ZEC |
2024/05/21 | Adding 'tid' to the TRADE RESPONSE events under Market Data v2 |
2024/05/05 | Remove Documentation for delisted token: OXT-BTC ,OXT-ETH ,BAT-BTC ,BAT-ETH , BTC-DAI , ETH-DAI |
2024/09/18 | Documentation for new token support WIF |
2024/09/18 | API Change Remove Documentation for support /v1/balances/earn , /v1/earn/rates , /v1/earn/interest |
2024/09/20 | Remove Documentation for delisted token: LUNA ,SNX ,QRDO ,ZBC |
2024/11/04 | Documentation for new token support BONK , POPCAT , OP |
2024/11/15 | Documentation for new token support MOODENG |
2024/11/19 | Documentation for new token support PNUT , GOAT , MEW , BOME |
- Trust is Our Product™
- For trademarks and patents, please see the Legal Notice.
- NMLS #1518126
- © Copyright 2022 Gemini Trust Company, LLC.