NAV
python shell

Introduction

Gemini offers two WebSocket APIs for streaming data:

  1. a private Order Events API
  2. a public Market Data API

Advantages include:

For example, to keep track of your orders, you might be requesting the Get Active Orders endpoint every five seconds:

  1. request /v1/orders
  2. bring back HTTP header data and a response body
  3. parse the JSON in the response body
  4. 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.

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.

Your account will automatically be credited with BTC, USD, ETH and ZEC. 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 institutional@gemini.com.

If you have any issues, or if you need to adjust your balances (to test insufficient funds handling, for example), contact institutional@gemini.com.

Rate Limits

To prevent abuse, Gemini imposes rate limits on incoming requests as described in the Gemini API Agreement.

For public WebSocket APIs, we limit requests to 12 requests per minute, and recommend that you do not exceed 3 requests per minute (one request per symbol).

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

Payload creation

{
    "request": "/v1/order/status",
    "nonce": 123456,
    "order_id": 18834
}

Whitespace is valid JSON, so it 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/status",
>     "nonce": 123456,
>
>     "order_id": 18834
> }
> EOF
ewogICAgInJlcXVlc3QiOiAiL3YxL29yZGVyL3N0YXR1cyIsCiAgICAibm9uY2UiOiAxMjM0NTYs
CgogICAgIm9yZGVyX2lkIjogMTg4MzQKfQo=
encoded_payload = json.dumps(payload)
b64 = base64.b64encode(encoded_payload)

In this example, the api_secret is 1234abcd

echo -n 'ewogICAgInJlcXVlc3QiOiAiL3YxL29yZGVyL3N0YXR1cyIsCiAgICAibm9uY2UiOiAxMjM0NTYsCgogICAgIm9yZGVyX2lkIjogMTg4MzQKfQo=' | openssl sha384 -hmac "1234abcd"
(stdin)= 337cc8b4ea692cfe65b4a85fcc9f042b2e3f702ac956fd098d600ab15705775017beae402be773ceee10719ff70d710f
gemini_api_secret = "1234abcd"
signature = hmac.new(gemini_api_secret, b64, hashlib.sha384).hexdigest()

The final request will look like this:

curl --request POST \
  --url https://api.gemini.com/v1/order/status \
  --header 'Cache-Control: no-cache' \
  --header 'Content-Length: 0' \
  --header 'Content-Type: text/plain' \
  --header 'X-GEMINI-APIKEY: mykey' \
  --header 'X-GEMINI-PAYLOAD: ewogICAgInJlcXVlc3QiOiAiL3YxL29yZGVyL3N0YXR1cyIsCiAgICAibm9uY2UiOiAxMjM0NTYsCgogICAgIm9yZGVyX2lkIjogMTg4MzQKfQo=' \
  --header 'X-GEMINI-SIGNATURE: 337cc8b4ea692cfe65b4a85fcc9f042b2e3f702ac956fd098d600ab15705775017beae402be773ceee10719ff70d710f'
import requests
import base64
import hmac
from hashlib import sha384

url = "https://api.gemini.com/v1/order/status"
gemini_api_key = "mykey"
gemini_api_secret = "1234abcd"
payload =  {"request": "/v1/mytrades", "nonce": payload_nonce}
encoded_payload = json.dumps(payload)
b64 = base64.b64encode(encoded_payload)
signature = hmac.new(gemini_api_secret, b64, hashlib.sha384).hexdigest()

request_headers = {
    'Content-Type': "text/plain",
    'Content-Length': "0",
    'X-GEMINI-APIKEY': gemini_api_key,
    'X-GEMINI-PAYLOAD': b64,
    'X-GEMINI-SIGNATURE': signature,
    'Cache-Control': "no-cache"
    }

response = requests.post(url, data=None, headers=request_headers, timeout=timeout, verify=False)
assert response.status_code == 200
return response.content

Which produces these HTTP headers

POST /v1/order/status
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

  1. one API key that can trade, and
  2. another API key that can withdraw digital assets to a whitelisted address, or
  3. 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:

See Error Codes for more information about API error responses.

Trader

Assigning the Trader role to an API key allows this API key to:

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:

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:

  1. The number of milliseconds since 1970-01-01 UTC
  2. 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

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, as this table makes explicit:

