Private API Invocation
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.
Authenticated APIs do not submit their payload as POSTed data, but instead put it in the X-GEMINI-PAYLOAD header
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.
Headers
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 |
Examples
To use the private WebSocket APIs, you need to properly authenticate your requests. This page explains how to generate the necessary authentication headers.
Request JSON
To walk through the process of generating a private API invocation, we start with the request JSON itself:
{ "request": "/v1/order/events", "nonce": <nonce> }json
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 Encoding
The JSON payload needs to be base64 encoded:
~$ base64 << EOF > { > "request": "/v1/order/events", > "nonce": <nonce> > } > EOF ewogICAgInJlcXVlc3QiOiAiL3YxL29yZGVyL3N0YXR1cyIsCiAgICAibm9uY2UiOiAxMjM0NTYs CgogICAgIm9yZGVyX2lkIjogMTg4MzQKfQo=bash
Generating the Signature
In this example, the api_secret
is 1234abcd
:
echo -n 'ewogICAgInJlcXVlc3QiOiAiL3YxL29yZGVyL3N0YXR1cyIsCiAgICAibm9uY2UiOiAxMjM0NTYsCgogICAgIm9yZGVyX2lkIjogMTg4MzQKfQo=' | openssl sha384 -hmac "1234abcd" (stdin)= 337cc8b4ea692cfe65b4a85fcc9f042b2e3f702ac956fd098d600ab15705775017beae402be773ceee10719ff70d710fbash
hmac.new("1234abcd", b64, hashlib.sha384).hexdigest() '337cc8b4ea692cfe65b4a85fcc9f042b2e3f702ac956fd098d600ab15705775017beae402be773ceee10719ff70d710f'python
Complete Python Example
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})python
Final Request Format
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 f70d710ftext