Symbol Price currency Quantity currency Minimum order size Minimum order increment Minimum price increment
btcusd USD BTC 0.00001 BTC (1e-5) 0.00000001 BTC (1e-8) 0.01 USD
ethusd USD ETH 0.001 ETH (1e-3) 0.000001 ETH (1e-6) 0.01 USD
ethbtc BTC ETH 0.001 ETH (1e-3) 0.000001 ETH (1e-6) 0.00001 BTC (1e-5)
zecusd USD ZEC 0.001 ZEC (1e-3) 0.000001 ZEC (1e-6) 0.01 USD
zecbtc BTC ZEC 0.001 ZEC (1e-3) 0.000001 ZEC (1e-6) 0.00001 BTC (1e-5)
zeceth ETH ZEC 0.001 ZEC (1e-3) 0.000001 ZEC (1e-6) 0.0001 ETH (1e-4)

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.

  1. WebSocket connection is established
  2. Optional: subscription acknowledgement, such as Order Events: Subscription Acknowledgement
  3. Your subscription begins - you receive your first event with socket_sequence set to a value of 0
  4. 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:

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:

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

Workflow

  1. Client submits order to Gemini exchange
  2. Is the order accepted?
    • Yes, order is accepted
      1. Gemini sends an accepted order event
      2. Gemini sends zero or more initial fill events
      3. 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?
                1. Yes, the order is cancelled
                  • Gemini sends a cancelled event followed by a closed event
                  • No further order events about this order
                2. 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
            • 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?
                1. Yes, the order is completely filled
                  • Gemini sends a closed event
                  • No further order events about this order
                2. No, the order has remaining quantity
                  • The order continues to rest on the books
        • No, the order has been completely filled
          • Gemini sends a closed event
          • No further order events about this order
    • No, order is rejected
      • Gemini responds with a rejected order event explaining why the order was rejected
      • No further order events about this order

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.

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:

  1. one or more supported symbols
  2. one or more of your API session keys
    • use UI as the session key for orders placed through the website
  3. 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

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 websocket
import datetime
import json
import time
import base64
import hmac
from hashlib import sha384


def on_message(ws, message):
    print message


def on_error(ws, error):
    print datetime.datetime.now(), "error", error


def on_close(ws):
    print datetime.datetime.now(), "### closed ###"


def on_open(ws):
    print datetime.datetime.now(), "### open ###"


if __name__ == "__main__":
    gemini_api_key = 'your-api-key'
    gemini_api_secret = 'your-secret'

    payload = base64.b64encode(json.dumps({
        'request': '/v1/order/events',
        'nonce': long(time.time()*1000)
    }, separators=(',', ':')))

    signature = hmac.new(gemini_api_secret, payload, sha384).hexdigest()

    ws = websocket.WebSocketApp("wss://api.gemini.com/v1/order/events",
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close,
                                on_open=on_open,
                                header={
                                    'X-GEMINI-PAYLOAD:%s' % payload,
                                    'X-GEMINI-APIKEY:%s' % gemini_api_key,
                                    'X-GEMINI-SIGNATURE:%s' % signature
                                })

    ws.run_forever(ping_interval=5)

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.

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

Response

Once your WebSocket session is established, you will receive:

  1. a subscription acknowledgement
  2. a list of your active orders, if you supply either
    • no eventTypeFilter, or
    • an explicit eventTypeFilter=initial argument as part of one or more eventTypeFilter arguments in your WebSocket request
  3. 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.

Subscription acknowledgement

Subscription acknowledgement showing empty filters

{
  "type" : "subscription_ack",
  "accountId" : 266,
  "subscriptionId" : "ws-order-events-266-as1lhpfh4bmin4limcj0",
  "symbolFilter" : [ ],
  "apiSessionFilter" : [ ],
  "eventTypeFilter" : [ ]
}

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. 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

{
  "type" : "heartbeat",
  "timestampms" : 1478729302396,
  "sequence" : 64, 
  "trace_id" : "ao9r76tkr9iq5qoo4qf0"
}

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 Y heartbeat
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" : "652150",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "buy",
  "order_type" : "exchange limit",
  "timestamp" : "1478789840",
  "timestampms" : 1478789840842,
  "is_live" : true,
  "is_cancelled" : false,
  "is_hidden" : false,
  "avg_execution_price" : "713.95",
  "executed_amount" : "2",
  "remaining_amount" : "3",
  "original_amount" : "5",
  "price" : "713.95", 
  "socket_sequence" : 0
}, {
  "type" : "initial",
  "order_id" : "652153",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "buy",
  "order_type" : "exchange limit",
  "timestamp" : "1478789859",
  "timestampms" : 1478789859263,
  "is_live" : true,
  "is_cancelled" : false,
  "is_hidden" : false,
  "avg_execution_price" : "0.00",
  "executed_amount" : "0",
  "remaining_amount" : "5",
  "original_amount" : "5",
  "price" : "713.95", 
  "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

Limit Order accepted

[ {
  "type" : "accepted",
  "order_id" : "6310",
  "event_id" : "6311",
  "api_session" : "UI",
  "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" : "1",
  "price" : "721.24", 
  "socket_sequence" : 302
} ]

Market Buy accepted

[ {
  "type" : "accepted",
  "order_id" : "652415",
  "event_id" : "652416",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "buy",
  "order_type" : "market buy",
  "timestamp" : "1478792456",
  "timestampms" : 1478792456175,
  "is_live" : false,
  "is_cancelled" : false,
  "is_hidden" : false,
  "total_spend" : "1500.00", 
  "socket_sequence" : 4015
} ]

Market Sell accepted

[ {
  "type" : "accepted",
  "order_id" : "6320",
  "event_id" : "6321",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "sell",
  "order_type" : "market sell",
  "timestamp" : "1478204198",
  "timestampms" : 1478204198989,
  "is_live" : true,
  "is_cancelled" : false,
  "is_hidden" : false, 
  "avg_execution_price" : "0", 
  "original_amount" : "500", 
  "socket_sequence" : 32307
} ]

Indication of Interest (i.e. block trading taker) limit order accepted json [ { "type" : "accepted", "order_id" : "6320", "event_id" : "6321", "api_session" : "UI", "symbol" : "btcusd", "side" : "sell", "order_type" : "", "timestamp" : "1478204198", "timestampms" : 1478204198989, "is_live" : true, "is_cancelled" : false, "is_hidden" : true, "avg_execution_price" : "0", "original_amount" : "500", "socket_sequence" : 32307 } ]

Block trading maker limit order accepted

[ {
  "type" : "accepted",
  "order_id" : "6321",
  "event_id" : "6322",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "sell",
  "order_type" : "block_trade",
  "timestamp" : "1478204198",
  "timestampms" : 1478204198989,
  "is_live" : true,
  "is_cancelled" : false,
  "is_hidden" : true,
  "avg_execution_price" : "0",
  "original_amount" : "500",
  "socket_sequence" : 32307
} ]

Auction-only limit order accepted

[ {
  "type" : "accepted",
  "order_id" : "6382",
  "event_id" : "6383",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "buy",
  "order_type" : "auction-only limit",
  "timestamp" : "1478204672",
  "timestampms" : 1478204672093,
  "is_live" : true,
  "is_cancelled" : false,
  "is_hidden" : true, 
  "avg_execution_price" : "0", 
  "original_amount" : "5",
  "price" : "700.00", 
  "socket_sequence" : 41313
} ]

Maker-or-cancel limit order accepted

[ {
  "type" : "accepted",
  "order_id" : "556817",
  "event_id" : "556818",
  "api_session" : "UI",
  "symbol" : "ethusd",
  "side" : "buy",
  "order_type" : "exchange limit",
  "timestamp" : "1478729041",
  "timestampMs" : 1478729041738,
  "is_live" : true,
  "is_cancelled" : false,
  "is_hidden" : false,
  "original_amount" : "636.645777",
  "price" : "10.73",
  "behavior" : "maker-or-cancel", 
  "socket_sequence" : 1881
} ]

Immediate-or-cancel limit order accepted, then immediately filled and closed

[ {
  "type" : "accepted",
  "order_id" : "652164",
  "event_id" : "652165",
  "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",
  "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",
  "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
} ]

Indication-of-Interest limit order accepted

[ {
  "type" : "accepted",
  "order_id" : "556817",
  "event_id" : "556818",
  "api_session" : "UI",
  "symbol" : "ethusd",
  "side" : "buy",
  "order_type" : "indication-of-interest limit",
  "timestamp" : "1478729041",
  "timestampMs" : 1478729041738,
  "is_live" : true,
  "is_cancelled" : false,
  "is_hidden" : false,
  "original_amount" : "636.645777",
  "price" : "10.73",
  "behavior" : "maker-or-cancel",
  "socket_sequence" : 1881
} ]

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:

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",
  "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 institutional@gemini.com for clarification.

Booked

Order booked

[ {
  "type" : "booked",
  "order_id" : "6366",
  "event_id" : "6368",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "buy",
  "order_type" : "exchange limit",
  "timestamp" : "1478204571",
  "timestampms" : 1478204571601,
  "is_live" : true,
  "is_cancelled" : false,
  "is_hidden" : false,
  "avg_execution_price" : "0.00",
  "executed_amount" : "0",
  "remaining_amount" : "10",
  "original_amount" : "10",
  "price" : "701.24", 
  "socket_sequence" : 388483
} ]

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.

Auction orders are accepted but not booked because the quantity is not visible on the exchange.

Field Type Required? Description
type order event type Y booked

Filled

Complete fill on a limit order

[ {
  "type" : "fill",
  "order_id" : "6310",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "buy",
  "order_type" : "exchange limit",
  "timestamp" : "1478203017",
  "timestampms" : 1478203017455,
  "is_live" : false,
  "is_cancelled" : false,
  "is_hidden" : false,
  "avg_execution_price" : "721.24",
  "executed_amount" : "1",
  "remaining_amount" : "0",
  "original_amount" : "1",
  "price" : "721.24",
  "fill" : {
    "trade_id" : "6312",
    "liquidity" : "Taker",
    "price" : "721.24",
    "amount" : "1", 
    "fee" : "1.8031", 
    "fee_currency" : "USD"
  }, 
  "socket_sequence" : 201961
} ]

Partial fill on a limit order

[ {
  "type" : "fill",
  "order_id" : 556309,
  "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, Taker, Auction, Block, or IndicatorOfInterest 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" : "6361",
  "event_id" : "6363",
  "reason" : "Requested",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "buy",
  "order_type" : "exchange limit",
  "timestamp" : "1478204410",
  "timestampms" : 1478204410558,
  "is_live" : false,
  "is_cancelled" : true,
  "is_hidden" : false,
  "avg_execution_price" : "0.00",
  "executed_amount" : "0",
  "remaining_amount" : "10",
  "original_amount" : "10",
  "price" : "800.00", 
  "socket_sequence" : 47473
} ]

Auction-only order cancelled

[ {
  "type" : "cancelled",
  "order_id" : "6425",
  "event_id" : "6534",
  "reason" : "AuctionClosedOrderNotFilled",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "buy",
  "order_type" : "auction-only limit",
  "timestamp" : "1478204829",
  "timestampms" : 1478204829753,
  "is_live" : false,
  "is_cancelled" : true,
  "is_hidden" : true,
  "avg_execution_price" : "0.00",
  "executed_amount" : "0",
  "remaining_amount" : "5",
  "original_amount" : "5",
  "price" : "721.24", 
  "socket_sequence" : 439870
} ]

When one of your active orders is cancelled, you will receive a notification via a cancelled event.

Your order could be cancelled because:

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" : "IneligibleTiming",
  "api_session" : "UI",
  "symbol" : "btcusd",
  "side" : "buy",
  "order_type" : "auction-only 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:

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

Order closed

[ {
  "type" : "closed",
  "order_id" : 556309,
  "event_id" : "557345",
  "api_session" : "UI",
  "symbol" : "ethbtc",
  "side" : "sell",
  "order_type" : "exchange limit",
  "timestamp" : "1478729298",
  "timestampMs" : 1478729298197,
  "is_live" : false,
  "is_cancelled" : false,
  "is_hidden" : false,
  "avg_execution_price" : "0.01514",
  "executed_amount" : "785.020886",
  "remaining_amount" : "0",
  "original_amount" : "785.020886",
  "price" : "0.01514", 
  "socket_sequence" : 126296
} ]

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.

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(ping_interval=5)
npm install -g wscat
wscat --connect wss://api.gemini.com/v1/marketdata/btcusd

Example: Offers only, top of book

import websocket

def on_message(ws, message):
    print message

ws = websocket.WebSocketApp("wss://api.gemini.com/v1/marketdata/btcusd?top_of_book=true&offers=true",
                                on_message=on_message)

ws.run_forever(ping_interval=5)
$ wscat --connect=ws://api.gemini.com/v1/marketdata/btcusd?top_of_book=true\&offers=true
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 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(ping_interval=5)
$ wscat --connect=ws://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 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(ping_interval=5)
$ wscat --connect=ws://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. 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
auctions No true Include auction events

The semantics of entry type filtering is:

NOTE: top_of_book has no meaning and initial book events are empty when only trades is specified

Response

Initial JSON response message:

{
   "type":"update",
   "eventId":1111596393,
   "socket_sequence":0, 
   "events":[
      // ...
      {
         "type":"change",
         "reason":"initial",
         "price":"2559.00",
         "delta":"0.31184056",
         "remaining":"0.31184056",
         "side":"bid"
      },
      {
         "type":"change",
         "reason":"initial",
         "price":"2559.97",
         "delta":"11.43872435",
         "remaining":"11.43872435",
         "side":"bid"
      },
      {
         "type":"change",
         "reason":"initial",
         "price":"2559.98",
         "delta":"21.0601725",
         "remaining":"21.0601725",
         "side":"ask"
      },
      {
         "type":"change",
         "reason":"initial",
         "price":"2560.01",
         "delta":"0.2",
         "remaining":"0.2",
         "side":"ask"
      },
      // ...
   ]
}     

If heartbeat is enabled:

{
   "type":"heartbeat",
   "socket_sequence":30
}

A WebSocket stream which 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, change, auction, or block_trade.

Change event

When an order is placed:

{
   "type":"update",
   "eventId":1111596536,
   "socket_sequence":1, 
   "timestamp":1501175022,
   "timestampms":1501175022456,
   "events":[
      {
         "type":"change",
         "side":"bid",
         "price":"2559.97",
         "remaining":"11.54303435",
         "delta":"1.10431",
         "reason":"place"
      }
   ]
}

When an order is canceled:

{
   "type":"update",
   "eventId":1111596579,
   "socket_sequence":2, 
   "timestamp":1501175022,
   "timestampms":1501175022983,
   "events":[
      {
         "type":"change",
         "side":"bid",
         "price":"2532.74",
         "remaining":"0",
         "delta":"-10",
         "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":1111597035,
   "socket_sequence":8, 
   "timestamp":1501175027,
   "timestampms":1501175027304,
   "events":[
   // The first event is the trade event representing 
   // the volume executed in this trade
      {
         "type":"trade",
         "tid":1111597035,
         "price":"2559.98",
         "amount":"0.07365713",
         "makerSide":"ask"
      },
      // the second event is a change event which decrements the 
      // quantity of an order that remains on the ask side of the book
      {
         "type":"change",
         "side":"ask",
         "price":"2559.98",
         "remaining":"20.98651537",
         "delta":"-0.07365713",
         "reason":"trade"
      }
   ]
}

When an auction runs:

{
    "eventId": 371469414,
    "socket_sequence":4009, 
    "timestamp":1486501200,
    "timestampms":1486501200000,
    "events": [
    // The first event is the trade event representing 
    // the total volume traded in the auction, 
        {
            "amount": "1406",
            "makerSide": "auction",
            "price": "1048.75",
            "tid": 371469414,
            "type": "trade"
        },
    // the second event is the auction outcome.
        {
            "auction_price": "1048.75",
            "auction_quantity": "1406",
            "eid": 371469414,
            "highest_bid_price": "1050.98",
            "lowest_ask_price": "1050.99",
            "result": "success",
            "time_ms": 1486501200000,
            "type": "auction_result"
        }
    ],
    "type": "update"
}

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, of if the trade occurred in an auction. Either bid, ask, or auction.

Block trade event

When a block trade occurs:

{
   "type":"update",
   "eventId":1111597035,
   "socket_sequence":8,
   "timestamp":1501175027,
   "timestampms":1501175027304,
   "events":[
      {
         "type":"block_trade",
         "tid":1111597035,
         "price":"10100.00",
         "amount":"1000"
      }
   ]
}

Elements of type block_trade advertise the following:

Field Type Description
price decimal The price this trade executed at.
amount decimal The amount traded.

Auction event

Elements of type auction advertise the following:

Auction open

When an auction opens:

{
    "eventId": 372481811,
    "socket_sequence":23,
    "timestamp": 1486591200,
    "timestampms": 1486591200000,  
    "events": [
        {
            "auction_open_ms": 1486591200000,
            "auction_time_ms": 1486674000000,
            "first_indicative_ms": 1486673400000,
            "last_cancel_time_ms": 1486673985000,
            "type": "auction_open"
        }
    ],
    "type": "update"
}

An auction open event advertises that an auction for this symbol is now open and accepting orders.

Field Type Description
type string auction_open
auction_open_ms timestampms Time this auction opened
auction_time_ms timestampms Time this auction will run
first_indicative_ms timestampms Time when the first indicative price will be published.
last_cancel_time_ms timestampms Time when it will no longer be possible to cancel auction orders for this auction.

Auction indicative price

When an indicative price is published

{
    "type": "update",
    "eventId": 2248762586,
    "timestamp": 1510865640,
    "timestampms": 1510865640122,
    "socket_sequence": 177,
    "events": [
        {
            "type": "auction_indicative",
            "eid": 2248762586,
            "result": "success",
            "time_ms": 1510865640000,
            "highest_bid_price": "7730.69",
            "lowest_ask_price": "7730.7",
            "collar_price": "7730.695",
            "indicative_price": "7750",
            "indicative_quantity": "45.43325086"
        }
    ]
}

When an auction indicative price is published, you will receive an event with an type of auction_indicative with the following fields:

Field Type Description
type string auction_indicative
eid integer Unique event ID for this specific auction event.
result string success or failure, indicating whether the auction would succeed if it were run at this point
time_ms timestampms Time that this event occurred
highest_bid_price decimal Highest bid price from the continuous order book at the time of the auction event, if available.
lowest_ask_price decimal Lowest ask price from the continuous order book at the time of the auction event, if available.
collar_price decimal The indicative_price must be within plus or minus five percent of the collar price for result to be success.
indicative_price decimal The price that this auction would take place at if it were run now. Zero if result is failure.
indicative_quantity decimal The quantity that would execute if the auction were run now.

Auction outcome

When an auction runs:

{
    "type": "update",
    "eventId": 2248795680,
    "timestamp": 1510866000,
    "timestampms": 1510866000095,
    "socket_sequence": 2920,
    "events": [
        {
            "type": "trade",
            "tid": 2248795680,
            "price": "7763.23",
            "amount": "55.95",
            "makerSide": "auction"
        },
        {
            "type": "auction_result",
            "eid": 2248795680,
            "result": "success",
            "time_ms": 1510866000000,
            "highest_bid_price": "7769",
            "lowest_ask_price": "7769.01",
            "collar_price": "7769.005",
            "auction_price": "7763.23",
            "auction_quantity": "55.95"
        }
    ]
}

When an auction runs, you will receive up to two events:

Field Type Description
type string auction_result
eid integer Unique event ID for this specific auction event.
result string success or failure, indicating whether a price has been found for the auction
time_ms timestampms Time that this event occurred
highest_bid_price decimal Highest bid price from the continuous order book at the time of the auction event, if available.
lowest_ask_price decimal Lowest ask price from the continuous order book at the time of the auction event, if available.
collar_price decimal The auction_price must be within plus or minus five percent of the collar price for result to be success.
auction_price decimal If result is success, the price at which orders were filled. Zero if result is failure.
auction_quantity decimal If result is success, the quantity that was filled. Zero if result is failure.

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 Auction not open or paused, ineligible timing, 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:

  1. result, which will always be "error"
  2. reason, which will be one of the strings listed in the table below
  3. message, a human-readable English string indicating additional error information.
Reason Meaning
AuctionNotOpen Failed to place an auction-only order because there is no current auction open for this symbol
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
EndpointMismatch The request was submitted to an endpoint different than the one in the payload
EndpointNotFound No endpoint was specified
IneligibleTiming Failed to place an auction order for the current auction on this symbol because the timing is not eligible: new orders may only be placed before the auction begins.
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 present
InvalidOrderType An unknown order type was provided
InvalidPrice For new orders, the price was invalid
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.
Maintenance The system is down for maintenance
MarketNotOpen The order was rejected because the market is not accepting new orders
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
MissingSignatureHeader The X-GEMINI-SIGNATURE header was missing
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.

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

© Copyright 2018 Gemini Trust Company, LLC.